// bslx_testoutstream.h                                               -*-C++-*-
#ifndef INCLUDED_BSLX_TESTOUTSTREAM
#define INCLUDED_BSLX_TESTOUTSTREAM

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

//@PURPOSE: Enable externalization of fundamental types with identification.
//
//@CLASSES:
//  bslx::TestOutStream: byte-array-based output stream for fundamental types
//
//@SEE_ALSO: bslx_testinstream, bslx_byteoutstream
//
//@DESCRIPTION: This component implements a byte-array-based output stream
// class, `bslx::TestOutStream`, that provides platform-independent output
// methods ("externalization") on values, and arrays of values, of fundamental
// types, and on `bsl::string`.  This component also externalizes information
// to the stream that can be used by the reader of the stream to verify, for
// these types, that the type of data requested from the input stream matches
// what was written by this output stream.  This component is meant for testing
// only.
//
// This component is intended to be used in conjunction with the
// `bslx_testinstream` "unexternalization" component.  Each output method of
// `bslx::TestOutStream` 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::TestInStream` method.  In general, the user cannot rely
// on any other mechanism to read data written by `bslx::TestOutStream` 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 format (i.e., network byte
// order).
//
// 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::TestOutStream` 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::TestOutStream`, 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::TestOutStream` with an arbitrary value for its
// `versionSelector` and externalize some values:
// ```
// bslx::TestOutStream 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(24 == length);
// assert( 0 == bsl::memcmp(theChars,
//                          "\xE6\x00\x00\x00\x01\xE6\x00\x00\x00\x02\xE0"
//                                     "c\xE0\x05\xE1\x00\x00\x00\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): -26
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 1
// nextByte (int): -26
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 0
// nextByte (int): 2
// nextByte (int): -32
// nextByte (char): c
// nextByte (int): -32
// nextByte (int): 5
// nextByte (int): -31
// nextByte (char): h
// nextByte (char): e
// nextByte (char): l
// nextByte (char): l
// nextByte (char): o
// ```
// Note the negative numeric values indicate the "type" of the data that
// follows (see `bslx_typecode`).
//
// See the `bslx_testinstream` component usage example for a more practical
// example of using this test output stream.

#include <bslscm_version.h>

#include <bslx_byteoutstream.h>
#include <bslx_outstreamfunctions.h>

#include <bsls_types.h>

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

namespace BloombergLP {

namespace bslma { class Allocator; }

namespace bslx {

                         // ===================
                         // class TestOutStream
                         // ===================

/// This class implements output methods to externalize fundamental types
/// and their associated type identification data.  It stores the
/// accumulated result in network byte order.  See the `bslx` package-level
/// documentation for the definition of the BDEX `OutStream` protocol.
class TestOutStream {

    // DATA
    ByteOutStream d_imp;                  // byte out stream implementation

