// bslx_byteoutstream.h                                               -*-C++-*-
#ifndef INCLUDED_BSLX_BYTEOUTSTREAM
#define INCLUDED_BSLX_BYTEOUTSTREAM

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

//@PURPOSE: Provide a stream class for externalization of fundamental types.
//
//@CLASSES:
//  bslx::ByteOutStream: byte-array-based output stream for fundamental types
//
//@SEE_ALSO: bslx_byteinstream
//
//@DESCRIPTION: This component implements a byte-array-based output stream
// class, `bslx::ByteOutStream`, that provides platform-independent output
// methods ("externalization") on values, and arrays of values, of fundamental
// types, and on `bsl::string`.
//
// This component is intended to be used in conjunction with the
// `bslx_byteinstream` "unexternalization" component.  Each output method of
// `bslx::ByteOutStream` writes a value or a homogeneous array of values to an
// internally managed buffer.  The values are formatted to be readable by the
// corresponding `bslx::ByteInStream` method.  In general, the user cannot rely
// on any other mechanism to read data written by `bslx::ByteOutStream` unless
// that mechanism explicitly states its ability to do so.
//
// The supported types and required content are listed in the `bslx`
// package-level documentation under "Supported Types".
//
// Note that the values are stored in big-endian (i.e., network byte order)
// format.
//
// Note that output streams can be *invalidated* explicitly and queried for
// *validity*.  Writing to an initially invalid stream has no effect.  Whenever
// an output operation fails, the stream should be invalidated explicitly.
//
///Versioning
///----------
// BDEX provides two concepts that support versioning the BDEX serialization
// format of a type: `version` and `versionSelector`.  A `version` is a 1-based
// integer indicating one of the supported formats (e.g., format 1, format 2,
// etc.).  A `versionSelector` is a value that is mapped to a `version` for a
// type by the type's implementation of `maxSupportedBdexVersion`.
//
// Selecting a value for a `versionSelector` is required at two different
// points: (1) when implementing a new `version` format within the
// `bdexStreamIn` and `bdexStreamOut` methods of a type, and (2) when
// implementing code that constructs a BDEX `OutStream`.  In both cases, the
// value should be a *compile*-time-selected value.
//
// When a new `version` format is implemented within the `bdexStreamIn` and
// `bdexStreamOut` methods of a type, a new mapping in
// `maxSupportedBdexVersion` should be created to expose this new `version`
// with a `versionSelector`.  A simple - and the recommended - approach is to
// use a value having the pattern "YYYYMMDD", where "YYYYMMDD" corresponds to
// the "go-live" date of the corresponding `version` format.
//
// When constructing an `OutStream`, a simple approach is to use the current
// date as a *compile*-time constant value.  In combination with the
// recommended selection of `versionSelector` values for
// `maxSupportedBdexVersion`, this will result in consistent and predictable
// behavior while externalizing types.  Note that this recommendation is chosen
// for its simplicity: to ensure the largest possible audience for an
// externalized representation, clients can select the minimum date value that
// will result in the desired version of all types externalized with
// `operator<<` being selected.
//
// See the `bslx` package-level documentation for more detailed information
// about versioning.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Externalization
/// - - - - - - - - - - - - - - - -
// A `bslx::ByteOutStream` can be used to externalize values in a
// platform-neutral way.  Writing out fundamental C++ types and `bsl::string`
// requires no additional work on the part of the client; the client can simply
// use the stream directly.  The following code serializes a few representative
// values using a `bslx::ByteOutStream`, compares the contents of this stream
// to the expected value, and then writes the contents of this stream's buffer
// to `stdout`.
//
// First, we create a `bslx::ByteOutStream` with an arbitrary value for its
// `versionSelector` and externalize some values:
// ```
// bslx::ByteOutStream outStream(20131127);
// outStream.putInt32(1);
// outStream.putInt32(2);
// outStream.putInt8('c');
// outStream.putString(bsl::string("hello"));
// ```
// Then, we compare the contents of the stream to the expected value:
// ```
// const char  *theChars = outStream.data();
// bsl::size_t  length   = outStream.length();
// assert(15 == length);
// assert( 0 == bsl::memcmp(theChars,
//                          "\x00\x00\x00\x01\x00\x00\x00\x02""c\x05""hello",
//                          length));
// ```
// Finally, we print the stream's contents to `bsl::cout`.
// ```
// for (bsl::size_t i = 0; i < length; ++i) {
//     if (bsl::isalnum(static_cast<unsigned char>(theChars[i]))) {
//         bsl::cout << "nextByte (char): " << theChars[i] << bsl::endl;
//     }
//     else {
//         bsl::cout << "nextByte (int): "
//                   << static_cast<int>(theChars[i])
//                   << bsl::endl;
//     }
// }
// ```
// Executing the above code results in the following output:
// ```
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 1
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 2
// nextByte (char): c
// nextByte (int): 5
// nextByte (char): h
// nextByte (char): e
// nextByte (char): l
// nextByte (char): l
// nextByte (char): o
// ```
// See the `bslx_byteinstream` component usage example for a more practical
// example of using `bslx` streams.

