// bdlsb_memoutstreambuf.h                                            -*-C++-*-
#ifndef INCLUDED_BDLSB_MEMOUTSTREAMBUF
#define INCLUDED_BDLSB_MEMOUTSTREAMBUF

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

//@PURPOSE: Provide an output `basic_streambuf` using managed memory.
//
//@CLASSES:
//   bdlsb::MemOutStreamBuf: output stream buffer using memory allocator
//
//@SEE_ALSO: bdlsb_fixedmemoutstreambuf, bdlsb_fixedmeminstreambuf
//
//@DESCRIPTION: This component provides a mechanism, `bdlsb::MemOutStreamBuf`,
// that implements the output portion of the `bsl::basic_streambuf` protocol
// using a managed, allocator-supplied memory buffer.  Method names necessarily
// correspond to those specified by the protocol.
//
// This component provides none of the input-related functionality of
// `basic_streambuf` (see "Streaming Architecture", below), nor does it use
// locales in any way.
//
// Because the underlying buffer is always obtained from the client-specified
// allocator, the `pubsetbuf` method in this component has no effect.
//
// Note that this component has an unspecified minimum allocation size, and
// therefore users trying to limit themselves to a fixed buffer should use
// bdlsb_fixedmemoutstreambuf.
//
///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.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
/// Example 1: Basic Use of `bdlsb::MemOutStreamBuf`
///- - - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates using a `bdlsb::MemOutStreamBuf` in order to test
// a user defined stream type, `CapitalizingStream`.  In this example, we'll
// define a simple example stream type `CapitalizingStream` that capitalizing
// 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::MemOutStreamBuf` object as its stream buffer; after we write some
// character data to the `CapitalizingStream` we'll inspect the buffer of the
// `bdlsb::MemOutStreamBuf` 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::MemOutStreamBuf):
// ```
// /// 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 underlying stream buffer to 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::MemOutStreamBuf` that will serve as
// underlying stream buffer for our `CapitalingStream`:
// ```
// bdlsb::MemOutStreamBuf streamBuffer;
// ```
// Now, we test our `CapitalingStream` by supplying the created instance of
// `bdlsb::MemOutStreamBuf` 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 placed
// into dynamically allocated buffer:
// ```
// assert(12 == streamBuffer.length());
// assert(0  == bsl::strncmp("HELLO WORLD.",
//                           streamBuffer.data(),
//                           streamBuffer.length()));
// ```

#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 <bsls_types.h>

#include <bsl_cstddef.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>
#include <bsl_ios.h>
#include <bsl_streambuf.h>  // (char|int|pos|off|traits)_type

namespace BloombergLP {
namespace bdlsb {

                         // =====================
                         // class MemOutStreamBuf
                         // =====================

/// This `class` implements the output functionality of the
/// `basic_streambuf` protocol, using a user-supplied or default `bslma`
/// allocator to supply memory.
class MemOutStreamBuf : public bsl::streambuf {

    // PRIVATE CONSTANTS
    enum {
        k_INITIAL_BUFFER_SIZE = 256,  // default initial buffer size

        k_GROWTH_FACTOR       =   2   // geometric growth factor to use when
                                      // resizing internal buffer

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
      , BDESB_INITIAL_BUFFER_SIZE = k_INITIAL_BUFFER_SIZE
      , BDESB_GROWTH_FACTOR       = k_GROWTH_FACTOR
      , INITIAL_BUFFER_SIZE       = k_INITIAL_BUFFER_SIZE
      , GROWTH_FACTOR             = k_GROWTH_FACTOR
#endif // BDE_OMIT_INTERNAL_DEPRECATED
    };

    // DATA
    bslma::Allocator *d_allocator_p;  // memory source for buffer memory
                                      // (held, not owned)

  private:
    // NOT IMPLEMENTED
    MemOutStreamBuf(const MemOutStreamBuf&); // = delete;
    MemOutStreamBuf& operator=(const MemOutStreamBuf&); // = delete;

  private:
    // PRIVATE MANIPULATORS