    bool          d_makeNextInvalidFlag;  // if 'true', next "put" operation
                                          // outputs the invalid data indicator
                                          // and resets this flag to 'false'

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

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

  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 TestOutStream(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.
    TestOutStream(int               versionSelector,
                  bsl::size_t       initialCapacity,
                  bslma::Allocator *basicAllocator = 0);

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

    // MANIPULATORS

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

    /// Make the next output operation externalize the invalid data
    /// indicator, as opposed to the actual type indicator, to this output
    /// stream; the data associated with the next output operation is still
    /// externalized.  Note that the invalid data indicator can be detected
    /// by a corresponding `TestInStream` object.
    void makeNextInvalid();

    /// If the specified `length` is less than 128, write to this stream the
    /// one-byte type indicator for a one-byte integer and the one-byte
    /// integer comprised of the least-significant one byte of the `length`;
    /// otherwise, write to this stream the one-byte type indicator for a
    /// four-byte integer and 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.  If the next output operation
    /// has been set to be marked invalid (see `makeNextInvalid`), reset
    /// this marking and emit the invalid indicator instead of the type
    /// indicator.  The behavior is undefined unless `0 <= length`.
    TestOutStream& putLength(int length);

    /// Write to this stream the one-byte type indicator for a one-byte
    /// unsigned integer and 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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.
    TestOutStream& 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 one-byte type indicator for an eight-byte
    /// integer and 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.  If the next output operation has been set to be
    /// marked invalid (see `makeNextInvalid`), reset this marking and emit
    /// the invalid indicator instead of the type indicator.
    TestOutStream& putInt64(bsls::Types::Int64 value);

    /// Write to this stream the one-byte type indicator for an eight-byte
    /// unsigned integer and 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.  If the next output operation
    /// has been set to be marked invalid (see `makeNextInvalid`), reset
    /// this marking and emit the invalid indicator instead of the type
    /// indicator.
    TestOutStream& putUint64(bsls::Types::Uint64 value);

    /// Write to this stream the one-byte type indicator for a seven-byte
    /// integer and 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.  If the next output operation has been set to be
    /// marked invalid (see `makeNextInvalid`), reset this marking and emit
    /// the invalid indicator instead of the type indicator.
    TestOutStream& putInt56(bsls::Types::Int64 value);

    /// Write to this stream the one-byte type indicator for a seven-byte
    /// unsigned integer and 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.  If the next output operation
    /// has been set to be marked invalid (see `makeNextInvalid`), reset
    /// this marking and emit the invalid indicator instead of the type
    /// indicator.
    TestOutStream& putUint56(bsls::Types::Uint64 value);

    /// Write to this stream the one-byte type indicator for a six-byte
    /// integer and 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.
    /// If the next output operation has been set to be marked invalid (see
    /// `makeNextInvalid`), reset this marking and emit the invalid
    /// indicator instead of the type indicator.
    TestOutStream& putInt48(bsls::Types::Int64 value);

    /// Write to this stream the one-byte type indicator for a six-byte
    /// unsigned integer and 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.  If the next output operation has been set
    /// to be marked invalid (see `makeNextInvalid`), reset this marking and
    /// emit the invalid indicator instead of the type indicator.
    TestOutStream& putUint48(bsls::Types::Uint64 value);

    /// Write to this stream the one-byte type indicator for a five-byte
    /// integer and 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.  If the next output operation has been set to be
    /// marked invalid (see `makeNextInvalid`), reset this marking and emit
    /// the invalid indicator instead of the type indicator.
    TestOutStream& putInt40(bsls::Types::Int64 value);

    /// Write to this stream the one-byte type indicator for a five-byte
    /// unsigned integer and 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.  If the next output operation has been
    /// set to be marked invalid (see `makeNextInvalid`), reset this marking
    /// and emit the invalid indicator instead of the type indicator.
    TestOutStream& putUint40(bsls::Types::Uint64 value);

    /// Write to this stream the one-byte type indicator for a four-byte
    /// integer and 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.  If the next output operation has been set to be
    /// marked invalid (see `makeNextInvalid`), reset this marking and emit
    /// the invalid indicator instead of the type indicator.
    TestOutStream& putInt32(int value);

    /// Write to this stream the one-byte type indicator for a four-byte
    /// unsigned integer and 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.  If the next output operation has been
    /// set to be marked invalid (see `makeNextInvalid`), reset this marking
    /// and emit the invalid indicator instead of the type indicator.
    TestOutStream& putUint32(unsigned int value);

    /// Write to this stream the one-byte type indicator for a three-byte
    /// integer and 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.  If the next output operation has been set to be
    /// marked invalid (see `makeNextInvalid`), reset this marking and emit
    /// the invalid indicator instead of the type indicator.
    TestOutStream& putInt24(int value);

    /// Write to this stream the one-byte type indicator for a three-byte
    /// unsigned integer and 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.  If the next output operation
    /// has been set to be marked invalid (see `makeNextInvalid`), reset
    /// this marking and emit the invalid indicator instead of the type
    /// indicator.
    TestOutStream& putUint24(unsigned int value);

    /// Write to this stream the one-byte type indicator for a two-byte
    /// integer and 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.
    /// If the next output operation has been set to be marked invalid (see
    /// `makeNextInvalid`), reset this marking and emit the invalid
    /// indicator instead of the type indicator.
    TestOutStream& putInt16(int value);

    /// Write to this stream the one-byte type indicator for a two-byte
    /// unsigned integer and 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.  If the next output operation has been set
    /// to be marked invalid (see `makeNextInvalid`), reset this marking and
    /// emit the invalid indicator instead of the type indicator.
    TestOutStream& putUint16(unsigned int value);

    /// Write to this stream the one-byte type indicator for a one-byte
    /// integer and 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.  If the next output operation has been set
    /// to be marked invalid (see `makeNextInvalid`), reset this marking and
    /// emit the invalid indicator instead of the type indicator.
    TestOutStream& putInt8(int value);

    /// Write to this stream the one-byte type indicator for a one-byte
    /// unsigned integer and 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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.
    TestOutStream& putUint8(unsigned int value);

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

    /// Write to this stream the one-byte type indicator for an eight-byte
    /// double-precision floating-point number and 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.
    /// If the next output operation has been set to be marked invalid (see
    /// `makeNextInvalid`), reset this marking and emit the invalid
    /// indicator instead of the type indicator.  Note that for
    /// non-conforming platforms, this operation may be lossy.
    TestOutStream& putFloat64(double value);

    /// Write to this stream the one-byte type indicator for a four-byte
    /// single-precision floating-point number and 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.
    /// If the next output operation has been set to be marked invalid (see
    /// `makeNextInvalid`), reset this marking and emit the invalid
    /// indicator instead of the type indicator.  Note that for
    /// non-conforming platforms, this operation may be lossy.
    TestOutStream& putFloat32(float value);

                      // *** string values ***

    /// Write to this stream the one-byte type indicator for a length (see
    /// `putLength`), the length of the specified `value` (see `putLength`),
    /// the one-byte type indicator for an array of one-byte unsigned
    /// integers, 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.  If
    /// the next output operation has been set to be marked invalid (see
    /// `makeNextInvalid`), reset this marking and emit the invalid
    /// indicator instead of the type indicator.
    TestOutStream& putString(const bsl::string& value);

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

    /// Write to this stream the one-byte type indicator for an eight-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// eight-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant eight bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt64(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the one-byte type indicator for an eight-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// eight-byte, two's complement unsigned integers (in network byte
    /// order) comprised of the least-significant eight bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint64(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the one-byte type indicator for a seven-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// seven-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant seven bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt56(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the one-byte type indicator for a seven-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// seven-byte, two's complement unsigned integers (in network byte
    /// order) comprised of the least-significant seven bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint56(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the one-byte type indicator for a six-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// six-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant six bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt48(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the one-byte type indicator for a six-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// six-byte, two's complement unsigned integers (in network byte order)
    /// comprised of the least-significant six bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint48(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the one-byte type indicator for a five-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// five-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant five bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt40(const bsls::Types::Int64 *values,
                                 int                       numValues);