#include <bslscm_version.h>

#include <bslx_marshallingutil.h>
#include <bslx_outstreamfunctions.h>

#include <bslma_allocator.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 ByteOutStream
                         // ===================

/// This class provides output methods to externalize values, and C-style
/// arrays of values, of the fundamental integral and floating-point types,
/// as well as `bsl::string` values.  In particular, each `put` method of
/// this class is guaranteed to write stream data that can be read by the
/// corresponding `get` method of `bslx::ByteInStream`.  See the `bslx`
/// package-level documentation for the definition of the BDEX `OutStream`
/// protocol.
class ByteOutStream {

    // DATA
    bsl::vector<char> d_buffer;     // byte buffer to write to

    int               d_versionSelector;
                                    // 'versionSelector' to use with
                                    // 'operator<<' as per the 'bslx'
                                    // package-level documentation

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

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

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

  private:
    // PRIVATE MANIPULATORS

    /// Put this output stream into a valid state.  This function has no
    /// effect if this stream is already valid.
    void validate();

  public:
    // CREATORS

    /// Create an empty output byte stream that will use the specified
    /// (*compile*-time-defined) `versionSelector` as needed (see
    /// {Versioning}).  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.  Note that the `versionSelector` is expected to
    /// be formatted as "YYYYMMDD", a date representation.
    explicit ByteOutStream(int               versionSelector,
                           bslma::Allocator *basicAllocator = 0);

    /// Create an empty output byte stream having an initial buffer capacity
    /// of at least the specified `initialCapacity` (in bytes) and that will
    /// use the specified (*compile*-time-defined) `versionSelector` as
    /// needed (see {Versioning}).  Optionally specify a `basicAllocator`
    /// used to supply memory.  If `basicAllocator` is 0, the currently
    /// installed default allocator is used.  Note that the
    /// `versionSelector` is expected to be formatted as "YYYYMMDD", a date
    /// representation.
    ByteOutStream(int               versionSelector,
                  bsl::size_t       initialCapacity,
                  bslma::Allocator *basicAllocator = 0);

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

    // MANIPULATORS

    /// Put this output stream in an invalid state.  This function has no
    /// effect if this stream is already invalid.
    void invalidate();

    /// If the specified `length` is less than 128, write to this stream the
    /// one-byte integer comprised of the least-significant one byte of the
    /// `length`; otherwise, write to this stream the four-byte, two's
    /// complement integer (in network byte order) comprised of the
    /// least-significant four bytes of the `length` (in host byte order)
    /// with the most-significant bit set.  Return a reference to this
    /// stream.  If this stream is initially invalid, this operation has no
    /// effect.  The behavior is undefined unless `0 <= length`.
    ByteOutStream& putLength(int length);

