// 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 `bslx`,
// 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):
// ```
// /// This class capitalizes lower-case ASCII characters that are output.
// class CapitalizingStream {
//
//     // DATA
//     bsl::streambuf  *d_streamBuffer_p;   // pointer to a stream buffer
//
//     // FRIENDS
//     friend CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                           const char          *data);
//   public:
//     // CREATORS
//
//     /// Create a capitalizing stream using the specified `streamBuffer` as
//     /// the underlying stream buffer for the stream.
//     explicit CapitalizingStream(bsl::streambuf *streamBuffer);
// };
//
// // FREE OPERATORS
//
// /// Write the specified `data` in capitalized form to the specified
// /// `stream`.
// CapitalizingStream& operator<<(CapitalizingStream&  stream,
//                                const char          *data);
//
// 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`:
// ```
// /// Return the upper-case equivalent to the specified `input` character.
// static unsigned char ucharToUpper(unsigned char input)
// {
//     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 <bsls_keyword.h>

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

namespace BloombergLP {
namespace bdlsb {

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

/// 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.
class OverflowMemOutStreamBuf : public bsl::streambuf {

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

    /// 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 grow(bsl::size_t numBytes);

    /// 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.
    void privateSync() const;

  protected:
    // PROTECTED VIRTUAL FUNCTIONS

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

    /// 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.
    pos_type seekpos(
       pos_type                position,
       bsl::ios_base::openmode which = bsl::ios_base::in | bsl::ios_base::out)
                                                         BSLS_KEYWORD_OVERRIDE;

    /// 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.
    int sync() BSLS_KEYWORD_OVERRIDE;

    /// 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'.
    bsl::streamsize xsputn(const char_type *source,
                           bsl::streamsize  numChars) BSLS_KEYWORD_OVERRIDE;

    /// 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`.
    int_type overflow(int_type c = bsl::streambuf::traits_type::eof())
                                                         BSLS_KEYWORD_OVERRIDE;

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

    // CREATORS

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

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

    // ACCESSORS

    /// 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 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 to the non-modifiable buffer supplied at construction.
    const char *initialBuffer() const;

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

    /// Return a pointer to the non-modifiable 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 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 ----------------------------------
