// bslx_testinstream.h                                                -*-C++-*-
#ifndef INCLUDED_BSLX_TESTINSTREAM
#define INCLUDED_BSLX_TESTINSTREAM

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Enable unexternalization of fundamental types with identification.
//
//@CLASSES:
//  bslx::TestInStream: byte-array-based input stream class
//
//@MACROS:
//  BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN: macro to begin testing exceptions
//  BSLX_TESTINSTREAM_EXCEPTION_TEST_END: macro to end testing exceptions
//
//@SEE_ALSO: bslx_testoutstream, bslx_byteinstream
//
//@DESCRIPTION: This component implements a byte-array-based input stream
// class, `bslx::TestInStream`, that provides platform-independent input
// methods ("unexternalization") on values, and arrays of values, of
// fundamental types, and on `bsl::string`.  `bslx::TestInStream` also
// verifies, for these types, that the type of data requested from the stream
// matches what was written to the stream.  `bslx::TestInStream` is meant for
// testing only.
//
// The `bslx::TestInStream` type reads from a user-supplied buffer directly,
// with no data copying or assumption of ownership.  The user must therefore
// make sure that the lifetime and visibility of the buffer is sufficient to
// satisfy the needs of the input stream.
//
// This component is intended to be used in conjunction with the
// `bslx_testoutstream` externalization component.  Each input method of
// `bslx::TestInStream` reads either a value or a homogeneous array of values
// of a fundamental type, in a format that was written by the corresponding
// `bslx::TestOutStream` method.  In general, the user of this component cannot
// rely on being able to read data that was written by any mechanism other than
// `bslx::TestOutStream`.
//
// The supported types and required content are listed in the `bslx`
// package-level documentation under "Supported Types".
//
// Note that input streams can be *invalidated* explicitly and queried for
// *validity* and *emptiness*.  Reading from an initially invalid stream has no
// effect.  Attempting to read beyond the end of a stream will automatically
// invalidate the stream.  Whenever an inconsistent value is detected, the
// stream should be invalidated explicitly.
//
///Input Limit
///-----------
// If exceptions are enabled at compile time, the test input stream can be
// configured to throw an exception after a specified number of input requests
// is exceeded.  If the input limit is less than zero (default), then the
// stream never throws an exception.  Note that a non-negative input limit is
// decremented after each input attempt, and throws only when the current input
// limit transitions from 0 to -1; no additional exceptions will be thrown
// until the input limit is again reset to a non-negative value.
//
// The input limit is set using the `setInputLimit` manipulator.
//
///Exception Test Macros
///---------------------
// This component also provides a pair of macros:
//
// * `BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN(testInStream)`
// * `BSLX_TESTINSTREAM_EXCEPTION_TEST_END`
//
// These macros can be used for testing exception-safety of classes and their
// methods when BDEX streaming is involved.  A reference to an object of type
// `bslx::TestInStream` must be supplied as an argument to the `*_BEGIN` macro.
// Note that if exception-handling is disabled (i.e., if
// `-DBDE_BUILD_TARGET_EXC` was *not* supplied at compile time), then the
// macros simply print the following:
// ```
// BSLX EXCEPTION TEST -- (NOT ENABLED) --
// ```
// When exception-handling is enabled (i.e., if `-DBDE_BUILD_TARGET_EXC` was
// supplied at compile time), the `*_BEGIN` macro will set the input limit of
// the supplied instream to 0, `try` the code being tested, `catch` any
// `TestInstreamException`s that are thrown, and keep increasing the input
// limit until the code being tested completes successfully.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Unexternalization Test
///- - - - - - - - - - - - - - - - - - - -
// Suppose we wish to implement a (deliberately simple) `MyPerson` class as a
// value-semantic object that supports BDEX externalization and
// unexternalization.  In addition to whatever data and methods that we choose
// to put into our design, we must supply three methods having specific names
// and signatures in order to comply with the BDEX protocol: a class method
// `maxSupportedBdexVersion`, an accessor (i.e., a `const` method)
// `bdexStreamOut`, and a manipulator (i.e., a non-`const` method)
// `bdexStreamIn`.  This example shows how to implement those three methods.
//
// In this example we will not worry overly about "good design" of the
// `MyPerson` component, and we will declare but not implement illustrative
// methods and free operators, except for the three required BDEX methods,
// which are implemented in full.  In particular, we will not make explicit use
// of `bslma` allocators; a more complete design would do so:
//
// First, we implement `MyPerson`:
// ```
// class MyPerson {
//     bsl::string d_firstName;
//     bsl::string d_lastName;
//     int         d_age;
//
//     friend bool operator==(const MyPerson&, const MyPerson&);
//
//   public:
//     // CLASS METHODS
//
//     /// Return the maximum valid BDEX format version, as indicated by
//     /// the specified `versionSelector`, to be passed to the
//     /// `bdexStreamOut` method.  Note that it is highly recommended that
//     /// `versionSelector` be formatted as "YYYYMMDD", a date
//     /// representation.  Also note that `versionSelector` should be a
//     /// *compile*-time-chosen value that selects a format version
//     /// supported by both externalizer and unexternalizer.  See the
//     /// `bslx` package-level documentation for more information on BDEX
//     /// streaming of value-semantic types and containers.
//     static int maxSupportedBdexVersion(int versionSelector);
//
//     // CREATORS
//
//     /// Create a default person.
//     MyPerson();
//
//     /// Create a person having the specified `firstName`, `lastName`,
//     /// and `age`.
//     MyPerson(const char *firstName, const char *lastName, int age);
//
//     /// Create a person having the value of the specified `original` person.
//     MyPerson(const MyPerson& original);
//
//     /// Destroy this object.
//     ~MyPerson();
//
//     // MANIPULATORS
//
//     /// Assign to this person the value of the specified `rhs` person, and
//     /// return a reference to this person.
//     MyPerson& operator=(const MyPerson& rhs);
//
//     /// Assign to this object the value read from the specified input
//     /// `stream` using the specified `version` format, and return a
//     /// reference to `stream`.  If `stream` is initially invalid, this
//     /// operation has no effect.  If `version` is not supported, this
//     /// object is unaltered and `stream` is invalidated, but otherwise
//     /// unmodified.  If `version` is supported but `stream` becomes
//     /// invalid during this operation, this object has an undefined, but
//     /// valid, state.  Note that no version is read from `stream`.  See
//     /// the `bslx` package-level documentation for more information on
//     /// BDEX streaming of value-semantic types and containers.
//     template <class STREAM>
//     STREAM& bdexStreamIn(STREAM& stream, int version);
//
//     //...
//
//     // ACCESSORS
//
//     /// Return the first name of this person.
//     const bsl::string& firstName() const;
//
//     /// Return the last name of this person.
//     const bsl::string& lastName() const;
//
//     /// Return the age of this person.
//     int age() const;
//
//     /// Write the value of this object, using the specified `version`
//     /// format, to the specified output `stream`, and return a reference
//     /// to `stream`.  If `stream` is initially invalid, this operation
//     /// has no effect.  If `version` is not supported, `stream` is
//     /// invalidated, but otherwise unmodified.  Note that `version` is
//     /// not written to `stream`.  See the `bslx` package-level
//     /// documentation for more information on BDEX streaming of
//     /// value-semantic types and containers.
//     template <class STREAM>
//     STREAM& bdexStreamOut(STREAM& stream, int version) const;
//
//     //...
//
// };
//
// // FREE OPERATORS
//
// /// Return `true` if the specified `lhs` and `rhs` person objects have
// /// the same value, and `false` otherwise.  Two person objects have the
// /// same value if they have the same first name, last name, and age.
// bool operator==(const MyPerson& lhs, const MyPerson& rhs);
//
// /// Return `true` if the specified `lhs` and `rhs` person objects do not
// /// have the same value, and `false` otherwise.  Two person objects
// /// differ in value if they differ in first name, last name, or age.
// bool operator!=(const MyPerson& lhs, const MyPerson& rhs);
//
// /// Write the specified `person` value to the specified output `stream`
// /// in some reasonable format, and return a reference to `stream`.
// bsl::ostream& operator<<(bsl::ostream& stream, const MyPerson& person);
//
// // ========================================================================
// //                  INLINE FUNCTION DEFINITIONS
// // ========================================================================
//
// // CLASS METHODS
// inline
// int MyPerson::maxSupportedBdexVersion(int /* versionSelector */) {
//     return 1;
// }
//
// // CREATORS
// inline
// MyPerson::MyPerson()
// : d_firstName("")
// , d_lastName("")
// , d_age(0)
// {
// }
//
// inline
// MyPerson::MyPerson(const char *firstName, const char *lastName, int age)
// : d_firstName(firstName)
// , d_lastName(lastName)
// , d_age(age)
// {
// }
//
// inline
// MyPerson::~MyPerson()
// {
// }
//
// template <class STREAM>
// STREAM& MyPerson::bdexStreamIn(STREAM& stream, int version)
// {
//     if (stream) {
//         switch (version) {  // switch on the 'bslx' version
//           case 1: {
//             stream.getString(d_firstName);
//             if (!stream) {
//                 d_firstName = "stream error";  // *might* be corrupted;
//                                                //  value for testing
//                 return stream;
//             }
//             stream.getString(d_lastName);
//             if (!stream) {
//                 d_lastName = "stream error";  // *might* be corrupted;
//                                               //  value for testing
//                 return stream;
//             }
//             stream.getInt32(d_age);
//             if (!stream) {
//                 d_age = 999;     // *might* be corrupted; value for testing
//                 return stream;
//             }
//           } break;
//           default: {
//             stream.invalidate();
//           }
//         }
//     }
//     return stream;
// }
//
// // ACCESSORS
// template <class STREAM>
// STREAM& MyPerson::bdexStreamOut(STREAM& stream, int version) const
// {
//     switch (version) {
//       case 1: {
//         stream.putString(d_firstName);
//         stream.putString(d_lastName);
//         stream.putInt32(d_age);
//       } break;
//       default: {
//         stream.invalidate();
//       } break;
//     }
//     return stream;
// }
// ```
// Then, we can exercise the new `MyPerson` value-semantic class by
// externalizing and reconstituting an object.  First, create a `MyPerson`
// `janeSmith` and a `bslx::TestOutStream` `outStream`:
// ```
// MyPerson janeSmith("Jane", "Smith", 42);
// bslx::TestOutStream outStream(20131127);
// const int VERSION = 1;
// outStream.putVersion(VERSION);
// janeSmith.bdexStreamOut(outStream, VERSION);
// assert(outStream.isValid());
// ```
// Next, create a `MyPerson` `janeCopy` initialized to the default value, and
// assert that `janeCopy` is different from `janeSmith`:
// ```
// MyPerson janeCopy;
// assert(janeCopy != janeSmith);
// ```
// Then, create a `bslx::TestInStream` `inStream` initialized with the buffer
// from the `bslx::TestOutStream` object `outStream` and unexternalize this
// data into `janeCopy`:
// ```
// bslx::TestInStream inStream(outStream.data(), outStream.length());
// int version;
// inStream.getVersion(version);
// janeCopy.bdexStreamIn(inStream, version);
// assert(inStream.isValid());
// ```
// Finally, `assert` the obtained values are as expected and display the
// results to `bsl::stdout`:
// ```
// assert(version  == VERSION);
// assert(janeCopy == janeSmith);
//
// if (janeCopy == janeSmith) {
//     bsl::cout << "Successfully serialized and de-serialized Jane Smith:"
//               << "\n\tFirstName: " << janeCopy.firstName()
//               << "\n\tLastName : " << janeCopy.lastName()
//               << "\n\tAge      : " << janeCopy.age() << bsl::endl;
// }
// else {
//     bsl::cout << "Serialization unsuccessful.  'janeCopy' holds:"
//               << "\n\tFirstName: " << janeCopy.firstName()
//               << "\n\tLastName : " << janeCopy.lastName()
//               << "\n\tAge      : " << janeCopy.age() << bsl::endl;
// }
// ```

