// bdlsb_overflowmemoutstreambuf.h                                    -*-C++-*-
#ifndef INCLUDED_BDLSB_OVERFLOWMEMOUTSTREAMBUF
#define INCLUDED_BDLSB_OVERFLOWMEMOUTSTREAMBUF

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

//@PURPOSE: Provide an overflowable output 'streambuf' using a client buffer.
//
//@CLASSES:
//  bdlsb::OverflowMemOutStreamBuf: overflowable output 'bsl::streambuf'
//
//@SEE_ALSO: bdlsb_overflowmemoutput
//
//@DESCRIPTION: This component implements the output portion of the
// 'bsl::basic_streambuf' protocol using a user-supplied initial buffer and a
// dynamically allocated overflow buffer.  As with 'bdlsb_fixedmemoutput',
// users 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::OverflowMemOutStreamBuf' will
// allocate another buffer (see "Overflow Buffer" below).   The only difference
// between this component and 'bdlsb_overflowmemoutput' is that the class
// 'bdlsb::OverflowMemOutStreamBuf' is derived from a 'bsl::streambuf'.  Method
// names necessarily correspond to those specified by the protocol.  Refer to
// the C++ Standard, Section 27.5.2, for a full specification of the
// 'bsl::basic_streambuf' interface.  This component provides none of the
// input-related functionality of 'basic_streambuf' (see Streaming
// Architecture, below), nor does it use locales in any way.
//
///Overflow Buffer
///---------------
// This output stream buffer will use 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/seek
// forward 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 'bdex',
// including all of 'bdlsb', follows this model.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
/// Example 1: Basic Use of 'bdlsb::OverflowMemOutStreamBuf'
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates using a 'bdlsb::OverflowMemOutStreamBuf' in order
// to test a user defined stream type, 'CapitalizingStream'. In this example,
// we'll define a simple example stream type 'CapitalizingStream' that
// capitalizes lower-case ASCII data written to the stream. In order to test
// this 'CapitalizingStream' type, we'll create an instance, and supply it a
// 'bdlsb::OverflowMemOutStreamBuf' object as its stream buffer; after we write
// some character data to the 'CapitalizingStream' we'll inspect the buffer of
// the 'bdlsb::OverflowMemOutStreamBuf' and verify its contents match our
// expected output. Note that to simplify the example, we do not include the
// functions for streaming non-character data, e.g., numeric values.
//
// First, we define our example stream class, 'CapitalizingStream' (which we
// will later test using 'bdlsb::OverflowMemOutStreamBuf):
//..
//  class CapitalizingStream {
//      // This class capitalizes lower-case ASCII characters that are output.
//
//      // DATA
//      bsl::streambuf  *d_streamBuffer_p;   // pointer to a stream buffer
//
//      // FRIENDS
//      friend CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                            const char          *data);
//    public:
//      // CREATORS
//      explicit CapitalizingStream(bsl::streambuf *streamBuffer);
//          // Create a capitalizing stream using the specified 'streamBuffer'
//          // as the underlying stream buffer for the stream.
//  };
//
//  // FREE OPERATORS
//  CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                 const char          *data);
//      // Write the specified 'data' in capitalized form to the specified
//      // 'stream'.
//
//  CapitalizingStream::CapitalizingStream(bsl::streambuf *streamBuffer)
//  : d_streamBuffer_p(streamBuffer)
//  {
//  }
//..
// As is typical, the streaming operators are made friends of the class.
//
// Note that we cannot directly use 'bsl::toupper' to capitalize each
// individual character, because 'bsl::toupper' operates on 'int' instead of
// 'char'.  Instead, we call a function 'ucharToUpper' that works in terms of
// 'unsigned char'.  some care must be made to avoid undefined and
// implementation-specific behavior during the conversions to and from 'int'.
// Therefore we wrap 'bsl::toupper' in an interface that works in terms of
// 'unsigned char':
//..
//  static unsigned char ucharToUpper(unsigned char input)
//      // Return the upper-case equivalent to the specified 'input' character.
//  {
//      return static_cast<unsigned char>(bsl::toupper(input));
//  }
//..
// Finally, we use the 'transform' algorithm to convert lower-case characters
// to upper-case.
//..
//  // FREE OPERATORS
//  CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                 const char          *data)
//  {
//      bsl::string tmp(data);
//      bsl::transform(tmp.begin(),
//                     tmp.end(),
//                     tmp.begin(),
//                     ucharToUpper);
//      stream.d_streamBuffer_p->sputn(tmp.data(), tmp.length());
//      return stream;
//  }
//..
// Now, we create an instance of 'bdlsb::OverflowMemOutStreamBuf' that will
// serve as underlying stream buffer for our 'CapitalingStream':
//..
//  enum { INITIAL_CAPACITY = 10 };
//  char buffer[INITIAL_CAPACITY];
//
//  bdlsb::OverflowMemOutStreamBuf streamBuffer(buffer, INITIAL_CAPACITY);
//..
// Now, we test our 'CapitalingStream' by supplying the created instance of
// 'bdlsb::OverflowMemOutStreamBuf' and using it to inspect the output of the
// stream:
//..
//  CapitalizingStream  testStream(&streamBuffer);
//  testStream << "Hello world.";
//..
// Finally, we verify that the streamed data has been capitalized and the
// portion of the data that does not fit into initial buffer is placed into
// dynamically allocated overflow buffer:
//..
//  assert(10 == streamBuffer.dataLengthInInitialBuffer());
//  assert(0  == strncmp("HELLO WORL",
//                       streamBuffer.initialBuffer(),
//                       streamBuffer.dataLengthInInitialBuffer()));
//  assert(2  == streamBuffer.dataLengthInOverflowBuffer());
//  assert(0  == strncmp("D.",
//                       streamBuffer.overflowBuffer(),
//                       streamBuffer.dataLengthInOverflowBuffer()));
//..

