// bslx_genericoutstream.h                                            -*-C++-*-
#ifndef INCLUDED_BSLX_GENERICOUTSTREAM
#define INCLUDED_BSLX_GENERICOUTSTREAM

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

//@PURPOSE: Externalization of fundamental types to a parameterized stream.
//
//@CLASSES:
//  bslx::GenericOutStream: parameterized output stream for fundamentals
//
//@SEE_ALSO: bslx_streambufoutstream, bslx_genericinstream
//
//@DESCRIPTION: This component implements a parameterized output stream
// class, `bslx::GenericOutStream`, 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_genericinstream` "unexternalization" component.  Each output method of
// `bslx::GenericOutStream` writes either a value or a homogeneous array of
// values of a fundamental type, in a format that is readable by the
// corresponding `bslx::GenericInStream` method.  In general, the user cannot
// rely on any other mechanism to read data written by `bslx::GenericOutStream`
// 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.
//
///Generic Byte-Format Generator
///-----------------------------
// The class `bslx::GenericOutStream` is parameterized by a buffered stream
// class, `STREAMBUF`, which, given the declarations:
// ```
// char        c;
// int         len;
// const char *s;
// STREAMBUF  *sb;
// ```
// must make the following expressions syntactically valid, with the assert
// statements highlighting the expected return values:
// ```
// STREAMBUF::traits_type::int_type eof = STREAMBUF::traits_type::eof();
// assert(eof != sb->sputc(c));
// assert(len == sb->sputn(s, len));
// assert(  0 == sb->pubsync());
// ```
// Suitable choices for `STREAMBUF` include any class that implements the
// `bsl::basic_streambuf` protocol.
//
// The class `bslx::StreambufOutStream` is a `typedef` of
// `bslx::GenericOutStream<bsl::streambuf>`.
//
///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.  The first example
// depicts usage with a `bsl::stringbuf`.  The second example replaces the
// `bsl::stringbuf` with a user-defined `STREAMBUF`.
//
///Example 1: Basic Externalization
/// - - - - - - - - - - - - - - - -
// A `bslx::GenericOutStream` 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::GenericOutStream`, 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::GenericOutStream`, with an arbitrary value for its
// `versionSelector`, and externalize some values:
// ```
// bsl::stringbuf                         buffer1;
// bslx::GenericOutStream<bsl::stringbuf> outStream1(&buffer1, 20131127);
// outStream1.putInt32(1);
// outStream1.putInt32(2);
// outStream1.putInt8('c');
// outStream1.putString(bsl::string("hello"));
// ```
// Then, we compare the contents of the buffer to the expected value:
// ```
// bsl::string  theChars = buffer1.str();
// assert(15 == theChars.size());
// assert( 0 == bsl::memcmp(theChars.data(),
//                          "\x00\x00\x00\x01\x00\x00\x00\x02""c\x05""hello",
//                          15));
// ```
// Finally, we print the buffer's contents to `bsl::cout`.
// ```
// for (bsl::size_t i = 0; i < theChars.size(); ++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_genericinstream` component usage example for a more practical
// example of using `bslx` streams.
//
///Example 2: Sample `STREAMBUF` Implementation
/// - - - - - - - - - - - - - - - - - - - - - -
// For this example, we will implement `MyOutStreamBuf`, a minimal `STREAMBUF`
// to be used with `bslx::GenericOutStream`.  The implementation will consist
// of only what is required of the type and two accessors to verify correct
// functionality (`data` and `length`).
//
// First, we implement `MyOutStreamBuf` (which, for brevity, simply uses the
// default allocator):
// ```
// class MyOutStreamBuf {
//     // This class implements a very basic stream buffer suitable for use in
//     // 'bslx::GenericOutStream'.
//
//     // DATA
//     bsl::string d_buffer;  // output buffer
//
//   private:
//     // NOT IMPLEMENTED
//     MyOutStreamBuf(const MyOutStreamBuf&);
//     MyOutStreamBuf& operator=(const MyOutStreamBuf&);
//
//   public:
//     // TYPES
//     struct traits_type {
//         static int eof() { return -1; }
//     };
//
//     // CREATORS
//     MyOutStreamBuf();
//         // Create an empty stream buffer.
//
//     ~MyOutStreamBuf();
//         // Destroy this stream buffer.
//
//     // MANIPULATORS
//     int pubsync();
//         // Return 0.
//
//     int sputc(char c);
//         // Write the specified character 'c' to this buffer.  Return 'c' on
//         // success, and 'traits_type::eof()' otherwise.
//
//     bsl::streamsize sputn(const char *s, bsl::streamsize length);
//         // Write the specified 'length' characters at the specified address
//         // 's' to this buffer, and return the number of characters written.
//
//     // ACCESSORS
//     const char *data() const;
//         // Return the address of the non-modifiable character buffer held
//         // by this stream buffer.
//
//     bsl::streamsize size() const;
//         // Return the number of characters from the beginning of the buffer
//         // to the current write position.
// };
//
// // ========================================================================
// //                  INLINE FUNCTION DEFINITIONS
// // ========================================================================
//
// // CREATORS
// MyOutStreamBuf::MyOutStreamBuf()
// : d_buffer()
// {
// }
//
// MyOutStreamBuf::~MyOutStreamBuf()
// {
// }
//
// // MANIPULATORS
// int MyOutStreamBuf::pubsync()
// {
//     // In this implementation, there is nothing to be done except return
//     // success.
//
//     return 0;
// }
//
// int MyOutStreamBuf::sputc(char c)
// {
//     d_buffer += c;
//     return static_cast<int>(c);
// }
//
// bsl::streamsize MyOutStreamBuf::sputn(const char      *s,
//                                       bsl::streamsize  length)
// {
//     d_buffer.append(s, length);
//     return length;
// }
//
// // ACCESSORS
// const char *MyOutStreamBuf::data() const
// {
//     return d_buffer.data();
// }
//
// bsl::streamsize MyOutStreamBuf::size() const
// {
//     return d_buffer.size();
// }
// ```
// Then, we create `buffer2`, an instance of `MyOutStreamBuf`, and a
// `bslx::GenericOutStream` using `buffer2`, with an arbitrary value for its
// `versionSelector`, and externalize some values:
// ```
// MyOutStreamBuf                         buffer2;
// bslx::GenericOutStream<MyOutStreamBuf> outStream2(&buffer2, 20131127);
// outStream2.putInt32(1);
// outStream2.putInt32(2);
// outStream2.putInt8('c');
// outStream2.putString(bsl::string("hello"));
// ```
// Finally, we compare the contents of the buffer to the expected value:
// ```
// assert(15 == buffer2.size());
// assert( 0 == bsl::memcmp(buffer2.data(),
//                          "\x00\x00\x00\x01\x00\x00\x00\x02""c\x05""hello",
//                          15));
// ```