    /// Write to this stream the one-byte, two's complement unsigned integer
    /// comprised of the least-significant one byte of the specified
    /// `version`, and return a reference to this stream.  If this stream is
    /// initially invalid, this operation has no effect.
    ByteOutStream& putVersion(int version);

    /// Set the internal buffer size of this stream to be at least the
    /// specified `newCapacity` (in bytes).
    void reserveCapacity(bsl::size_t newCapacity);

    /// Remove all content in this stream and validate this stream if it is
    /// currently invalid.
    void reset();

                      // *** scalar integer values ***

    /// Write to this stream the eight-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant eight bytes
    /// of the specified `value` (in host byte order), and return a
    /// reference to this stream.  If this stream is initially invalid, this
    /// operation has no effect.
    ByteOutStream& putInt64(bsls::Types::Int64 value);

    /// Write to this stream the eight-byte, two's complement unsigned
    /// integer (in network byte order) comprised of the least-significant
    /// eight bytes of the specified `value` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.
    ByteOutStream& putUint64(bsls::Types::Uint64 value);

    /// Write to this stream the seven-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant seven bytes
    /// of the specified `value` (in host byte order), and return a
    /// reference to this stream.  If this stream is initially invalid, this
    /// operation has no effect.
    ByteOutStream& putInt56(bsls::Types::Int64 value);

    /// Write to this stream the seven-byte, two's complement unsigned
    /// integer (in network byte order) comprised of the least-significant
    /// seven bytes of the specified `value` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.
    ByteOutStream& putUint56(bsls::Types::Uint64 value);

    /// Write to this stream the six-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant six bytes of
    /// the specified `value` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.
    ByteOutStream& putInt48(bsls::Types::Int64 value);

    /// Write to this stream the six-byte, two's complement unsigned integer
    /// (in network byte order) comprised of the least-significant six bytes
    /// of the specified `value` (in host byte order), and return a
    /// reference to this stream.  If this stream is initially invalid, this
    /// operation has no effect.
    ByteOutStream& putUint48(bsls::Types::Uint64 value);

    /// Write to this stream the five-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant five bytes of
    /// the specified `value` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.
    ByteOutStream& putInt40(bsls::Types::Int64 value);

    /// Write to this stream the five-byte, two's complement unsigned
    /// integer (in network byte order) comprised of the least-significant
    /// five bytes of the specified `value` (in host byte order), and return
    /// a reference to this stream.  If this stream is initially invalid,
    /// this operation has no effect.
    ByteOutStream& putUint40(bsls::Types::Uint64 value);

    /// Write to this stream the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `value` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.
    ByteOutStream& putInt32(int value);

    /// Write to this stream the four-byte, two's complement unsigned
    /// integer (in network byte order) comprised of the least-significant
    /// four bytes of the specified `value` (in host byte order), and return
    /// a reference to this stream.  If this stream is initially invalid,
    /// this operation has no effect.
    ByteOutStream& putUint32(unsigned int value);

    /// Write to this stream the three-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant three bytes
    /// of the specified `value` (in host byte order), and return a
    /// reference to this stream.  If this stream is initially invalid, this
    /// operation has no effect.
    ByteOutStream& putInt24(int value);

    /// Write to this stream the three-byte, two's complement unsigned
    /// integer (in network byte order) comprised of the least-significant
    /// three bytes of the specified `value` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.
    ByteOutStream& putUint24(unsigned int value);

    /// Write to this stream the two-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant two bytes of
    /// the specified `value` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.
    ByteOutStream& putInt16(int value);

    /// Write to this stream the two-byte, two's complement unsigned integer
    /// (in network byte order) comprised of the least-significant two bytes
    /// of the specified `value` (in host byte order), and return a
    /// reference to this stream.  If this stream is initially invalid, this
    /// operation has no effect.
    ByteOutStream& putUint16(unsigned int value);

