// 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
//
//     /// Return the maximum valid BDEX format version, as indicated by
//     /// the specified `versionSelector`, to be passed to the
//     /// `bdexStreamOut` method.  Note that it is highly recommended that
//     /// `versionSelector` be formatted as "YYYYMMDD", a date
//     /// representation.  Also note that `versionSelector` should be a
//     /// *compile*-time-chosen value that selects a format version
//     /// supported by both externalizer and unexternalizer.  See the
//     /// `bslx` package-level documentation for more information on BDEX
//     /// streaming of value-semantic types and containers.
//     static int maxSupportedBdexVersion(int versionSelector);
//
//     // CREATORS
//
//     /// Create a default person.
//     MyPerson();
//
//     /// Create a person having the specified `firstName`, `lastName`,
//     /// and `age`.
//     MyPerson(const char *firstName, const char *lastName, int age);
//
//     /// Create a person having the value of the specified `original`
//     /// person.
//     MyPerson(const MyPerson& original);
//
//     /// Destroy this object.
//     ~MyPerson();
//
//     // MANIPULATORS
//
//     /// Assign to this person the value of the specified `rhs` person,
//     /// and return a reference to this person.
//     MyPerson& operator=(const MyPerson& rhs);
//
//     /// Assign to this object the value read from the specified input
//     /// `stream` using the specified `version` format, and return a
//     /// reference to `stream`.  If `stream` is initially invalid, this
//     /// operation has no effect.  If `version` is not supported, this
//     /// object is unaltered and `stream` is invalidated, but otherwise
//     /// unmodified.  If `version` is supported but `stream` becomes
//     /// invalid during this operation, this object has an undefined, but
//     /// valid, state.  Note that no version is read from `stream`.  See
//     /// the `bslx` package-level documentation for more information on
//     /// BDEX streaming of value-semantic types and containers.
//     template <class STREAM>
//     STREAM& bdexStreamIn(STREAM& stream, int version);
//
//     //...
//
//     // ACCESSORS
//
//     /// Return the age of this person.
//     int age() const;
//
//     /// Write the value of this object, using the specified `version`
//     /// format, to the specified output `stream`, and return a reference
//     /// to `stream`.  If `stream` is initially invalid, this operation
//     /// has no effect.  If `version` is not supported, `stream` is
//     /// invalidated, but otherwise unmodified.  Note that `version` is
//     /// not written to `stream`.  See the `bslx` package-level
//     /// documentation for more information on BDEX streaming of
//     /// value-semantic types and containers.
//     template <class STREAM>
//     STREAM& bdexStreamOut(STREAM& stream, int version) const;
//
//     /// Return the first name of this person.
//     const bsl::string& firstName() const;
//
//     /// Return the last name of this person.
//     const bsl::string& lastName() const;
//
//     //...
//
// };
//
// // FREE OPERATORS
//
// /// Return `true` if the specified `lhs` and `rhs` person objects have
// /// the same value, and `false` otherwise.  Two person objects have the
// /// same value if they have the same first name, last name, and age.
// bool operator==(const MyPerson& lhs, const MyPerson& rhs);
//
// /// Return 'true' if the specified 'lhs' and 'rhs' person objects do not
// /// have the same value, and 'false' otherwise.  Two person objects
// /// differ in value if they differ in first name, last name, or age.
// bool operator!=(const MyPerson& lhs, const MyPerson& rhs);
//
// // ========================================================================
// //                  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
                         // ==================

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

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

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

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

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

    /// Destroy this object.
    ~ByteInStream();

    // MANIPULATORS

    /// 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& getLength(int& length);

    /// 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.
    ByteInStream& getVersion(int& version);

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

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

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

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

                      // *** scalar integer values ***

    /// 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& getInt64(bsls::Types::Int64& 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& getUint64(bsls::Types::Uint64& 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& getInt56(bsls::Types::Int64& 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& getUint56(bsls::Types::Uint64& 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& getInt48(bsls::Types::Int64& 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& getUint48(bsls::Types::Uint64& 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& getInt40(bsls::Types::Int64& 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& getUint40(bsls::Types::Uint64& 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& getInt32(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& getUint32(unsigned 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& getInt24(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& getUint24(unsigned int& 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& getInt16(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& getUint16(unsigned short& 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& getInt8(char&        variable);
    ByteInStream& getInt8(signed 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.
    ByteInStream& getUint8(char&          variable);
    ByteInStream& getUint8(unsigned char& variable);

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

    /// 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& getFloat64(double& 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.
    ByteInStream& getFloat32(float& variable);

                      // *** string values ***

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

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

    /// 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& getArrayInt64(bsls::Types::Int64 *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& getArrayUint64(bsls::Types::Uint64 *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& getArrayInt56(bsls::Types::Int64 *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& getArrayUint56(bsls::Types::Uint64 *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& getArrayInt48(bsls::Types::Int64 *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& getArrayUint48(bsls::Types::Uint64 *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& getArrayInt40(bsls::Types::Int64 *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& getArrayUint40(bsls::Types::Uint64 *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& getArrayInt32(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& getArrayUint32(unsigned 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& getArrayInt24(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& getArrayUint24(unsigned int *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& getArrayInt16(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& getArrayUint16(unsigned short *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& getArrayInt8(char *variables,        int numVariables);
    ByteInStream& getArrayInt8(signed 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.
    ByteInStream& getArrayUint8(char *variables,          int numVariables);
    ByteInStream& getArrayUint8(unsigned char *variables, int numVariables);

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

    /// 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& getArrayFloat64(double *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.
    ByteInStream& getArrayFloat32(float *variables, int numVariables);

    // ACCESSORS

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

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

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

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

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

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

// FREE OPERATORS

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

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

// ============================================================================
//                           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 = 0;
    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
    }
    BSLS_ASSERT(0 <= length);

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