    /// Write to this stream the one-byte type indicator for a five-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// five-byte, two's complement unsigned integers (in network byte
    /// order) comprised of the least-significant five bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint40(const bsls::Types::Uint64 *values,
                                  int                        numValues);

    /// Write to this stream the one-byte type indicator for a four-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// four-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant four bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt32(const int *values, int numValues);

    /// Write to this stream the one-byte type indicator for a four-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// four-byte, two's complement unsigned integers (in network byte
    /// order) comprised of the least-significant four bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint32(const unsigned int *values, int numValues);

    /// Write to this stream the one-byte type indicator for a three-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// three-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant three bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt24(const int *values, int numValues);

    /// Write to this stream the one-byte type indicator for a three-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// three-byte, two's complement unsigned integers (in network byte
    /// order) comprised of the least-significant three bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint24(const unsigned int *values, int numValues);

    /// Write to this stream the one-byte type indicator for a two-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// two-byte, two's complement integers (in network byte order)
    /// comprised of the least-significant two bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayInt16(const short *values, int numValues);

    /// Write to this stream the one-byte type indicator for a two-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// two-byte, two's complement unsigned integers (in network byte order)
    /// comprised of the least-significant two bytes of each of the
    /// `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.  If the next output
    /// operation has been set to be marked invalid (see `makeNextInvalid`),
    /// reset this marking and emit the invalid indicator instead of the
    /// type indicator.  The behavior is undefined unless `0 <= numValues`
    /// and `values` has sufficient contents.
    TestOutStream& putArrayUint16(const unsigned short *values, int numValues);

