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

/// 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.
class OverflowMemOutput {

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

    /// 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.
    void grow(bsl::size_t numBytes, bool copyOrigin = true);

  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

    /// 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(char             *buffer,
                      bsl::size_t       length,
                      bslma::Allocator *basicAllocator = 0);

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

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

    /// 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 pubseekoff(off_type                offset,
                        bsl::ios_base::seekdir  way,
                        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.
    pos_type pubseekpos(pos_type                position,
                        bsl::ios_base::openmode which = bsl::ios_base::out);

    /// 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.
    OverflowMemOutput *pubsetbuf(char *buffer, bsl::streamsize length);

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

                             // *** 27.5.2.2.5 put area ***

    /// 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.
    int_type sputc(char c);

    /// 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`.
    bsl::streamsize sputn(const char *source, bsl::streamsize length);

    // ACCESSORS

    /// Return the number of bytes that have been written to this object.
    bsl::size_t dataLength() 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 dataLengthInInitialBuffer() 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.
    bsl::size_t dataLengthInOverflowBuffer() const;

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

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

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

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

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