// bslx_byteinstream.h                                                -*-C++-*-
#ifndef INCLUDED_BSLX_BYTEINSTREAM
#define INCLUDED_BSLX_BYTEINSTREAM

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

//@PURPOSE: Provide a stream class for unexternalization of fundamental types.
//
//@CLASSES:
//  bslx::ByteInStream: byte-array-based input stream for fundamental types
//
//@SEE_ALSO: bslx_byteoutstream
//
//@DESCRIPTION: This component implements a byte-array-based input stream
// class, 'bslx::ByteInStream', that provides platform-independent input
// methods ("unexternalization") on values, and arrays of values, of
// fundamental types, and on 'bsl::string'.
//
// The 'bslx::ByteInStream' 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_byteoutstream' "externalization" component.  Each input method of
// 'bslx::ByteInStream' reads either a value or a homogeneous array of values
// of a fundamental type, in a format that was written by the corresponding
// 'bslx::ByteOutStream' 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::ByteOutStream'.
//
// 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.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Unexternalization
/// - - - - - - - - - - - - - - - - -
// 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
//      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.
//
//      const bsl::string& firstName() const;
//          // Return the first name of this person.
//
//      const bsl::string& lastName() const;
//          // Return the last name of this person.
//
//      //...
//
//  };
//
//  // 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.
//
//  // ========================================================================
//  //                  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;                                    // RETURN
//              }
//              stream.getString(d_lastName);
//              if (!stream) {
//                  d_lastName = "stream error";  // *might* be corrupted;
//                                                //  value for testing
//                  return stream;                                    // RETURN
//              }
//              stream.getInt32(d_age);
//              if (!stream) {
//                  d_age = 999;     // *might* be corrupted; value for testing
//                  return stream;                                    // RETURN
//              }
//            } break;
//            default: {
//              stream.invalidate();
//            }
//          }
//      }
//      return stream;
//  }
//
//  // ACCESSORS
//  inline
//  int MyPerson::age() const
//  {
//      return d_age;
//  }
//
//  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;
//  }
//
//  inline
//  const bsl::string& MyPerson::firstName() const
//  {
//      return d_firstName;
//  }
//
//  inline
//  const bsl::string& MyPerson::lastName() const
//  {
//      return d_lastName;
//  }
//
//  // FREE OPERATORS
//  inline
//  bool operator==(const MyPerson& lhs, const MyPerson& rhs)
//  {
//      return lhs.d_firstName == rhs.d_firstName &&
//             lhs.d_lastName  == rhs.d_lastName  &&
//             lhs.d_age       == rhs.d_age;
//  }
//
//  inline
//  bool operator!=(const MyPerson& lhs, const MyPerson& rhs)
//  {
//      return !(lhs == rhs);
//  }
//..
// Then, we can exercise the new 'MyPerson' value-semantic class by
// externalizing and reconstituting an object.  First, create a 'MyPerson'
// 'janeSmith' and a 'bslx::ByteOutStream' 'outStream':
//..
//  MyPerson            janeSmith("Jane", "Smith", 42);
//  bslx::ByteOutStream 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::ByteInStream' 'inStream' initialized with the buffer
// from the 'bslx::ByteOutStream' object 'outStream' and unexternalize this
// data into 'janeCopy':
//..
//  bslx::ByteInStream 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_marshallingutil.h>

#include <bsls_assert.h>
#include <bsls_performancehint.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 ByteInStream
                         // ==================

class ByteInStream {
    // This class provides input methods to unexternalize values, and C-style
    // arrays of values, of the fundamental integral and floating-point types,
    // as well as 'bsl::string' values, using a byte format documented in the
    // 'bslx_byteoutstream' component.  In particular, each 'get' method of
    // this class is guaranteed to read stream data written by the
    // corresponding 'put' method of 'bslx::ByteOutStream'.  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

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

    // FRIENDS
    friend bsl::ostream& operator<<(bsl::ostream&       stream,
                                    const ByteInStream& object);

  private:
    // NOT IMPLEMENTED
    ByteInStream(const ByteInStream&);
    ByteInStream& operator=(const ByteInStream&);

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

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