#include <bdlscm_version.h>

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

#include <bslmf_nestedtraitdeclaration.h>

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

namespace BloombergLP {
namespace bdlsb {

                   // =============================
                   // class OverflowMemOutStreamBuf
                   // =============================

class OverflowMemOutStreamBuf : public bsl::streambuf {
    // This class implements the output functionality of the
    // 'bsl::basic_streambuf' protocol, using client-supplied memory and
    // client-supplied allocator if additional memory is needed.

    // DATA
    // The order of the data members is determined by our usage pattern for
    // cache efficiency.  Do not reorder them.
    //
    // 'd_dataLength' is marked 'mutable' because it is used to cache the
    // length of the data written to the stream buffer.  Characters can be
    // written to the buffer via the base class (without calling a manipulator
    // on this class), so this cached value is updated when accessors are
    // called.

    mutable bsl::size_t  d_dataLength;            // total data length

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

    bsl::size_t          d_initialBufferSize;     // size of initial buffer

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

    char                *d_overflowBuffer_p;      // overflow buffer (owned)

    bsl::size_t          d_overflowBufferSize;    // size of overflow buffer

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

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

  private:
    // PRIVATE MANIPULATORS
    void grow(bsl::size_t numBytes);
        // Replace the overflow buffer with another buffer larger by at least
        // the specified 'numBytes', by growing geometrically by a factor of
        // two, and preserve the bytes that are in use by the overflow buffer,
        // as determined by the 'd_dataLength'.  Note that 'pptr()' is not
        // updated at this time, and may be pointing to deallocated memory when
        // this returns.  Also note, that because 'd_dataLength' is used to
        // determine the amount of data in the buffer, this function should be
        // called after 'privateSync'.

    void privateSync() const;
        // Set 'd_dataLength' to the amount of data that has been written to
        // this stream buffer, from the beginning of the stream to the current
        // 'pptr()' position.  Note that if 'pptr()' points into the overflow
        // buffer, this size the initial buffer, plus the portion of the
        // overflow buffer that has been written to.

  protected:
    // PROTECTED VIRTUAL FUNCTIONS

           // *** 27.5.2.4.2 buffer management and positioning ***
    virtual pos_type seekoff(
       off_type                offset,
       bsl::ios_base::seekdir  way,
       bsl::ios_base::openmode which = bsl::ios_base::in | 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
        // negative.

    virtual pos_type seekpos(
       pos_type                position,
       bsl::ios_base::openmode which = bsl::ios_base::in | 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 negative.

    virtual int sync();
        // Set 'd_dataLength' to the amount of data that has been written to
        // this stream buffer, from the beginning of the stream to the current
        // 'pptr()' position and return 0.  Note that if 'pptr()' points into
        // the overflow buffer, this size the initial buffer, plus the portion
        // of the overflow buffer that has been written to.

    virtual bsl::streamsize xsputn(const char_type *source,
                                   bsl::streamsize  numChars);
        // Write the specified 'numChars' characters from the specified
        // 'source' to the stream buffer.  Return the number of characters
        // successfully written.  The behavior is undefined unless '(source &&
        // 0 < numChars) || 0 == numChars'.

    virtual int_type overflow(int_type c = bsl::streambuf::traits_type::eof());
        // If c is not 'EOF', adjust the underlying buffer so that the next put
        // position is valid, put the specified 'c' at this position, and
        // increment the put position.  Return 'c' on success, and
        // 'traits_type::not_eof(c)' if 'c' is 'EOF'.

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

    // CREATORS
    OverflowMemOutStreamBuf(char             *buffer,
                            bsl::size_t       size,
                            bslma::Allocator *basicAllocator = 0);
        // Create an empty stream buffer that uses the specified 'buffer' as an
        // initial output buffer of the specified 'size' (in bytes).
        // 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' points to a valid
        // sequence of positive 'size' characters.  Note that this stream
        // buffer does not assume ownership of 'buffer'.

    virtual ~OverflowMemOutStreamBuf();
        // Destroy this stream buffer.

    // ACCESSORS
    bsl::size_t dataLength() const;
        // Return the number of bytes written to this stream.  Note that if
        // 'pptr()' is currently pointing into the overflow buffer the data
        // length will be greater than the size of the initial buffer.

    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 to the non-modifiable buffer supplied at
        // construction.

    bsl::size_t initialBufferSize() const;
        // Return the size of the buffer supplied at construction.

    const char *overflowBuffer() const;
        // Return a pointer to the non-modifiable 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 OverflowMemOutStreamBuf
                    // -----------------------------

// PROTECTED VIRTUAL FUNCTIONS
inline
OverflowMemOutStreamBuf::pos_type
OverflowMemOutStreamBuf::seekpos(pos_type                position,
                                 bsl::ios_base::openmode which)
{
    return seekoff(off_type(position), bsl::ios_base::beg, which);
}

inline
int OverflowMemOutStreamBuf::sync()
{
    privateSync();
    return 0;
}

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

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

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

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

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

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

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

inline
bsl::size_t OverflowMemOutStreamBuf::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 ----------------------------------