#include <bslscm_version.h>

#include <bslx_outstreamfunctions.h>

#include <bsls_assert.h>
#include <bsls_performancehint.h>
#include <bsls_platform.h>
#include <bsls_types.h>

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

namespace BloombergLP {
namespace bslx {

                        // ======================
                        // class GenericOutStream
                        // ======================

/// 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::GenericInStream`.  See the `bslx`
/// package-level documentation for the definition of the BDEX `OutStream`
/// protocol.
template <class STREAMBUF>
class GenericOutStream {

    // PRIVATE TYPES
    enum {
        // Enumerate the platform-independent sizes (in bytes) of data types in
        // wire format.  Note that the wire format size may differ from the
        // size in memory.

        k_SIZEOF_INT64   = 8,
        k_SIZEOF_INT56   = 7,
        k_SIZEOF_INT48   = 6,
        k_SIZEOF_INT40   = 5,
        k_SIZEOF_INT32   = 4,
        k_SIZEOF_INT24   = 3,
        k_SIZEOF_INT16   = 2,
        k_SIZEOF_INT8    = 1,
        k_SIZEOF_FLOAT64 = 8,
        k_SIZEOF_FLOAT32 = 4
    };

    // DATA
    STREAMBUF *d_streamBuf;        // held stream 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

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

  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 output byte stream that writes its output to the specified
    /// `streamBuf` and uses the specified (*compile*-time-defined)
    /// `versionSelector` as needed (see {Versioning}).  Note that the
    /// `versionSelector` is expected to be formatted as "YYYYMMDD", a date
    /// representation.
    GenericOutStream(STREAMBUF *streamBuf, int versionSelector);

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