    /// Grow the size of the internal buffer to be at least large enough to
    /// fit the specified `newLength` characters.  The buffer size is grown
    /// by the minimum power of `k_GROWTH_FACTOR` needed to accommodate the
    /// new length, but with a final size not less than
    /// `k_INITIAL_BUFFER_SIZE`.  This method has no effect if 'newLength <=
    /// capacity()' holds before the call.
    void grow(bsl::size_t newLength);

  protected:
    // PROTECTED MANIPULATORS

    /// Append the optionally specified `insertionChar` to this stream
    /// buffer's character buffer and return `insertionChar`.  If
    /// `insertionChar` is not specified, `traits_type::eof()` is appended
    /// instead.
    int_type overflow(
                  int_type insertionChar = bsl::streambuf::traits_type::eof())
                                                         BSLS_KEYWORD_OVERRIDE;

    /// 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 or greater then `length()`.
    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 less then zero or greater
    /// than `length()`.
    pos_type seekpos(pos_type                position,
                     bsl::ios_base::openmode which = bsl::ios_base::in
                                                         | bsl::ios_base::out)
                                                         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;

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

    // CREATORS

    /// Create an empty stream buffer.  Optionally specify a `basicAllocator`
    /// used to supply memory.  If `basicAllocator` is 0, the currently
    /// installed default allocator is used.
    explicit
    MemOutStreamBuf(bslma::Allocator *basicAllocator = 0);

    /// Create an empty stream buffer with sufficient initial capacity to
    /// accommodate up to the specified `numElements` characters without
    /// subsequent reallocation.  If `numElements == 0`, an
    /// implementation-defined initial capacity is used.  Optionally specify a
    /// `basicAllocator` used to supply memory.  If `basicAllocator` is 0, the
    /// currently installed default allocator is used.
    explicit
    MemOutStreamBuf(bsl::size_t       numElements,
                    bslma::Allocator *basicAllocator = 0);

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

    // MANIPULATORS

    /// Reserve sufficient internal capacity to store at least the specified
    /// `numCharacters` characters without reallocation.  Note that if the
    /// storage size specified is less than the number of characters already in
    /// the buffer, this method has no effect.
    void reserveCapacity(bsl::size_t numCharacters);

    /// Destroy the contents of this stream buffer, return all allocated memory
    /// to the allocator, and reset the buffer to the default constructed
    /// state.  Note that `length() == 0` holds following a call to this
    /// method.
    void reset();

    // ACCESSORS

    /// Return the current capacity of the buffer managed by this stream
    /// buffer.
    bsl::size_t capacity() const;

    /// Return the address of the non-modifiable character buffer managed by
    /// this stream buffer.
    const char *data() const;

    /// Return the number of valid characters in this stream buffer.
    bsl::size_t length() const;
};

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

                         // ---------------------
                         // class MemOutStreamBuf
                         // ---------------------

// CREATORS
inline
MemOutStreamBuf::MemOutStreamBuf(bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    setp(0, 0);
}

inline
MemOutStreamBuf::MemOutStreamBuf(bsl::size_t       numElements,
                                 bslma::Allocator *basicAllocator)
: d_allocator_p(bslma::Default::allocator(basicAllocator))
{
    setp(0, 0);
    reserveCapacity(numElements == 0
                    ? static_cast<bsl::streamsize>(k_INITIAL_BUFFER_SIZE)
                    : numElements);
}

inline
MemOutStreamBuf::~MemOutStreamBuf()
{
    d_allocator_p->deallocate(pbase());
}

// MANIPULATORS
inline
void MemOutStreamBuf::reset()
{
    d_allocator_p->deallocate(pbase());
    setp(0, 0);
}

// ACCESSORS
inline
bsl::size_t MemOutStreamBuf::capacity() const
{
    return epptr() - pbase();
}

inline
const char *MemOutStreamBuf::data() const
{
    return pbase();
}

inline
bsl::size_t MemOutStreamBuf::length() const
{
    return pptr() - pbase();
}

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