    explicit ByteInStream(const bslstl::StringRef& srcData);
        // Create an input byte stream containing the specified 'srcData'.

    ~ByteInStream();
        // Destroy this object.

    // MANIPULATORS
    ByteInStream& getLength(int& length);
        // If the most-significant bit of the one byte of this stream at the
        // current cursor location is set, assign to the specified 'length' the
        // four-byte, two's complement integer (in host byte order) comprised
        // of the four bytes of this stream at the current cursor location (in
        // network byte order) with the most-significant bit unset; otherwise,
        // assign to 'length' the one-byte, two's complement integer comprised
        // of the one byte of this stream at the current cursor location.
        // 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 'length' is undefined.
        // Note that the value will be zero-extended.

    ByteInStream& getVersion(int& version);
        // Assign to the specified 'version' the one-byte, two's complement
        // unsigned integer comprised of the one byte of this stream at the
        // current cursor location, 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 'version' is undefined.  Note that the value will be
        // zero-extended.

    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.

                      // *** scalar integer values ***

    ByteInStream& getInt64(bsls::Types::Int64& variable);
        // Assign to the specified 'variable' the eight-byte, two's complement
        // integer (in host byte order) comprised of the eight bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint64(bsls::Types::Uint64& variable);
        // Assign to the specified 'variable' the eight-byte, two's complement
        // unsigned integer (in host byte order) comprised of the eight bytes
        // of this stream at the current cursor location (in network byte
        // order), 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.  Note that the value will be zero-extended.

    ByteInStream& getInt56(bsls::Types::Int64& variable);
        // Assign to the specified 'variable' the seven-byte, two's complement
        // integer (in host byte order) comprised of the seven bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint56(bsls::Types::Uint64& variable);
        // Assign to the specified 'variable' the seven-byte, two's complement
        // unsigned integer (in host byte order) comprised of the seven bytes
        // of this stream at the current cursor location (in network byte
        // order), 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.  Note that the value will be zero-extended.

    ByteInStream& getInt48(bsls::Types::Int64& variable);
        // Assign to the specified 'variable' the six-byte, two's complement
        // integer (in host byte order) comprised of the six bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint48(bsls::Types::Uint64& variable);
        // Assign to the specified 'variable' the six-byte, two's complement
        // unsigned integer (in host byte order) comprised of the six bytes of
        // this stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be zero-extended.

    ByteInStream& getInt40(bsls::Types::Int64& variable);
        // Assign to the specified 'variable' the five-byte, two's complement
        // integer (in host byte order) comprised of the five bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint40(bsls::Types::Uint64& variable);
        // Assign to the specified 'variable' the five-byte, two's complement
        // unsigned integer (in host byte order) comprised of the five bytes of
        // this stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be zero-extended.

    ByteInStream& getInt32(int& variable);
        // Assign to the specified 'variable' the four-byte, two's complement
        // integer (in host byte order) comprised of the four bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint32(unsigned int& variable);
        // Assign to the specified 'variable' the four-byte, two's complement
        // unsigned integer (in host byte order) comprised of the four bytes of
        // this stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be zero-extended.

    ByteInStream& getInt24(int& variable);
        // Assign to the specified 'variable' the three-byte, two's complement
        // integer (in host byte order) comprised of the three bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint24(unsigned int& variable);
        // Assign to the specified 'variable' the three-byte, two's complement
        // unsigned integer (in host byte order) comprised of the three bytes
        // of this stream at the current cursor location (in network byte
        // order), 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.  Note that the value will be zero-extended.

    ByteInStream& getInt16(short& variable);
        // Assign to the specified 'variable' the two-byte, two's complement
        // integer (in host byte order) comprised of the two bytes of this
        // stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be sign-extended.

    ByteInStream& getUint16(unsigned short& variable);
        // Assign to the specified 'variable' the two-byte, two's complement
        // unsigned integer (in host byte order) comprised of the two bytes of
        // this stream at the current cursor location (in network byte order),
        // 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.
        // Note that the value will be zero-extended.