    /// Write to this stream the one-byte, two's complement integer
    /// comprised of the least-significant one byte of the specified
    /// `value`, and return a reference to this stream.  If this stream is
    /// initially invalid, this operation has no effect.
    ByteOutStream& putInt8(int value);

    /// Write to this stream the one-byte, two's complement unsigned integer
    /// comprised of the least-significant one byte of the specified
    /// `value`, and return a reference to this stream.  If this stream is
    /// initially invalid, this operation has no effect.
    ByteOutStream& putUint8(unsigned int value);

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

    /// Write to this stream the eight-byte IEEE double-precision
    /// floating-point number (in network byte order) comprised of the
    /// most-significant eight bytes of the specified `value` (in host byte
    /// order), and return a reference to this stream.  If this stream is
    /// initially invalid, this operation has no effect.  Note that for
    /// non-conforming platforms, this operation may be lossy.
    ByteOutStream& putFloat64(double value);

    /// Write to this stream the four-byte IEEE single-precision
    /// floating-point number (in network byte order) comprised of the
    /// most-significant four bytes of the specified `value` (in host byte
    /// order), and return a reference to this stream.  If this stream is
    /// initially invalid, this operation has no effect.  Note that for
    /// non-conforming platforms, this operation may be lossy.
    ByteOutStream& putFloat32(float value);

                      // *** string values ***

    /// Write to this stream the length of the specified `value` (see
    /// `putLength`) and an array of one-byte, two's complement unsigned
    /// integers comprised of the least-significant one byte of each
    /// character in the `value`, and return a reference to this stream.  If
    /// this stream is initially invalid, this operation has no effect.
    ByteOutStream& putString(const bsl::string& value);

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

    /// Write to this stream the consecutive eight-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// eight bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt64(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the consecutive eight-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant eight bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint64(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the consecutive seven-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// seven bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt56(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the consecutive seven-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant seven bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint56(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the consecutive six-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// six bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt48(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the consecutive six-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant six bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint48(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the consecutive five-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// five bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt40(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the consecutive five-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant five bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint40(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the consecutive four-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// four bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt32(const int *values, int numValues);

    /// Write to this stream the consecutive four-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant four bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint32(const unsigned int *values, int numValues);

    /// Write to this stream the consecutive three-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// three bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt24(const int *values, int numValues);

    /// Write to this stream the consecutive three-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant three bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint24(const unsigned int *values, int numValues);

    /// Write to this stream the consecutive two-byte, two's complement
    /// integers (in network byte order) comprised of the least-significant
    /// two bytes of each of the specified `numValues` leading entries in
    /// the specified `values` (in host byte order), and return a reference
    /// to this stream.  If this stream is initially invalid, this operation
    /// has no effect.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    ByteOutStream& putArrayInt16(const short *values, int numValues);

    /// Write to this stream the consecutive two-byte, two's complement
    /// unsigned integers (in network byte order) comprised of the
    /// least-significant two bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayUint16(const unsigned short *values, int numValues);

    /// Write to this stream the consecutive one-byte, two's complement
    /// integers comprised of the least-significant one byte of each of the
    /// specified `numValues` leading entries in the specified `values`, and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.
    ByteOutStream& putArrayInt8(const char        *values, int numValues);
    ByteOutStream& putArrayInt8(const signed char *values, int numValues);

    /// Write to this stream the consecutive one-byte, two's complement
    /// unsigned integers comprised of the least-significant one byte of
    /// each of the specified `numValues` leading entries in the specified
    /// `values`, and return a reference to this stream.  If this stream is
    /// initially invalid, this operation has no effect.  The behavior is
    /// undefined unless `0 <= numValues` and `values` has sufficient
    /// contents.
    ByteOutStream& putArrayUint8(const char          *values, int numValues);
    ByteOutStream& putArrayUint8(const unsigned char *values, int numValues);

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