#include <bslscm_version.h>

#include <bslx_instreamfunctions.h>
#include <bslx_testinstreamexception.h>
#include <bslx_typecode.h>

#include <bsls_assert.h>
#include <bsls_buildtarget.h>
#include <bsls_keyword.h>
#include <bsls_types.h>

#include <bsl_cstddef.h>
#include <bsl_iosfwd.h>
#include <bsl_string.h>
#include <bsl_vector.h>

namespace BloombergLP {
namespace bslx {

                         // ==================
                         // class TestInStream
                         // ==================

/// This class provides input methods to unexternalize values, and C-style
/// arrays of values, of fundamental types from their byte representations.
/// Each input method also verifies the input value type.  By default, if
/// invalid data is detected, error messages are displayed on `stdout`; this
/// error reporting may be disabled via the `setQuiet` method.  Note that
/// attempting to read beyond the end of a stream will automatically
/// invalidate the stream.  See the `bslx` package-level documentation for
/// the definition of the BDEX `InStream` protocol.
class TestInStream {

    // DATA
    const char  *d_buffer;      // bytes to be unexternalized

    bsl::size_t  d_numBytes;    // number of bytes in 'd_buffer'

    bool         d_validFlag;   // stream validity flag; 'true' if stream is in
                                // valid state, 'false' otherwise