    ByteInStream& getInt8(char&        variable);
    ByteInStream& getInt8(signed char& variable);
        // Assign to the specified 'variable' the one-byte, two's complement
        // integer comprised of the one byte of this stream at the current
        // cursor location, 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.  Note that the value will be sign-extended.

    ByteInStream& getUint8(char&          variable);
    ByteInStream& getUint8(unsigned char& variable);
        // Assign to the specified 'variable' the one-byte, two's complement
        // unsigned integer comprised of the one byte of this stream at the
        // current cursor location, 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.  Note that the value will be
        // zero-extended.

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

    ByteInStream& getFloat64(double& variable);
        // Assign to the specified 'variable' the eight-byte IEEE
        // double-precision floating-point number (in host byte order)
        // comprised of the eight bytes of this stream at the current cursor
        // location (in network byte order), 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.

    ByteInStream& getFloat32(float& variable);
        // Assign to the specified 'variable' the four-byte IEEE
        // single-precision floating-point number (in host byte order)
        // comprised of the four bytes of this stream at the current cursor
        // location (in network byte order), 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.

                      // *** string values ***

    ByteInStream& getString(bsl::string& variable);
        // Assign to the specified 'variable' the string comprised of the
        // length of the string (see 'getLength') and the string data (see
        // 'getUint8'), 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.

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

    ByteInStream& getArrayInt64(bsls::Types::Int64 *variables,
                                int                 numVariables);
        // Assign to the specified 'variables' the consecutive eight-byte,
        // two's complement integers (in host byte order) comprised of each of
        // the specified 'numVariables' eight-byte sequences of this stream at
        // the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numVariables' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // sign-extended.

