// 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:
//
//: o 'BSLX_TESTINSTREAM_EXCEPTION_TEST_BEGIN(testInStream)'
//: o '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
//      static int maxSupportedBdexVersion(int versionSelector);
//          // 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.
//
//      // CREATORS
//      MyPerson();
//          // Create a default person.
//
//      MyPerson(const char *firstName, const char *lastName, int age);
//          // Create a person having the specified 'firstName', 'lastName',
//          // and 'age'.
//
//      MyPerson(const MyPerson& original);
//          // Create a person having the value of the specified 'original'
//          // person.
//
//      ~MyPerson();
//          // Destroy this object.
//
//      // MANIPULATORS
//      MyPerson& operator=(const MyPerson& rhs);
//          // Assign to this person the value of the specified 'rhs' person,
//          // and return a reference to this person.
//
//      template <class STREAM>
//      STREAM& bdexStreamIn(STREAM& stream, int version);
//          // 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.
//
//      //...
//
//      // ACCESSORS
//      const bsl::string& firstName() const;
//          // Return the first name of this person.
//
//      const bsl::string& lastName() const;
//          // Return the last name of this person.
//
//      int age() const;
//          // Return the age of this person.
//
//      template <class STREAM>
//      STREAM& bdexStreamOut(STREAM& stream, int version) 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.
//
//      //...
//
//  };
//
//  // FREE OPERATORS
//  bool operator==(const MyPerson& lhs, const MyPerson& rhs);
//      // 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.
//
//  bsl::ostream& operator<<(bsl::ostream& stream, const MyPerson& person);
//      // Write the specified 'person' value to the specified output 'stream'
//      // in some reasonable format, and return a reference to 'stream'.
//
//  // ========================================================================
//  //                  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_types.h>

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

namespace BloombergLP {
namespace bslx {

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

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.

    // 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
    void checkArray(TypeCode::Enum code,
                    int            elementSize,
                    int            numElements);
        // 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 checkTypeCodeAndAvailableLength(TypeCode::Enum code,
                                         bsl::size_t    numExpectedBytes);
        // 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 throwExceptionIfInputLimitExhausted(const TypeCode::Enum& code);
        // Decrement the internal input limit of this test stream.  If the
        // input limit becomes negative and exception-handling is enabled
        // (i.e., '-DBDE_BUILD_TARGET_EXC' was supplied at compile time), then
        // throw a 'TestInStreamException' object initialized with the
        // specified type 'code'.  If exception-handling is not enabled, this
        // method has no effect.

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

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

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

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

    // MANIPULATORS
    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 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& getVersion(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.

    void invalidate();
        // 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 reset();
        // 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(const char *buffer, bsl::size_t numBytes);
        // 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 bslstl::StringRef& srcData);
        // 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 seek(bsl::size_t offset);
        // 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 setInputLimit(int limit);
        // 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 setQuiet(bool flagValue);
        // 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.

                      // *** scalar integer values ***

    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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& getUint8(char&          variable);
    TestInStream& getUint8(unsigned 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.

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

    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 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& getFloat32(float& 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.

                      // *** string values ***

    TestInStream& getString(bsl::string& variable);
        // 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.

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

    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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& 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 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& getArrayUint8(char          *variables, int numVariables);
    TestInStream& getArrayUint8(unsigned 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.

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

    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 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& getArrayFloat32(float *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.

    // ACCESSORS
    operator const void *() const;
        // 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.

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

    const char *data() 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.

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

    bool isEmpty() 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 isQuiet() const;
        // Return 'true' if this stream's quiet mode is ON, and 'false'
        // otherwise.

    bool isValid() 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.

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

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

template <class TYPE>
TestInStream& operator>>(TestInStream& stream, TYPE& value);
    // 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.

}  // close package namespace

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

#ifdef BDE_BUILD_TARGET_EXC

namespace bslx {

class TestInStream_ProxyBase {
    // 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'.

  public:
    virtual ~TestInStream_ProxyBase()
    {
    }

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

template <class BSLX_STREAM_TYPE>
class TestInStream_Proxy: public TestInStream_ProxyBase {
    // 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'.

    // 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()
    {
    }

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

template <class BSLX_STREAM_TYPE>
inline
TestInStream_Proxy<BSLX_STREAM_TYPE>
TestInStream_getProxy(BSLX_STREAM_TYPE *stream)
    // Return, by value, a test stream proxy for the specified parameterized
    // '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 ----------------------------------