    /// Write to this stream the one-byte type indicator for a one-byte
    /// integer, the four-byte, two's complement integer (in network byte
    /// order) comprised of the least-significant four bytes of the
    /// specified `numValues` (in host byte order), and the consecutive
    /// one-byte, two's complement integers comprised of the
    /// least-significant one byte of each of the `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.  If the next output operation has been set to be marked
    /// invalid (see `makeNextInvalid`), reset this marking and emit the
    /// invalid indicator instead of the type indicator.  The behavior is
    /// undefined unless `0 <= numValues` and `values` has sufficient
    /// contents.
    TestOutStream& putArrayInt8(const char        *values, int numValues);
    TestOutStream& putArrayInt8(const signed char *values, int numValues);

    /// Write to this stream the one-byte type indicator for a one-byte
    /// unsigned integer, the four-byte, two's complement integer (in
    /// network byte order) comprised of the least-significant four bytes of
    /// the specified `numValues` (in host byte order), and the consecutive
    /// one-byte, two's complement unsigned integers comprised of the
    /// least-significant one byte of each of the `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.  If the next output operation has been set to be marked
    /// invalid (see `makeNextInvalid`), reset this marking and emit the
    /// invalid indicator instead of the type indicator.  The behavior is
    /// undefined unless `0 <= numValues` and `values` has sufficient
    /// contents.
    TestOutStream& putArrayUint8(const char          *values, int numValues);
    TestOutStream& putArrayUint8(const unsigned char *values, int numValues);

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

    /// Write to this stream the one-byte type indicator for an eight-byte
    /// double-precision floating-point number, the four-byte, two's
    /// complement integer (in network byte order) comprised of the
    /// least-significant four bytes of the specified `numValues` (in host
    /// byte order), and 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 `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.  If the next output operation has been set
    /// to be marked invalid (see `makeNextInvalid`), reset this marking and
    /// emit the invalid indicator instead of the type indicator.  The
    /// behavior is undefined unless `0 <= numValues` and `values` has
    /// sufficient contents.  Note that for non-conforming platforms, this
    /// operation may be lossy.
    TestOutStream& putArrayFloat64(const double *values, int numValues);

    /// Write to this stream the one-byte type indicator for a four-byte
    /// single-precision floating-point number, the four-byte, two's
    /// complement integer (in network byte order) comprised of the
    /// least-significant four bytes of the specified `numValues` (in host
    /// byte order), and 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 `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.  If the next output operation has been set
    /// to be marked invalid (see `makeNextInvalid`), reset this marking and
    /// emit the invalid indicator instead of the type indicator.  The
    /// behavior is undefined unless `0 <= numValues` and `values` has
    /// sufficient contents.  Note that for non-conforming platforms, this
    /// operation may be lossy.
    TestOutStream& 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 array is not destroyed or modified (i.e., the current capacity
    /// is not exceeded).  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 TestOutStream& 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>
TestOutStream& operator<<(TestOutStream& stream, const TYPE& value);

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

                         // -------------------
                         // class TestOutStream
                         // -------------------

// MANIPULATORS
inline
void TestOutStream::invalidate()
{
    d_imp.invalidate();
}

inline
void TestOutStream::makeNextInvalid()
{
    d_makeNextInvalidFlag = true;
}

inline
void TestOutStream::reserveCapacity(bsl::size_t newCapacity)
{
    d_imp.reserveCapacity(newCapacity);
}

inline
void TestOutStream::reset()
{
    d_imp.reset();
}

                      // *** string values ***

inline
TestOutStream& TestOutStream::putString(const bsl::string& value)
{
    putLength(static_cast<int>(value.length()));
    return putArrayUint8(value.data(), static_cast<int>(value.length()));
}

// ACCESSORS
inline
TestOutStream::operator const void *() const
{
    return d_imp;
}

inline
int TestOutStream::bdexVersionSelector() const
{
    return d_imp.bdexVersionSelector();
}

inline
const char *TestOutStream::data() const
{
    return d_imp.data();
}

inline
bool TestOutStream::isValid() const
{
    return d_imp.isValid();
}

inline
bsl::size_t TestOutStream::length() const
{
    return d_imp.length();
}

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

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

// TRAITS
namespace BloombergLP {
namespace bslma {

template <>
struct UsesBslmaAllocator<bslx::TestOutStream> : 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 ----------------------------------
