// bdlsb_overflowmemoutput.h                                          -*-C++-*-
#ifndef INCLUDED_BDLSB_OVERFLOWMEMOUTPUT
#define INCLUDED_BDLSB_OVERFLOWMEMOUTPUT

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

//@PURPOSE: Provide an overflowable output 'streambuf' using a client buffer.
//
//@CLASSES:
// bdlsb::OverflowMemOutput: overflowable output stream buffer
//
//@SEE_ALSO: bdlsb_fixedmemoutput, bdlsb_overflowmemoutstreambuf
//
//@DESCRIPTION: This component provides a mechanism,
// 'bdlsb::OverflowMemOutput', that implements the output portion of the
// 'bsl::basic_streambuf' protocol using a user-supplied memory buffer and a
// managed, allocator-supplied overflow buffer that is created when the
// client-supplied buffer runs out.  Method names necessarily correspond to the
// protocol-specified method names.  As with 'bdlsb_overflowmemoutstreambuf',
// clients supply the character buffer at construction.  Unlike
// 'bdlsb_fixedmemoutput', they can no longer reinitialize the stream buffer
// with a different character buffer by calling the 'pubsetbuf' method;
// instead, if that buffer runs out, the 'bdlsb::OverflowMemOutput' will
// allocate another buffer (see "Overflow Buffer" below).  The only difference
// between this component and 'bdlsb_overflowmemoutstreambuf' is that the class
// 'bdlsb::OverflowMemOutput' does *not* derive from 'bsl::streambuf' and does
// not support locales.  This is advantageous for performance reasons, as the
// overhead of the initialization and virtual function calls of a
// 'bsl::streambuf' can be undesirable.  The 'bdlsb::OverflowMemOutput' is
// designed to be used by generic template code that must be instantiated on a
// type that matches the interface of 'bsl::streambuf', but does not require an
// actual 'bsl::streambuf', in particular 'bslx_genericoutstream'.
//
///Overflow Buffer
///---------------
// This output stream buffer uses the initial buffer (supplied at construction)
// as its output buffer.  If an overflow of the initial buffer were to occur,
// an additional buffer (the overflow buffer) will be allocated.  If this
// overflow buffer ever becomes full, it will be automatically grown.  The
// overflow buffer grows geometrically (to twice the current overflow buffer
// size) whenever the amount of data written exceeds the amount of space
// available.  On growth, the old overflow buffer is copied over to the newly
// allocated overflow buffer, and then deallocated, thus after any write one
// cannot assume that the overflow buffer is still the same memory.  Data in
// the overflow buffer beyond the reach of the current write position is not
// guaranteed to be preserved after a growth operation.
//
///Streaming Architecture
///----------------------
// Stream buffers are designed to decouple device handling from content
// formatting, providing the requisite device handling and possible buffering
// services, and leaving the formatting to the client stream.  The standard C++
// IOStreams library further partitions streaming into input streaming and
// output streaming, separating responsibilities for each at both the stream
// layer and the stream buffer layer.  The BDE streaming library for 'blsx',
// including all of 'bdlsb', follows this model.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of 'bdlsb::OverflowMemOutput'
/// - - - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates instantiating a template,
// 'bslx::GenericOutStream', on a 'bdlsb::OverflowMemOutput' object and using
// the 'bslx::GenericOutStream' object to stream out some data.
//
// First, we create a stream buffer, 'streamBuf', and supply it stack allocated
// memory as its initial buffer:
//..
//  enum { k_STREAMBUF_CAPACITY = 8 };
//
//  char                     buffer[k_STREAMBUF_CAPACITY];
//  bdlsb::OverflowMemOutput streamBuf(buffer, k_STREAMBUF_CAPACITY);
//..
// Then, we create an instance of 'bslx::GenericOutStream' using 'streamBuf',
// with an arbitrary value for its 'versionSelector', and serialize some data:
//..
//  bslx::GenericOutStream<bdlsb::OverflowMemOutput> outStream(&streamBuf,
//                                                             20150707);
//  int MAGIC = 0x1812;
//  outStream.putInt32(MAGIC);
//  outStream.putInt32(MAGIC+1);
//..
// Next, we verify that the data was correctly serialized and completely filled
// initial buffer supplied at the stream buffer construction:
//..
//  assert(outStream.isValid());
//  assert(8 == streamBuf.dataLength());
//  assert(0 == bsl::memcmp(streamBuf.initialBuffer(),
//                          "\x00\x00\x18\x12\x00\x00\x18\x13",
//                          8));
//  assert(0 == bsl::memcmp(buffer, "\x00\x00\x18\x12\x00\x00\x18\x13", 8));
//  assert(0 == streamBuf.overflowBuffer());
//  assert(0 == streamBuf.overflowBufferSize());
//..
// Then, we serialize some more data to trigger allocation of the internal
// overflow buffer:
//..
//  outStream.putString(bsl::string("test"));
//..
// Finally, we verify that the additional data was serialized correctly and
// landed into dynamically allocated overflow buffer:
//..
//  assert(outStream.isValid());
//  assert(13 == streamBuf.dataLength());
//  assert(0  != streamBuf.overflowBuffer());
//  assert(5  == streamBuf.dataLengthInOverflowBuffer());
//  assert(0  == bsl::memcmp(streamBuf.overflowBuffer(), "\x04test", 5));
//..