    int          d_quietFlag;   // flag for "quiet" mode

    int          d_inputLimit;  // number of input op's before exception

    bsl::size_t  d_cursor;      // index of the next byte to be extracted from
                                // this stream

    // FRIENDS
    friend bsl::ostream& operator<<(bsl::ostream&, const TestInStream&);

    // NOT IMPLEMENTED
    TestInStream(const TestInStream&);
    TestInStream& operator=(const TestInStream&);

  private:
    // PRIVATE MANIPULATORS

    /// Verify the validity of the type code and array length, and the
    /// sufficiency of data at the current cursor position in the external
    /// memory buffer.  Extract the type code at the cursor position from the
    /// buffer.  If the type code does not correspond to the specified `code`,
    /// then mark this stream as invalid, and if the quiet flag is zero print
    /// an error message.  Otherwise, advance the cursor by the size of the
    /// type code and extract the array length.  If the length does not
    /// correspond to the specified `numElements`, then mark this stream as
    /// invalid, and if the quiet flag is zero print an error message.
    /// Otherwise, advance the cursor by the size of the array length, and
    /// verify that the buffer contains sufficient bytes for `numElements` of
    /// the specified `elementSize`.  If there are too few bytes in the buffer,
    /// then mark this stream as invalid.  If this stream is invalid on entry,
    /// this function has no effect.  The behavior is undefined unless
    /// `0 < elementSize` and `0 <= numElements`.  Note that error messages are
    /// not printed for insufficient data in the buffer.
    void checkArray(TypeCode::Enum code,
                    int            elementSize,
                    int            numElements);

