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

template <class STREAMBUF>
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.

    // 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
    void validate();
        // Put this output stream into a valid state.  This function has no
        // effect if this stream is already valid.

  public:
    // CREATORS
    GenericOutStream(STREAMBUF *streamBuf, int versionSelector);
        // 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();
        // Destroy this object.

    // MANIPULATORS
    GenericOutStream& flush();
        // 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.

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

    GenericOutStream& putLength(int length);
        // 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& putVersion(int version);
        // 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.

                      // *** scalar integer values ***

    GenericOutStream& putInt64(bsls::Types::Int64 value);
        // 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& putUint64(bsls::Types::Uint64 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& putInt56(bsls::Types::Int64 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& putUint56(bsls::Types::Uint64 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& putInt48(bsls::Types::Int64 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& putUint48(bsls::Types::Uint64 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& putInt40(bsls::Types::Int64 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& putUint40(bsls::Types::Uint64 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& putInt32(int 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& putUint32(unsigned 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& putInt24(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& putUint24(unsigned 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& putInt16(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& putUint16(unsigned 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& putInt8(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& putUint8(unsigned 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.

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

    GenericOutStream& putFloat64(double value);
        // 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& putFloat32(float 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.

                      // *** string values ***

    GenericOutStream& putString(const bsl::string& value);
        // 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.

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

    GenericOutStream& putArrayInt64(const bsls::Types::Int64 *values,
                                    int                       numValues);
        // 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& putArrayUint64(const bsls::Types::Uint64 *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& putArrayInt56(const bsls::Types::Int64 *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& putArrayUint56(const bsls::Types::Uint64 *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& putArrayInt48(const bsls::Types::Int64 *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& putArrayUint48(const bsls::Types::Uint64 *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& putArrayInt40(const bsls::Types::Int64 *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& putArrayUint40(const bsls::Types::Uint64 *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& putArrayInt32(const int *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& putArrayUint32(const unsigned 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& putArrayInt24(const 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& putArrayUint24(const unsigned 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& putArrayInt16(const short *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& putArrayUint16(const unsigned 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& 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 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);
        // 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.

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

    GenericOutStream& putArrayFloat64(const double *values, int numValues);
        // 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& putArrayFloat32(const float *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.

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

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

    bool isValid() 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.
};

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

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