    /// Write to this stream the consecutive eight-byte IEEE
    /// double-precision floating-point numbers (in network byte order)
    /// comprised of the most-significant eight bytes of each of the
    /// specified `numValues` leading entries in the specified `values` (in
    /// host byte order), and return a reference to this stream.  If this
    /// stream is initially invalid, this operation has no effect.  The
    /// behavior is undefined unless `0 <= numValues` and `values` has
    /// sufficient contents.  Note that for non-conforming platforms, this
    /// operation may be lossy.
    ByteOutStream& putArrayFloat64(const double *values, int numValues);

    /// Write to this stream the consecutive four-byte IEEE single-precision
    /// floating-point numbers (in network byte order) comprised of the
    /// most-significant four bytes of each of the specified `numValues`
    /// leading entries in the specified `values` (in host byte order), and
    /// return a reference to this stream.  If this stream is initially
    /// invalid, this operation has no effect.  The behavior is undefined
    /// unless `0 <= numValues` and `values` has sufficient contents.  Note
    /// that for non-conforming platforms, this operation may be lossy.
    ByteOutStream& putArrayFloat32(const float *values, int numValues);

    // ACCESSORS

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

    /// Return the `versionSelector` to be used with `operator<<` for BDEX
    /// streaming as per the `bslx` package-level documentation.
    int bdexVersionSelector() const;

    /// Return the address of the contiguous, non-modifiable internal memory
    /// buffer of this stream.  The address will remain valid as long as
    /// this stream is not destroyed or modified.  The behavior of accessing
    /// elements outside the range `[ data() .. data() + (length() - 1) ]`
    /// is undefined.
    const char *data() const;

    /// Return `true` if this stream is valid, and `false` otherwise.  An
    /// invalid stream is a stream for which an output operation was
    /// detected to have failed or `invalidate` was called.
    bool isValid() const;

    /// Return the number of bytes in this stream.
    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 ByteOutStream& object);

/// Write the specified `value` to the specified output `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>
ByteOutStream& operator<<(ByteOutStream& stream, const TYPE& value);

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

                         // -------------------
                         // class ByteOutStream
                         // -------------------

// PRIVATE MANIPULATORS
inline
void ByteOutStream::validate()
{
    d_validFlag = true;
}

// CREATORS
inline
ByteOutStream::ByteOutStream(int               versionSelector,
                             bslma::Allocator *basicAllocator)
: d_buffer(basicAllocator)
, d_versionSelector(versionSelector)
, d_validFlag(true)
{
}

inline
ByteOutStream::ByteOutStream(int               versionSelector,
                             bsl::size_t       initialCapacity,
                             bslma::Allocator *basicAllocator)
: d_buffer(basicAllocator)
, d_versionSelector(versionSelector)
, d_validFlag(true)
{
    d_buffer.reserve(initialCapacity);
}

inline
ByteOutStream::~ByteOutStream()
{
}

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

inline
ByteOutStream& ByteOutStream::putLength(int length)
{
    BSLS_ASSERT_SAFE(0 <= length);

    if (length > 127) {
        putInt32(length | (1 << 31));
    } else {
        putInt8(length);
    }
    return *this;
}

inline
ByteOutStream& ByteOutStream::putVersion(int version)
{
    return putUint8(version);
}

inline
void ByteOutStream::reserveCapacity(bsl::size_t newCapacity)
{
    d_buffer.reserve(newCapacity);
}

inline
void ByteOutStream::reset()
{
    d_buffer.clear();
    validate();
}

                      // *** scalar integer values ***