    /// Verify the validity of the type code and the sufficiency of data at the
    /// current cursor position in the external memory buffer.  Extract the
    /// type code at the cursor position from the buffer.  If the type code
    /// does not correspond to the specified `code`, then mark this stream as
    /// invalid and, if the quiet flag is zero, print an error message.
    /// Otherwise, advance the cursor position by the size of the type code,
    /// and verify that the buffer contains sufficient bytes for the specified
    /// `numExpectedBytes`.  If there are too few bytes, then this stream is
    /// marked as invalid.  If this stream is invalid on entry, this function
    /// has no effect.  The behavior is undefined unless
    /// `0 < numExpectedBytes`.  Also note that error messages are not printed
    /// for insufficient data in the buffer.
    void checkTypeCodeAndAvailableLength(TypeCode::Enum code,
                                         bsl::size_t    numExpectedBytes);

    /// Decrement the internal input limit of this test stream.  If the input
    /// limit becomes negative and exception-handling is enabled (enabled by
    /// default), then throw a `TestInStreamException` object initialized with
    /// the specified type `code`.  If exception-handling is not enabled, this
    /// method has no effect.
    void throwExceptionIfInputLimitExhausted(const TypeCode::Enum& code);

  public:
    // CREATORS

    /// Create an empty test input stream.  Note that the constructed object is
    /// useless until a buffer is set with the `reset` method.
    explicit TestInStream();

    /// Create a test input stream containing the specified initial `numBytes`
    /// from the specified `buffer`.  The behavior is undefined unless
    /// `0 == numBytes` if `0 == buffer`.
    TestInStream(const char *buffer, bsl::size_t numBytes);

    /// Create a test input stream containing the specified `srcData`.
    explicit TestInStream(const bslstl::StringRef& srcData);

    /// Destroy this test input stream.
    ~TestInStream();