    ByteInStream& getArrayUint64(bsls::Types::Uint64 *variables,
                                 int                  numVariables);
        // Assign to the specified 'variables' the consecutive eight-byte,
        // two's complement unsigned integers (in host byte order) comprised of
        // each of the specified 'numVariables' eight-byte sequences of this
        // stream at the current cursor location (in network byte order),
        // 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 'variables' is undefined.
        // The behavior is undefined unless '0 <= numVariables' and 'variables'
        // has sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt56(bsls::Types::Int64 *variables,
                                int                 numVariables);
        // Assign to the specified 'variables' the consecutive seven-byte,
        // two's complement integers (in host byte order) comprised of each of
        // the specified 'numVariables' seven-byte sequences of this stream at
        // the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numVariables' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // sign-extended.

    ByteInStream& getArrayUint56(bsls::Types::Uint64 *variables,
                                 int                  numVariables);
        // Assign to the specified 'variables' the consecutive seven-byte,
        // two's complement unsigned integers (in host byte order) comprised of
        // each of the specified 'numVariables' seven-byte sequences of this
        // stream at the current cursor location (in network byte order),
        // 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 'variables' is undefined.
        // The behavior is undefined unless '0 <= numVariables' and 'variables'
        // has sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt48(bsls::Types::Int64 *variables,
                                int                 numVariables);
        // Assign to the specified 'variables' the consecutive six-byte, two's
        // complement integers (in host byte order) comprised of each of the
        // specified 'numVariables' six-byte sequences of this stream at the
        // current cursor location (in network byte order), 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 'variables' is undefined.  The behavior is
        // undefined unless '0 <= numVariables' and 'variables' has sufficient
        // capacity.  Note that each of the values will be sign-extended.

    ByteInStream& getArrayUint48(bsls::Types::Uint64 *variables,
                                 int                  numVariables);
        // Assign to the specified 'variables' the consecutive six-byte, two's
        // complement unsigned integers (in host byte order) comprised of each
        // of the specified 'numVariables' six-byte sequences of this stream at
        // the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numVariables' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt40(bsls::Types::Int64 *variables,
                                int                 numVariables);
        // Assign to the specified 'variables' the consecutive five-byte, two's
        // complement integers (in host byte order) comprised of each of the
        // specified 'numVariables' five-byte sequences of this stream at the
        // current cursor location (in network byte order), 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 'variables' is undefined.  The behavior is
        // undefined unless '0 <= numVariables' and 'variables' has sufficient
        // capacity.  Note that each of the values will be sign-extended.

    ByteInStream& getArrayUint40(bsls::Types::Uint64 *variables,
                                 int                  numVariables);
        // Assign to the specified 'variables' the consecutive five-byte, two's
        // complement unsigned integers (in host byte order) comprised of each
        // of the specified 'numVariables' five-byte sequences of this stream
        // at the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numVariables' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt32(int *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive four-byte, two's
        // complement integers (in host byte order) comprised of each of the
        // specified 'numVariables' four-byte sequences of this stream at the
        // current cursor location (in network byte order), 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 'variables' is undefined.  The behavior is
        // undefined unless '0 <= numVariables' and 'variables' has sufficient
        // capacity.  Note that each of the values will be sign-extended.

    ByteInStream& getArrayUint32(unsigned int *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive four-byte, two's
        // complement unsigned integers (in host byte order) comprised of each
        // of the specified 'numVariables' four-byte sequences of this stream
        // at the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numVariables' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt24(int *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive three-byte,
        // two's complement integers (in host byte order) comprised of each of
        // the specified 'numVariables' three-byte sequences of this stream at
        // the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numValues' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // sign-extended.

    ByteInStream& getArrayUint24(unsigned int *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive three-byte,
        // two's complement unsigned integers (in host byte order) comprised of
        // each of the specified 'numVariables' three-byte sequences of this
        // stream at the current cursor location (in network byte order),
        // 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 'variables' is undefined.
        // The behavior is undefined unless '0 <= numVariables' and 'variables'
        // has sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt16(short *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive two-byte, two's
        // complement integers (in host byte order) comprised of each of the
        // specified 'numVariables' two-byte sequences of this stream at the
        // current cursor location (in network byte order), 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 'variables' is undefined.  The behavior is
        // undefined unless '0 <= numVariables' and 'variables' has sufficient
        // capacity.  Note that each of the values will be sign-extended.

    ByteInStream& getArrayUint16(unsigned short *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive two-byte, two's
        // complement unsigned integers (in host byte order) comprised of each
        // of the specified 'numVariables' two-byte sequences of this stream at
        // the current cursor location (in network byte order), 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 'variables' is undefined.  The
        // behavior is undefined unless '0 <= numVariables' and 'variables' has
        // sufficient capacity.  Note that each of the values will be
        // zero-extended.

    ByteInStream& getArrayInt8(char *variables,        int numVariables);
    ByteInStream& getArrayInt8(signed char *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive one-byte, two's
        // complement integers comprised of each of the specified
        // 'numVariables' one-byte sequences of this stream at the current
        // cursor location, 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 'variables' is
        // undefined.  The behavior is undefined unless '0 <= numVariables' and
        // 'variables' has sufficient capacity.  Note that each of the values
        // will be sign-extended.

    ByteInStream& getArrayUint8(char *variables,          int numVariables);
    ByteInStream& getArrayUint8(unsigned char *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive one-byte, two's
        // complement unsigned integers comprised of each of the specified
        // 'numVariables' one-byte sequences of this stream at the current
        // cursor location, 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 'variables' is
        // undefined.  The behavior is undefined unless '0 <= numVariables' and
        // 'variables' has sufficient capacity.  Note that each of the values
        // will be zero-extended.

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

    ByteInStream& getArrayFloat64(double *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive eight-byte IEEE
        // double-precision floating-point numbers (in host byte order)
        // comprised of each of the specified 'numVariables' eight-byte
        // sequences of this stream at the current cursor location (in network
        // byte order), 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 'variables' is
        // undefined.  The behavior is undefined unless '0 <= numVariables' and
        // 'variables' has sufficient capacity.

    ByteInStream& getArrayFloat32(float *variables, int numVariables);
        // Assign to the specified 'variables' the consecutive four-byte IEEE
        // single-precision floating-point numbers (in host byte order)
        // comprised of each of the specified 'numVariables' four-byte
        // sequences of this stream at the current cursor location (in network
        // byte order), 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 'variables' is
        // undefined.  The behavior is undefined unless '0 <= numVariables' and
        // 'variables' has sufficient capacity.

    // 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.

    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 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 ByteInStream& 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>
ByteInStream& operator>>(ByteInStream& 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.

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

                         // ------------------
                         // class ByteInStream
                         // ------------------

// CREATORS
inline
ByteInStream::ByteInStream()
: d_buffer(0)
, d_numBytes(0)
, d_validFlag(true)
, d_cursor(0)
{
}

inline
ByteInStream::ByteInStream(const char *buffer, bsl::size_t numBytes)
: d_buffer(buffer)
, d_numBytes(numBytes)
, d_validFlag(true)
, d_cursor(0)
{
    BSLS_ASSERT_SAFE(buffer || 0 == numBytes);
}

inline
ByteInStream::ByteInStream(const bslstl::StringRef& srcData)
: d_buffer(srcData.data())
, d_numBytes(static_cast<int>(srcData.length()))
, d_validFlag(true)
, d_cursor(0)
{
}

inline
ByteInStream::~ByteInStream()
{
}

// MANIPULATORS
inline
ByteInStream& ByteInStream::getLength(int& length)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT8 <= ByteInStream::length()) {
        if (127 < static_cast<unsigned char>(d_buffer[cursor()])) {
            // If 'length > 127', 'length' is stored as 4 bytes with top bit
            // set.

            getInt32(length);
            length &= 0x7fffffff;  // Clear top bit.
        }
        else {
            // If 'length <= 127', 'length' is stored as one byte.

            char tmp;
            MarshallingUtil::getInt8(&tmp, data() + cursor());
            d_cursor += MarshallingUtil::k_SIZEOF_INT8;
            length = tmp;
        }
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getVersion(int& version)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    unsigned char tmp;
    getUint8(tmp);
    version = tmp;

    return *this;
}

inline
void ByteInStream::invalidate()
{
    d_validFlag = false;
}

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

inline
void ByteInStream::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 ByteInStream::reset(const bslstl::StringRef& srcData)
{
    d_buffer    = srcData.data();
    d_numBytes  = srcData.length();
    d_validFlag = true;
    d_cursor    = 0;
}

                      // *** scalar integer values ***

inline
ByteInStream& ByteInStream::getInt64(bsls::Types::Int64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT64 <= length()) {
        MarshallingUtil::getInt64(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT64;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint64(bsls::Types::Uint64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT64 <= length()) {
        MarshallingUtil::getUint64(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT64;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt56(bsls::Types::Int64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT56 <= length()) {
        MarshallingUtil::getInt56(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT56;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint56(bsls::Types::Uint64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT56 <= length()) {
        MarshallingUtil::getUint56(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT56;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt48(bsls::Types::Int64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT48 <= length()) {
        MarshallingUtil::getInt48(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT48;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint48(bsls::Types::Uint64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT48 <= length()) {
        MarshallingUtil::getUint48(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT48;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt40(bsls::Types::Int64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT40 <= length()) {
        MarshallingUtil::getInt40(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT40;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint40(bsls::Types::Uint64& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT40 <= length()) {
        MarshallingUtil::getUint40(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT40;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt32(int& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT32 <= length()) {
        MarshallingUtil::getInt32(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT32;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint32(unsigned int& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT32 <= length()) {
        MarshallingUtil::getUint32(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT32;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt24(int& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT24 <= length()) {
        MarshallingUtil::getInt24(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT24;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint24(unsigned int& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT24 <= length()) {
        MarshallingUtil::getUint24(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT24;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt16(short& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT16 <= length()) {
        MarshallingUtil::getInt16(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT16;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getUint16(unsigned short& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT16 <= length()) {
        MarshallingUtil::getUint16(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT16;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt8(char& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_INT8 <= length()) {
        MarshallingUtil::getInt8(&variable, data() + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_INT8;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getInt8(signed char& variable)
{
    return getInt8(reinterpret_cast<char&>(variable));
}

inline
ByteInStream& ByteInStream::getUint8(char& variable)
{
    return getInt8(variable);
}

inline
ByteInStream& ByteInStream::getUint8(unsigned char& variable)
{
    return getInt8(reinterpret_cast<char&>(variable));
}

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

inline
ByteInStream& ByteInStream::getFloat64(double& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_FLOAT64 <= length()) {
        MarshallingUtil::getFloat64(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_FLOAT64;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getFloat32(float& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    if (cursor() + MarshallingUtil::k_SIZEOF_FLOAT32 <= length()) {
        MarshallingUtil::getFloat32(&variable, d_buffer + cursor());
        d_cursor += MarshallingUtil::k_SIZEOF_FLOAT32;
    }
    else {
        invalidate();
    }

    return *this;
}

                      // *** string values ***

inline
ByteInStream& ByteInStream::getString(bsl::string& variable)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    int length;
    getLength(length);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // 'length' could be corrupt or invalid, so we limit the initial 'resize'
    // to something that can accommodate the preponderance of strings that will
    // arise in practice.  The remaining portion of a string longer than 16M is
    // read in via a second pass.

    enum { k_INITIAL_ALLOCATION_SIZE = 16 * 1024 * 1024 };

    const int initialLength = length < k_INITIAL_ALLOCATION_SIZE
                              ? length
                              : k_INITIAL_ALLOCATION_SIZE;

    variable.resize(initialLength);

    if (0 == length) {
        return *this;                                                 // RETURN
    }

    getArrayUint8(&variable.front(), initialLength);
    if (isValid() && length > initialLength) {
        variable.resize(length);
        getArrayUint8(&variable[initialLength], length - initialLength);
    }

    return *this;
}

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

inline
ByteInStream& ByteInStream::getArrayInt64(bsls::Types::Int64 *variables,
                                          int                 numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT64 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt64(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint64(bsls::Types::Uint64 *variables,
                                           int                  numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT64 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint64(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt56(bsls::Types::Int64 *variables,
                                          int                 numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT56 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt56(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint56(bsls::Types::Uint64 *variables,
                                           int                  numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT56 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint56(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt48(bsls::Types::Int64 *variables,
                                          int                 numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT48 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt48(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint48(bsls::Types::Uint64 *variables,
                                           int                  numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT48 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint48(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt40(bsls::Types::Int64 *variables,
                                          int                 numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT40 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt40(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint40(bsls::Types::Uint64 *variables,
                                           int                  numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT40 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint40(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt32(int *variables, int numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT32 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt32(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint32(unsigned int *variables,
                                           int           numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT32 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint32(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt24(int *variables, int numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT24 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt24(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint24(unsigned int *variables,
                                           int           numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT24 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint24(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt16(short *variables, int numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT16 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt16(variables,
                                       d_buffer + cursor(),
                                       numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayUint16(unsigned short *variables,
                                           int             numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT16 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayUint16(variables,
                                        d_buffer + cursor(),
                                        numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt8(char *variables, int numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_INT8 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayInt8(variables,
                                      d_buffer + cursor(),
                                      numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayInt8(signed char *variables,
                                         int          numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    return getArrayInt8(reinterpret_cast<char *>(variables), numVariables);
}

inline
ByteInStream& ByteInStream::getArrayUint8(char *variables, int numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    return getArrayInt8(variables, numVariables);
}

inline
ByteInStream& ByteInStream::getArrayUint8(unsigned char *variables,
                                          int            numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    return getArrayInt8(reinterpret_cast<char *>(variables), numVariables);
}

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

inline
ByteInStream& ByteInStream::getArrayFloat64(double *variables,
                                            int     numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_FLOAT64 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayFloat64(variables,
                                         d_buffer + cursor(),
                                         numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

inline
ByteInStream& ByteInStream::getArrayFloat32(float *variables, int numVariables)
{
    BSLS_ASSERT_SAFE(variables);
    BSLS_ASSERT_SAFE(0 <= numVariables);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    const int len = MarshallingUtil::k_SIZEOF_FLOAT32 * numVariables;
    if (cursor() + len  <= length()) {
        MarshallingUtil::getArrayFloat32(variables,
                                         d_buffer + cursor(),
                                         numVariables);
        d_cursor += len;
    }
    else {
        invalidate();
    }

    return *this;
}

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

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

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

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

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

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

template <class TYPE>
inline
ByteInStream& operator>>(ByteInStream& 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 ----------------------------------