inline
ByteOutStream& ByteOutStream::putInt64(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT64);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt64(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint64(bsls::Types::Uint64 value)
{
    return putInt64(static_cast<bsls::Types::Int64>(value));
}

inline
ByteOutStream& ByteOutStream::putInt56(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT56);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt56(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint56(bsls::Types::Uint64 value)
{
    return putInt56(static_cast<bsls::Types::Int64>(value));
}

inline
ByteOutStream& ByteOutStream::putInt48(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT48);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt48(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint48(bsls::Types::Uint64 value)
{
    return putInt48(static_cast<bsls::Types::Int64>(value));
}

inline
ByteOutStream& ByteOutStream::putInt40(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT40);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt40(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint40(bsls::Types::Uint64 value)
{
    return putInt40(static_cast<bsls::Types::Int64>(value));
}

inline
ByteOutStream& ByteOutStream::putInt32(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT32);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt32(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint32(unsigned int value)
{
    return putInt32(static_cast<int>(value));
}

inline
ByteOutStream& ByteOutStream::putInt24(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT24);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt24(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint24(unsigned int value)
{
    return putInt24(static_cast<int>(value));
}

inline
ByteOutStream& ByteOutStream::putInt16(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT16);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt16(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint16(unsigned int value)
{
    return putInt16(static_cast<int>(value));
}

inline
ByteOutStream& ByteOutStream::putInt8(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_INT8);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putInt8(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putUint8(unsigned int value)
{
    return putInt8(static_cast<int>(value));
}

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

inline
ByteOutStream& ByteOutStream::putFloat64(double value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_FLOAT64);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putFloat64(d_buffer.data() + n, value);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putFloat32(float value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + MarshallingUtil::k_SIZEOF_FLOAT32);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putFloat32(d_buffer.data() + n, value);

    return *this;
}

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

inline
ByteOutStream& ByteOutStream::putArrayInt64(
                                           const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT64);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt64(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint64(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT64);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt64(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt56(
                                           const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT56);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt56(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint56(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT56);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt56(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt48(
                                           const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT48);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt48(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint48(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT48);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt48(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt40(
                                           const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT40);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt40(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint40(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT40);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt40(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt32(const int *values, int numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT32);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt32(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint32(const unsigned int *values,
                                             int                 numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT32);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt32(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt24(const int *values, int numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT24);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt24(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint24(const unsigned int *values,
                                             int                 numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT24);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt24(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt16(const short *values, int numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT16);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt16(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint16(const unsigned short *values,
                                             int                   numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT16);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt16(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt8(const char *values, int numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT8);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt8(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayInt8(const signed char *values,
                                           int                numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT8);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt8(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint8(const char *values, int numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT8);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt8(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayUint8(const unsigned char *values,
                                            int                  numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_INT8);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayInt8(d_buffer.data() + n, values, numValues);

    return *this;
}

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

inline
ByteOutStream& ByteOutStream::putArrayFloat64(const double *values,
                                              int           numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_FLOAT64);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayFloat64(d_buffer.data() + n, values, numValues);

    return *this;
}

inline
ByteOutStream& ByteOutStream::putArrayFloat32(const float *values,
                                              int          numValues)
{
    BSLS_ASSERT_SAFE(values);
    BSLS_ASSERT_SAFE(0 <= numValues);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid() || 0 == numValues)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    // Resize the buffer to have sufficient capacity with care to ensure this
    // stream is invalidated if an exception is thrown.

    const bsl::size_t n = d_buffer.size();
    invalidate();
    d_buffer.resize(n + numValues * MarshallingUtil::k_SIZEOF_FLOAT32);
    validate();

    // Write to the buffer the specified 'value'.

    MarshallingUtil::putArrayFloat32(d_buffer.data() + n, values, numValues);

    return *this;
}

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

inline
int ByteOutStream::bdexVersionSelector() const
{
    return d_versionSelector;
}

inline
const char *ByteOutStream::data() const
{
    return d_buffer.begin();
}

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

inline
bsl::size_t ByteOutStream::length() const
{
    return d_buffer.size();
}

// FREE OPERATORS
template <class TYPE>
inline
ByteOutStream& operator<<(ByteOutStream& stream, const TYPE& value)
{
    return OutStreamFunctions::bdexStreamOut(stream, value);
}

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

// TRAITS
namespace BloombergLP {
namespace bslma {

template <>
struct UsesBslmaAllocator<bslx::ByteOutStream> : bsl::true_type {};

}  // close namespace bslma
}  // 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 ----------------------------------