#include <bdlscm_version.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

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

namespace BloombergLP {
namespace bdlsb {

                       // =======================
                       // class OverflowMemOutput
                       // =======================

class OverflowMemOutput {
    // This class, like 'bdlsb::OverflowMemOutStreamBuf', implements the output
    // functionality of the 'bsl::basic_streambuf' interface, using a
    // client-supplied buffer and allocator-supplied overflow buffer if
    // additional memory is needed.  It has an interface similar to
    // 'bdlsb::OverflowMemOutStreamBuf' but does *not* inherit from
    // 'bsl::streambuf'.  Thus, it is suitable for use as template parameter to
    // 'bslx::GenericByteOutStream' (but not to 'bslx::ByteOutStream' or
    // 'bslx::ByteOutStreamFormatter').  Note that this class is not designed
    // to be derived from.

    // DATA

    // The order of the data members is determined by our usage pattern for
    // cache efficiency.  Do not reorder them.

    bsl::size_t       d_dataLength;            // total data length

    char             *d_put_p;                 // current put pointer

    char             *d_initialBuffer_p;       // user-supplied buffer (held,
                                               // not owned)

    bsl::size_t       d_initialBufferSize;     // size of 'd_initialBuffer_p'
                                               // buffer

    bool              d_inOverflowBufferFlag;  // true if 'd_put_p' points into
                                               // the overflow buffer

    char             *d_overflowBuffer_p;      // overflow buffer (owned)

    bsl::size_t       d_overflowBufferSize;    // size of 'd_overflowBuffer_p'
                                               // buffer

    bslma::Allocator *d_allocator_p;           // memory allocator (held, not
                                               // owned)

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

  private:
    // PRIVATE MANIPULATORS
    void grow(bsl::size_t numBytes, bool copyOrigin = true);
        // Replace the overflow buffer with another buffer, larger then the
        // current buffer by at least the specified 'numBytes', by growing
        // geometrically by a factor of two.  Optionally specify a 'copyOrigin'
        // indicating whether the content of the overflow buffer should be
        // copied into new location.  Note that 'd_put_p' is not updated, and
        // may be pointing to deallocated memory when the method returns.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(OverflowMemOutput,
                                   bslma::UsesBslmaAllocator);

    // TYPES
    typedef char                    char_type;
    typedef bsl::char_traits<char>  traits_type;
    typedef traits_type::int_type   int_type;
    typedef traits_type::pos_type   pos_type;
    typedef traits_type::off_type   off_type;

    // CREATORS
    OverflowMemOutput(char             *buffer,
                      bsl::size_t       length,
                      bslma::Allocator *basicAllocator = 0);
        // Create an 'OverflowMemOutput' using the specified 'buffer' of the
        // specified 'length' as the initial output buffer.  Optionally specify
        // a 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined unless 'buffer != 0 && length > 0'.  Note that this stream
        // buffer does not assume ownership of 'buffer'.

    ~OverflowMemOutput();
        // Destroy this stream buffer.

    // MANIPULATORS
                             // *** 27.5.2.2.2 buffer and positioning ***

    pos_type pubseekoff(off_type                offset,
                        bsl::ios_base::seekdir  way,
                        bsl::ios_base::openmode which = bsl::ios_base::out);
        // Set the position indicator to the relative specified 'offset' from
        // the base position indicated by the specified 'way' and return the
        // resulting absolute position on success or pos_type(-1) on failure.
        // Optionally specify 'which' area of the stream buffer.  The seek
        // operation will fail if 'which' does not include the flag
        // 'bsl::ios_base::out' or if the resulting absolute position is less
        // than zero.