    // MANIPULATORS

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 8-bit unsigned integer or 32-bit signed integer
    /// value representing a length (see the `bslx` package-level
    /// documentation) into the specified `variable` if its type is
    /// appropriate, update the cursor location, and return a reference to this
    /// stream.  Consume an 8-bit unsigned integer if the most significant bit
    /// of this byte is 0, otherwise consume a 32-bit signed integer and set
    /// the most significant bit to zero in the resultant `variable`.  If the
    /// type is incorrect, then this stream is marked invalid and the value of
    /// `variable` is unchanged.  If this stream is initially invalid, this
    /// operation has no effect.  If this function otherwise fails to extract a
    /// valid value, this stream is marked invalid and the value of `variable`
    /// is undefined.
    TestInStream& getLength(int& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 8-bit unsigned integer value representing a
    /// version (see the `bslx` package-level documentation) into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getVersion(int& variable);

    /// Put this input stream in an invalid state.  This function has no effect
    /// if this stream is already invalid.  Note that this function should be
    /// called whenever a value extracted from this stream is determined to be
    /// invalid, inconsistent, or otherwise incorrect.
    void invalidate();

    /// Set the index of the next byte to be extracted from this stream to 0
    /// (i.e., the beginning of the stream) and validate this stream if it is
    /// currently invalid.
    void reset();

    /// Reset this stream to extract from the specified `buffer` containing the
    /// specified `numBytes`, set the index of the next byte to be extracted to
    /// 0 (i.e., the beginning of the stream), and validate this stream if it
    /// is currently invalid.  The behavior is undefined unless `0 == numBytes`
    /// if `0 == buffer`.
    void reset(const char *buffer, bsl::size_t numBytes);

    /// Reset this stream to extract from the specified `srcData`, set the
    /// index of the next byte to be extracted to 0 (i.e., the beginning of the
    /// stream), and validate this stream if it is currently invalid.
    void reset(const bslstl::StringRef& srcData);

    /// Set the index of the next byte to be extracted from this stream to the
    /// specified `offset` from the beginning of the stream, and validate this
    /// stream if it is currently invalid.  The behavior is undefined unless
    /// `offset <= length()`.
    void seek(bsl::size_t offset);

    /// Set the number of input operations allowed on this stream to the
    /// specified `limit` before an exception is thrown.  If `limit` is less
    /// than 0, no exception is to be thrown.  By default, no exception is
    /// scheduled.
    void setInputLimit(int limit);

    /// Set the quiet mode for this test stream to the specified (boolean)
    /// `flagValue`.  If `flagValue` is `true`, then quiet mode is turned ON
    /// and no error messages will be written to standard output.  If
    /// `flagValue` is `false`, then quiet mode is turned OFF.  Note that quiet
    /// mode is turned OFF by default.
    void setQuiet(bool flagValue);

                      // *** scalar integer values ***

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 64-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt64(bsls::Types::Int64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 64-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint64(bsls::Types::Uint64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 56-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt56(bsls::Types::Int64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 56-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint56(bsls::Types::Uint64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 48-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt48(bsls::Types::Int64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 48-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint48(bsls::Types::Uint64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 40-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt40(bsls::Types::Int64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 40-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint40(bsls::Types::Uint64& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 32-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt32(int& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 32-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint32(unsigned int& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 24-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt24(int& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 24-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint24(unsigned int& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 16-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt16(short& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 16-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint16(unsigned short& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 8-bit signed integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getInt8(char&        variable);
    TestInStream& getInt8(signed char& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in this
    /// stream, consume that 8-bit unsigned integer value into the specified
    /// `variable` if its type is appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variable` is unchanged.  If
    /// this stream is initially invalid, this operation has no effect.  If
    /// this function otherwise fails to extract a valid value, this stream is
    /// marked invalid and the value of `variable` is undefined.
    TestInStream& getUint8(char&          variable);
    TestInStream& getUint8(unsigned char& variable);

                      // *** scalar floating-point values ***

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that IEEE double-precision (8-byte)
    /// floating-point value into the specified `variable` if its type is
    /// appropriate, update the cursor location, and return a reference to
    /// this stream.  If the type is incorrect, then this stream is marked
    /// invalid and the value of `variable` is unchanged.  If this stream is
    /// initially invalid, this operation has no effect.  If this function
    /// otherwise fails to extract a valid value, this stream is marked
    /// invalid and the value of `variable` is undefined.  Note that for
    /// non-conforming platforms, this operation may be lossy.
    TestInStream& getFloat64(double& variable);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that IEEE single-precision (4-byte)
    /// floating-point value into the specified `variable` if its type is
    /// appropriate, update the cursor location, and return a reference to
    /// this stream.  If the type is incorrect, then this stream is marked
    /// invalid and the value of `variable` is unchanged.  If this stream is
    /// initially invalid, this operation has no effect.  If this function
    /// otherwise fails to extract a valid value, this stream is marked
    /// invalid and the value of `variable` is undefined.  Note that for
    /// non-conforming platforms, this operation may be lossy.
    TestInStream& getFloat32(float& variable);

                      // *** string values ***

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume a string
    /// from this input stream, assign that value to the specified
    /// `variable`, update the cursor location, and return a reference to
    /// this stream.  If this stream is initially invalid, this operation
    /// has no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variable` is
    /// undefined.  The string must be prefaced by a non-negative integer
    /// indicating the number of characters composing the string.  The
    /// behavior is undefined unless the length indicator is non-negative.
    TestInStream& getString(bsl::string& variable);

                      // *** arrays of integer values ***

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 64-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt64(bsls::Types::Int64 *variables,
                                int                 numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 64-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint64(bsls::Types::Uint64 *variables,
                                 int                  numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 56-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt56(bsls::Types::Int64 *variables,
                                int                 numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 56-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint56(bsls::Types::Uint64 *variables,
                                 int                  numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 48-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt48(bsls::Types::Int64 *variables,
                                int                 numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 48-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint48(bsls::Types::Uint64 *variables,
                                 int                  numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 40-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt40(bsls::Types::Int64 *variables,
                                int                 numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 40-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint40(bsls::Types::Uint64 *variables,
                                 int                  numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 32-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt32(int *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 32-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint32(unsigned int *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 24-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt24(int *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 24-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint24(unsigned int *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 16-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt16(short *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 16-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint16(unsigned short *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 8-bit signed integer array value into the
    /// specified `variables` of the specified `numVariables` if its type
    /// and length are appropriate, update the cursor location, and return a
    /// reference to this stream.  If the type is incorrect, then this
    /// stream is marked invalid and the value of `variables` is unchanged.
    /// If this stream is initially invalid, this operation has no effect.
    /// If this function otherwise fails to extract a valid value, this
    /// stream is marked invalid and the value of `variables` is undefined.
    /// The behavior is undefined unless `0 <= numVariables` and `variables`
    /// has sufficient capacity.
    TestInStream& getArrayInt8(char        *variables, int numVariables);
    TestInStream& getArrayInt8(signed char *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that 8-bit unsigned integer array value into
    /// the specified `variables` of the specified `numVariables` if its
    /// type and length are appropriate, update the cursor location, and
    /// return a reference to this stream.  If the type is incorrect, then
    /// this stream is marked invalid and the value of `variables` is
    /// unchanged.  If this stream is initially invalid, this operation has
    /// no effect.  If this function otherwise fails to extract a valid
    /// value, this stream is marked invalid and the value of `variables` is
    /// undefined.  The behavior is undefined unless `0 <= numVariables` and
    /// `variables` has sufficient capacity.
    TestInStream& getArrayUint8(char          *variables, int numVariables);
    TestInStream& getArrayUint8(unsigned char *variables, int numVariables);

                      // *** arrays of floating-point values ***

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that IEEE double-precision (8-byte)
    /// floating-point array value into the specified `variables` of the
    /// specified `numVariables` if its type and length are appropriate,
    /// update the cursor location, and return a reference to this stream.
    /// If the type is incorrect, then this stream is marked invalid and the
    /// value of `variables` is unchanged.  If this stream is initially
    /// invalid, this operation has no effect.  If this function otherwise
    /// fails to extract a valid value, this stream is marked invalid and
    /// the value of `variables` is undefined.  The behavior is undefined
    /// unless `0 <= numVariables` and `variables` has sufficient capacity.
    /// Note that for non-conforming platforms, this operation may be lossy.
    TestInStream& getArrayFloat64(double *variables, int numVariables);

    /// If required, throw a `TestInStreamException` (see
    /// `throwExceptionIfInputLimitExhausted`); otherwise, consume the 8-bit
    /// unsigned integer type code, verify the type of the next value in
    /// this stream, consume that IEEE single-precision (4-byte)
    /// floating-point array value into the specified `variables` of the
    /// specified `numVariables` if its type and length are appropriate,
    /// update the cursor location, and return a reference to this stream.
    /// If the type is incorrect, then this stream is marked invalid and the
    /// value of `variables` is unchanged.  If this stream is initially
    /// invalid, this operation has no effect.  If this function otherwise
    /// fails to extract a valid value, this stream is marked invalid and
    /// the value of `variables` is undefined.  The behavior is undefined
    /// unless `0 <= numVariables` and `variables` has sufficient capacity.
    /// Note that for non-conforming platforms, this operation may be lossy.
    TestInStream& getArrayFloat32(float *variables, int numVariables);

    // ACCESSORS

    /// Return a non-zero value if this stream is valid, and 0 otherwise.  An
    /// invalid stream is a stream for which an input operation was detected to
    /// have failed.
    operator const void *() const;

    /// Return the index of the next byte to be extracted from this stream.
    bsl::size_t cursor() const;

    /// Return the address of the contiguous, non-modifiable external memory
    /// buffer of this stream.  The behavior of accessing elements outside the
    /// range `[ data() .. data() + (length() - 1) ]` is undefined.
    const char *data() const;

    /// Return the current number of input requests left before an exception is
    /// thrown.  A negative value indicates that no exception is scheduled.
    int inputLimit() const;

    /// Return `true` if this stream is empty, and `false` otherwise.  Note
    /// that this function enables higher-level types to verify that, after
    /// successfully reading all expected data, no data remains.
    bool isEmpty() const;

    /// Return `true` if this stream's quiet mode is ON, and `false` otherwise.
    bool isQuiet() const;

    /// Return `true` if this stream is valid, and `false` otherwise.  An
    /// invalid stream is a stream in which insufficient or invalid data was
    /// detected during an extraction operation.  Note that an empty stream
    /// will be valid unless an extraction attempt or explicit invalidation
    /// causes it to be otherwise.
    bool isValid() const;

    /// Return the total number of bytes stored in the external memory buffer.
    bsl::size_t length() const;
};

// FREE OPERATORS

/// Write the specified `object` to the specified output `stream` in some
/// reasonable (multi-line) format, and return a reference to `stream`.
bsl::ostream& operator<<(bsl::ostream& stream, const TestInStream& object);

/// Read the specified `value` from the specified input `stream` following the
/// requirements of the BDEX protocol (see the `bslx` package-level
/// documentation), and return a reference to `stream`.  The behavior is
/// undefined unless `TYPE` is BDEX-compliant.
template <class TYPE>
TestInStream& operator>>(TestInStream& stream, TYPE& value);

}  // close package namespace

               // ============================================
               // macro BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN
               // ============================================

#ifdef BDE_BUILD_TARGET_EXC

namespace bslx {

/// This class provides a common base class for the parameterized
/// `TestInStream_Proxy` class (below).  Note that the `virtual`
/// `setInputLimit` method, although a "setter", *must* be declared `const`.
class TestInStream_ProxyBase {

  public:
    virtual ~TestInStream_ProxyBase()
    {
    }

    // ACCESSORS
    virtual void setInputLimit(int limit) const = 0;
};

/// This class provides a proxy to the test stream that is supplied to the
/// `BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN` macro.  This proxy may be
/// instantiated with `TestInStream`, or with a type that supports the same
/// interface as `TestInStream`.
template <class BSLX_STREAM_TYPE>
class TestInStream_Proxy: public TestInStream_ProxyBase {

    // DATA
    BSLX_STREAM_TYPE *d_stream_p;  // stream used in '*_BEGIN' and
                                   // '*_END' macros (held, not owned)

  public:
    // CREATORS
    TestInStream_Proxy(BSLX_STREAM_TYPE *stream)
    : d_stream_p(stream)
    {
    }

    ~TestInStream_Proxy() BSLS_KEYWORD_OVERRIDE
    {
    }

    // ACCESSORS
    void setInputLimit(int limit) const BSLS_KEYWORD_OVERRIDE
    {
        d_stream_p->setInputLimit(limit);
    }
};

/// Return, by value, a test stream proxy for the specified parameterized
/// `stream`.
template <class BSLX_STREAM_TYPE>
inline
TestInStream_Proxy<BSLX_STREAM_TYPE>
TestInStream_getProxy(BSLX_STREAM_TYPE *stream)
{
    return TestInStream_Proxy<BSLX_STREAM_TYPE>(stream);
}

}  // close package namespace

#ifndef BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN
#define BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN(BSLX_TESTINSTREAM)             \
{                                                                             \
    const bslx::TestInStream_ProxyBase& testInStream =                        \
                       bslx::TestInStream_getProxy(&BSLX_TESTINSTREAM);       \
    {                                                                         \
        static int firstTime = 1;                                             \
        if (veryVerbose && firstTime) bsl::cout <<                            \
            "### BSLX EXCEPTION TEST -- (ENABLED) --" << '\n';                \
        firstTime = 0;                                                        \
    }                                                                         \
    if (veryVeryVerbose) bsl::cout <<                                         \
        "### Begin BSLX exception test." << '\n';                             \
    int bslxExceptionCounter = 0;                                             \
    static int bslxExceptionLimit = 100;                                      \
    testInStream.setInputLimit(bslxExceptionCounter);                         \
    do {                                                                      \
        try {
#endif  // BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN

#else // !defined(BDE_BUILD_TARGET_EXC)

#ifndef BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN
#define BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN(testInStream)                  \
{                                                                             \
    static int firstTime = 1;                                                 \
    if (verbose && firstTime) {                                               \
        bsl::cout << "### BSLX EXCEPTION TEST -- (NOT ENABLED) --" << '\n';   \
        firstTime = 0;                                                        \
    }                                                                         \
}
#endif  // BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN

#endif  // BDE_BUILD_TARGET_EXC

               // ==========================================
               // macro BSLX_TESTINSTREAM_EXCEPTION_TEST_END
               // ==========================================

#ifdef BDE_BUILD_TARGET_EXC

#ifndef BSLX_TESTINSTREAM_EXCEPTION_TEST_END
#define BSLX_TESTINSTREAM_EXCEPTION_TEST_END                                  \
        } catch (bslx::TestInStreamException& e) {                            \
            if ((veryVerbose && bslxExceptionLimit) || veryVeryVerbose)       \
            {                                                                 \
                --bslxExceptionLimit;                                         \
                bsl::cout << "(" << bslxExceptionCounter << ')';              \
                if (veryVeryVerbose) {                                        \
                    bsl::cout << " BSLX EXCEPTION: "                          \
                              << "input limit = "                             \
                              << bslxExceptionCounter                         \
                              << ", "                                         \
                              << "last data type = "                          \
                              << e.dataType();                                \
                }                                                             \
                else if (0 == bslxExceptionLimit) {                           \
                    bsl::cout << " [ Note: 'bslxExceptionLimit' reached. ]";  \
                }                                                             \
                bsl::cout << '\n';                                            \
            }                                                                 \
            testInStream.setInputLimit(++bslxExceptionCounter);               \
            continue;                                                         \
        }                                                                     \
        testInStream.setInputLimit(-1);                                       \
        break;                                                                \
    } while (1);                                                              \
    if (veryVeryVerbose) {                                                    \
        bsl::cout << "### End BSLX exception test." << '\n';                  \
    }                                                                         \
}
#endif  // BSLX_TESTINSTREAM_EXCEPTION_TEST_END

#else // !defined(BDE_BUILD_TARGET_EXC)

#ifndef BSLX_TESTINSTREAM_EXCEPTION_TEST_END
#define BSLX_TESTINSTREAM_EXCEPTION_TEST_END
#endif

#endif  // BDE_BUILD_TARGET_EXC

namespace bslx {

// ============================================================================
//                          INLINE DEFINITIONS
// ============================================================================

                         // ------------------
                         // class TestInStream
                         // ------------------

// PRIVATE MANIPULATORS
inline
void TestInStream::throwExceptionIfInputLimitExhausted(
                                                    const TypeCode::Enum& code)
{
#ifdef BDE_BUILD_TARGET_EXC
    if (0 <= d_inputLimit) {
        --d_inputLimit;
        if (0 > d_inputLimit) {
            throw TestInStreamException(code);
        }
    }
#else
    (void)code;
#endif
}

// MANIPULATORS
inline
void TestInStream::invalidate()
{
    d_validFlag = false;
}

inline
void TestInStream::reset()
{
    d_validFlag = true;
    d_cursor    = 0;
}

inline
void TestInStream::reset(const char *buffer, bsl::size_t numBytes)
{
    BSLS_ASSERT_SAFE(buffer || 0 == numBytes);

    d_buffer    = buffer;
    d_numBytes  = numBytes;
    d_validFlag = true;
    d_cursor    = 0;
}

inline
void TestInStream::reset(const bslstl::StringRef& srcData)
{
    d_buffer    = srcData.data();
    d_numBytes  = srcData.length();
    d_validFlag = true;
    d_cursor    = 0;
}

inline
void TestInStream::seek(bsl::size_t offset)
{
    BSLS_ASSERT_SAFE(offset <= length());

    d_cursor    = offset;
    d_validFlag = 1;
}

inline
void TestInStream::setInputLimit(int limit)
{
    d_inputLimit = limit;
}

inline
void TestInStream::setQuiet(bool flagValue)
{
    d_quietFlag = flagValue;
}

// ACCESSORS
inline
TestInStream::operator const void *() const
{
    return isValid() ? this : 0;
}

inline
bsl::size_t TestInStream::cursor() const
{
    return d_cursor;
}

inline
const char *TestInStream::data() const
{
    return d_numBytes ? d_buffer : 0;
}

inline
int TestInStream::inputLimit() const
{
    return d_inputLimit;
}

inline
bool TestInStream::isEmpty() const
{
    return cursor() >= length();
}

inline
bool TestInStream::isQuiet() const
{
    return d_quietFlag;
}

inline
bool TestInStream::isValid() const
{
    return d_validFlag;
}

inline
bsl::size_t TestInStream::length() const
{
    return d_numBytes;
}

template <class TYPE>
inline
TestInStream& operator>>(TestInStream& stream, TYPE& value)
{
    return InStreamFunctions::bdexStreamIn(stream, value);
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