    // MANIPULATORS

    /// If this stream is valid, invoke the `pubsync` method on the
    /// underlying stream supplied at construction of this object;
    /// otherwise, this function has no effect.
    GenericOutStream& flush();

    /// 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 the stream
    /// supplied at construction the one-byte integer comprised of the
    /// least-significant one byte of the `length`; otherwise, write to the
    /// 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`.
    GenericOutStream& putLength(int length);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putVersion(int version);

                      // *** scalar integer values ***

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt64(bsls::Types::Int64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint64(bsls::Types::Uint64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt56(bsls::Types::Int64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint56(bsls::Types::Uint64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt48(bsls::Types::Int64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint48(bsls::Types::Uint64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt40(bsls::Types::Int64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint40(bsls::Types::Uint64 value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt32(int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint32(unsigned int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt24(int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint24(unsigned int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt16(int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint16(unsigned int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putInt8(int value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putUint8(unsigned int value);

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

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putFloat64(double value);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putFloat32(float value);

                      // *** string values ***

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putString(const bsl::string& value);

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

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt64(const bsls::Types::Int64 *values,
                                    int                       numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint64(const bsls::Types::Uint64 *values,
                                     int                        numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt56(const bsls::Types::Int64 *values,
                                    int                       numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint56(const bsls::Types::Uint64 *values,
                                     int                        numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt48(const bsls::Types::Int64 *values,
                                    int                       numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint48(const bsls::Types::Uint64 *values,
                                     int                        numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt40(const bsls::Types::Int64 *values,
                                    int                       numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint40(const bsls::Types::Uint64 *values,
                                     int                        numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt32(const int *values, int numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint32(const unsigned int *values,
                                     int                 numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt24(const int *values, int numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint24(const unsigned int *values,
                                     int                 numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt16(const short *values, int numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint16(const unsigned short *values,
                                     int                   numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayInt8(const char        *values, int numValues);
    GenericOutStream& putArrayInt8(const signed char *values, int numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayUint8(const char          *values,
                                    int                  numValues);
    GenericOutStream& putArrayUint8(const unsigned char *values,
                                    int                  numValues);

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

    /// Write to the stream supplied at construction 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.
    GenericOutStream& putArrayFloat64(const double *values, int numValues);

    /// Write to the stream supplied at construction 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.
    GenericOutStream& 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 `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;
};

// FREE OPERATORS

/// 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 STREAMBUF, class TYPE>
GenericOutStream<STREAMBUF>&
operator<<(GenericOutStream<STREAMBUF>& stream, const TYPE& value);

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

                        // ----------------------
                        // class GenericOutStream
                        // ----------------------

// PRIVATE MANIPULATORS
template <class STREAMBUF>
inline
void GenericOutStream<STREAMBUF>::validate()
{
    d_validFlag = true;
}

// CREATORS
template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>::GenericOutStream(STREAMBUF *streamBuf,
                                              int        versionSelector)
: d_streamBuf(streamBuf)
, d_versionSelector(versionSelector)
, d_validFlag(true)
{
    BSLS_ASSERT_SAFE(streamBuf);
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>::~GenericOutStream()
{
}

// MANIPULATORS
template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>& GenericOutStream<STREAMBUF>::flush()
{
    if (isValid()) {
        invalidate();
        if (0 == d_streamBuf->pubsync()) {
            validate();
        }
    }
    return *this;
}

template <class STREAMBUF>
inline
void GenericOutStream<STREAMBUF>::invalidate()
{
    d_validFlag = false;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putLength(int length)
{
    BSLS_ASSERT_SAFE(0 <= length);

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

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putVersion(int version)
{
    return putUint8(version);
}

                      // *** scalar integer values ***

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt64(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT64];

    bytes[0] = rawBytes[7];
    bytes[1] = rawBytes[6];
    bytes[2] = rawBytes[5];
    bytes[3] = rawBytes[4];
    bytes[4] = rawBytes[3];
    bytes[5] = rawBytes[2];
    bytes[6] = rawBytes[1];
    bytes[7] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT64;
#endif

    if (k_SIZEOF_INT64 == d_streamBuf->sputn(bytes, k_SIZEOF_INT64)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint64(bsls::Types::Uint64 value)
{
    return putInt64(static_cast<bsls::Types::Int64>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt56(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT56];

    bytes[0] = rawBytes[6];
    bytes[1] = rawBytes[5];
    bytes[2] = rawBytes[4];
    bytes[3] = rawBytes[3];
    bytes[4] = rawBytes[2];
    bytes[5] = rawBytes[1];
    bytes[6] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT56;
#endif

    if (k_SIZEOF_INT56 == d_streamBuf->sputn(bytes, k_SIZEOF_INT56)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint56(bsls::Types::Uint64 value)
{
    return putInt56(static_cast<bsls::Types::Int64>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt48(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT48];

    bytes[0] = rawBytes[5];
    bytes[1] = rawBytes[4];
    bytes[2] = rawBytes[3];
    bytes[3] = rawBytes[2];
    bytes[4] = rawBytes[1];
    bytes[5] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT48;
#endif

    if (k_SIZEOF_INT48 == d_streamBuf->sputn(bytes, k_SIZEOF_INT48)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint48(bsls::Types::Uint64 value)
{
    return putInt48(static_cast<bsls::Types::Int64>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt40(bsls::Types::Int64 value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT40];

    bytes[0] = rawBytes[4];
    bytes[1] = rawBytes[3];
    bytes[2] = rawBytes[2];
    bytes[3] = rawBytes[1];
    bytes[4] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT40;
#endif

    if (k_SIZEOF_INT40 == d_streamBuf->sputn(bytes, k_SIZEOF_INT40)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint40(bsls::Types::Uint64 value)
{
    return putInt40(static_cast<bsls::Types::Int64>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt32(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT32];

    bytes[0] = rawBytes[3];
    bytes[1] = rawBytes[2];
    bytes[2] = rawBytes[1];
    bytes[3] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT32;
#endif

    if (k_SIZEOF_INT32 == d_streamBuf->sputn(bytes, k_SIZEOF_INT32)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint32(unsigned int value)
{
    return putInt32(static_cast<int>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt24(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT24];

    bytes[0] = rawBytes[2];
    bytes[1] = rawBytes[1];
    bytes[2] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT24;
#endif

    if (k_SIZEOF_INT24 == d_streamBuf->sputn(bytes, k_SIZEOF_INT24)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint24(unsigned int value)
{
    return putInt24(static_cast<int>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt16(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_INT16];

    bytes[0] = rawBytes[1];
    bytes[1] = rawBytes[0];
#else
    const char *bytes =
              reinterpret_cast<char *>(&value) + sizeof value - k_SIZEOF_INT16;
#endif

    if (k_SIZEOF_INT16 == d_streamBuf->sputn(bytes, k_SIZEOF_INT16)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint16(unsigned int value)
{
    return putInt16(static_cast<int>(value));
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putInt8(int value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

    if (STREAMBUF::traits_type::eof() !=
                                d_streamBuf->sputc(static_cast<char>(value))) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putUint8(unsigned int value)
{
    return putInt8(static_cast<int>(value));
}

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

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putFloat64(double value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_FLOAT64];

    bytes[0] = rawBytes[sizeof value - 1];
    bytes[1] = rawBytes[sizeof value - 2];
    bytes[2] = rawBytes[sizeof value - 3];
    bytes[3] = rawBytes[sizeof value - 4];
    bytes[4] = rawBytes[sizeof value - 5];
    bytes[5] = rawBytes[sizeof value - 6];
    bytes[6] = rawBytes[sizeof value - 7];
    bytes[7] = rawBytes[sizeof value - 8];
#else
    const char *bytes = reinterpret_cast<char *>(&value);
#endif

    if (k_SIZEOF_FLOAT64 == d_streamBuf->sputn(bytes, k_SIZEOF_FLOAT64)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putFloat32(float value)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(!isValid())) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return *this;                                                 // RETURN
    }

    invalidate();

#if BSLS_PLATFORM_IS_LITTLE_ENDIAN
    const char *rawBytes = reinterpret_cast<char *>(&value);
    char        bytes[k_SIZEOF_FLOAT32];

    bytes[0] = rawBytes[sizeof value - 1];
    bytes[1] = rawBytes[sizeof value - 2];
    bytes[2] = rawBytes[sizeof value - 3];
    bytes[3] = rawBytes[sizeof value - 4];
#else
    const char *bytes = reinterpret_cast<char *>(&value);
#endif

    if (k_SIZEOF_FLOAT32 == d_streamBuf->sputn(bytes, k_SIZEOF_FLOAT32)) {
        validate();
    }

    return *this;
}

                      // *** string values ***

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

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

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt64(const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Int64 *end = values + numValues;
    for (; values != end; ++values) {
        putInt64(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint64(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Uint64 *end = values + numValues;
    for (; values != end; ++values) {
        putUint64(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt56(const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Int64 *end = values + numValues;
    for (; values != end; ++values) {
        putInt56(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint56(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Uint64 *end = values + numValues;
    for (; values != end; ++values) {
        putUint56(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt48(const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Int64 *end = values + numValues;
    for (; values != end; ++values) {
        putInt48(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint48(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Uint64 *end = values + numValues;
    for (; values != end; ++values) {
        putUint48(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt40(const bsls::Types::Int64 *values,
                                           int                       numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Int64 *end = values + numValues;
    for (; values != end; ++values) {
        putInt40(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint40(
                                          const bsls::Types::Uint64 *values,
                                          int                        numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const bsls::Types::Uint64 *end = values + numValues;
    for (; values != end; ++values) {
        putUint40(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt32(const int *values, int numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const int *end = values + numValues;
    for (; values != end; ++values) {
        putInt32(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint32(const unsigned int *values,
                                            int                 numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const unsigned int *end = values + numValues;
    for (; values != end; ++values) {
        putUint32(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt24(const int *values, int numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const int *end = values + numValues;
    for (; values != end; ++values) {
        putInt24(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint24(const unsigned int *values,
                                            int                 numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const unsigned int *end = values + numValues;
    for (; values != end; ++values) {
        putUint24(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayInt16(const short *values, int numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const short *end = values + numValues;
    for (; values != end; ++values) {
        putInt16(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayUint16(const unsigned short *values,
                                            int                   numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const unsigned short *end = values + numValues;
    for (; values != end; ++values) {
        putUint16(*values);
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::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
    }

    invalidate();

    if (numValues == d_streamBuf->sputn(values, numValues)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::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
    }

    invalidate();

    if (numValues == d_streamBuf->sputn(reinterpret_cast<const char *>(values),
                                        numValues)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::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
    }

    invalidate();

    if (numValues == d_streamBuf->sputn(values, numValues)) {
        validate();
    }

    return *this;
}

template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::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
    }

    invalidate();

    if (numValues == d_streamBuf->sputn(reinterpret_cast<const char *>(values),
                                        numValues)) {
        validate();
    }

    return *this;
}

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

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayFloat64(const double *values,
                                             int           numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const double *end = values + numValues;
    for (; values != end; ++values) {
        putFloat64(*values);
    }

    return *this;
}

template <class STREAMBUF>
GenericOutStream<STREAMBUF>&
GenericOutStream<STREAMBUF>::putArrayFloat32(const float *values,
                                             int          numValues)
{
    BSLS_ASSERT(values);
    BSLS_ASSERT(0 <= numValues);

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

    const float *end = values + numValues;
    for (; values != end; ++values) {
        putFloat32(*values);
    }

    return *this;
}

// ACCESSORS
template <class STREAMBUF>
inline
GenericOutStream<STREAMBUF>::operator const void *() const
{
    return isValid() ? this : 0;
}

template <class STREAMBUF>
inline
int GenericOutStream<STREAMBUF>::bdexVersionSelector() const
{
    return d_versionSelector;
}

template <class STREAMBUF>
inline
bool GenericOutStream<STREAMBUF>::isValid() const
{
    return d_validFlag;
}

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

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

#endif

// ----------------------------------------------------------------------------
// Copyright 2014 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------