    pos_type pubseekpos(pos_type                position,
                        bsl::ios_base::openmode which = bsl::ios_base::out);
        // Set the position indicator to the specified 'position' and return
        // the resulting absolute position on success or pos_type(-1) on
        // failure.  Optionally specify 'which' area of the stream buffer.  The
        // 'seekpos' operation will fail if 'which' does not include the flag
        // 'bsl::ios_base::out' or if 'position' is less then zero.

    OverflowMemOutput *pubsetbuf(char *buffer, bsl::streamsize length);
        // Reinitialize this stream buffer to use the specified character
        // 'buffer' having the specified 'length'.  Return a pointer providing
        // modifiable access to this stream buffer.  This stream buffer does
        // not support reinitialization of the internal character buffer.

    int pubsync();
        // Synchronize this stream buffer with associated character sequence.
        // Operation has no effect.  Return '0' unconditionally.

                             // *** 27.5.2.2.5 put area ***

    int_type sputc(char c);
        // Write the specified character 'c' at the current write position and
        // advance write position of this buffer.  Return 'c', or
        // 'traits_type::eof()' if the end of the write buffer is reached.

    bsl::streamsize sputn(const char *source, bsl::streamsize length);
        // Write the specified 'length' characters from the specified 'source'
        // to this buffer.  Return the number of characters written, which is
        // either 'length' or the distance from the current write position to
        // the end of the write buffer, whichever is smaller, and move the
        // write cursor position by this amount.  The behaviour is undefined
        // unless 'source != 0 || length > 0'.

    // ACCESSORS
    bsl::size_t dataLength() const;
        // Return the number of bytes that have been written to this object.

    bsl::size_t dataLengthInInitialBuffer() const;
        // Return the length of data in the initial buffer, i.e.,
        // 'dataLength()' if there is no overflow buffer, or
        // 'initialBufferSize()' if there is one.

    bsl::size_t dataLengthInOverflowBuffer() const;
        // Return the length of the data in the overflow buffer, i.e., 0 if
        // there is no overflow buffer, or 'dataLength() - initialBufferSize()'
        // if there is one.

    const char *initialBuffer() const;
        // Return a pointer providing non-modifiable access to the character
        // buffer held by this stream buffer (specified at construction).

    bsl::size_t initialBufferSize() const;
        // Return the size of the initial buffer held by this stream buffer.

    const char *overflowBuffer() const;
        // Return a pointer providing non-modifiable access to the overflow
        // buffer if there is one, or 0 otherwise.

    bsl::size_t overflowBufferSize() const;
        // Return the size of the overflow buffer, or 0 if there is no overflow
        // buffer.
};

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

                       // -----------------------
                       // class OverflowMemOutput
                       // -----------------------

// CREATORS
inline
OverflowMemOutput::~OverflowMemOutput()
{
    d_allocator_p->deallocate(d_overflowBuffer_p);
}

// MANIPULATORS
inline
OverflowMemOutput::pos_type
OverflowMemOutput::pubseekpos(pos_type position, bsl::ios_base::openmode which)
{
    return pubseekoff(off_type(position), bsl::ios_base::beg, which);
}

inline
OverflowMemOutput *OverflowMemOutput::pubsetbuf(char *, bsl::streamsize)
{
    return this;
}

inline
int OverflowMemOutput::pubsync()
{
    return 0;
}

// ACCESSORS
inline
bsl::size_t OverflowMemOutput::dataLength() const
{
    return d_dataLength;
}

inline
bsl::size_t OverflowMemOutput::dataLengthInInitialBuffer() const
{
    return d_inOverflowBufferFlag ? d_initialBufferSize : d_dataLength;
}

inline
bsl::size_t OverflowMemOutput::dataLengthInOverflowBuffer() const
{
    return d_inOverflowBufferFlag ? d_dataLength - d_initialBufferSize : 0;
}

inline
const char *OverflowMemOutput::initialBuffer() const
{
    return d_initialBuffer_p;
}

inline
bsl::size_t OverflowMemOutput::initialBufferSize() const
{
    return d_initialBufferSize;
}

inline
const char *OverflowMemOutput::overflowBuffer() const
{
    return d_overflowBuffer_p;
}

inline
bsl::size_t OverflowMemOutput::overflowBufferSize() const
{
    return d_overflowBufferSize;
}

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

#endif

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