// bslstl_stringbuf.h                                                 -*-C++-*-
#ifndef INCLUDED_BSLSTL_STRINGBUF
#define INCLUDED_BSLSTL_STRINGBUF

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

//@PURPOSE: Provide a C++03-compatible `stringbuf` class.
//
//@CLASSES:
//  bsl::stringbuf: C++03-compatible `stringbuf` class
//  bsl::StringBufContainer: wrapper for `basic_stringbuf`
//
//@CANONICAL_HEADER: bsl_sstream.h
//
//@SEE_ALSO: bslstl_stringstream, bslstl_ostringstream, bslstl_istringstream
//
//@DESCRIPTION: This component is for internal use only.  Please include
// `<bsl_sstream.h>` instead.
//
// This component defines a class template, `bsl::basic_stringbuf`, that
// implements a standard string buffer, providing an unformatted character
// input sequence and an unformatted character output sequence that
// may be initialized or accessed using a string value (see 27.8.2 [stringbuf]
// of the C++11 standard).  This component also defines two standard aliases,
// `bsl::stringbuf` and `bsl::wstringbuf`, that refer to specializations of the
// `bsl::basic_stringbuf` template for `char` and `wchar_t` types,
// respectively.  As with any stream buffer class, `bsl::basic_stringbuf` is
// rarely used directly.  Stream buffers provide low-level unformatted
// input/output.  They are usually plugged into `std::basic_stream` classes to
// provide higher-level formatted input and output via `operator>>` and
// `operator<<`.  `bsl::basic_stringbuf` is used in the
// `bsl::basic_stringstream` family of classes and users should prefer those
// classes over direct use of `bsl::basic_stringbuf`.
//
// `bsl::basic_stringbuf` derives from `std::basic_streambuf` and implements
// the necessary protected virtual methods.  In this way `bsl::basic_stringbuf`
// customizes the behavior of `std::basic_streambuf` to redirect the reading
// and writing of characters to an internally-maintained sequence of characters
// that can be initialized or accessed using a `bsl::basic_string`.  Note that
// although the standard mandates functions that access and modify the
// buffered sequence using a `basic_string`, it does not mandate that a
// `basic_stringbuf` internally store this buffer in a `basic_string`; this
// implementation currently uses a `basic_string` as its internal buffer, but
// that is subject to change without warning.
//
// The `bsl::stringbuf` template has three parameters, `CHAR_TYPE`,
// `CHAR_TRAITS`, and `ALLOCATOR`.  The `CHAR_TYPE` and `CHAR_TRAITS`
// parameters respectively define the character type for the stream buffer and
// a type providing a set of operations the stream buffer will use to
// manipulate characters of that type, which must meet the character traits
// requirements defined by the C++11 standard, 21.2 [char.traits].  The
// `ALLOCATOR` template parameter is described in the "Memory Allocation"
// section below.
//
///Memory Allocation
///-----------------
// The type supplied as a stream buffer's `ALLOCATOR` template parameter
// determines how that stream buffer will allocate memory.  The
// `basic_stringbuf` template supports allocators meeting the requirements
// of the C++11 standard, 17.6.3.5 [allocator.requirements]; in addition, it
// supports scoped-allocators derived from the `bslma::Allocator` memory
// allocation protocol.  Clients intending to use `bslma`-style allocators
// should use `bsl::allocator`, which provides a C++11 standard-compatible
// adapter for a `bslma::Allocator` object.  Note that the standard aliases
// `bsl::stringbuf` and `bsl::wstringbuf` both use `bsl::allocator`.
//
///`bslma`-Style Allocators
/// - - - - - - - - - - - -
// If the type supplied for the `ALLOCATOR` template parameter of a `stringbuf`
// instantiation is `bsl::allocator`, then objects of that stream buffer type
// will conform to the standard behavior of a `bslma`-allocator-enabled type.
// Such a stream buffer accepts an optional `bslma::Allocator` argument at
// construction.  If the address of a `bslma::Allocator` object is explicitly
// supplied at construction, it will be used to supply memory for the stream
// buffer throughout its lifetime; otherwise, the stream buffer will use the
// default allocator installed at the time of the stream buffer's construction
// (see `bslma_default`).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Operations
///- - - - - - - - - - - - - -
// The following example demonstrates the use of `bsl::stringbuf` to read and
// write character data from and to a `bsl::string` object.
//
// Suppose we want to implement a simplified converter from `unsigned int` to
// `bsl::string` and back.  First, we define the prototypes of two conversion
// functions:
// ```
// bsl::string  toString(unsigned int from);
// unsigned int fromString(const bsl::string& from);
// ```
// Then, we use `bsl::stringbuf` to implement the `toString` function.  We
// write all digits into `bsl::stringbuf` individually using `sputc` methods
// and then return the resulting `bsl::string` object:
// ```
// #include <algorithm>
//
// bsl::string toString(unsigned int from)
// {
//     bsl::stringbuf out;
//
//     for (; from != 0; from /= 10) {
//         out.sputc('0' + from % 10);
//     }
//
//     bsl::string result(out.str());
//     std::reverse(result.begin(), result.end());
//     return result;
// }
// ```
// Now, we implement the `fromString` function that converts from
// `bsl::string` to `unsigned int` by using `bsl::stringbuf` to read individual
// digits from the string object:
// ```
// unsigned int fromString(const bsl::string& from)
// {
//     unsigned int result = 0;
//
//     for (bsl::stringbuf in(from); in.in_avail(); ) {
//         result = result * 10 + (in.sbumpc() - '0');
//     }
//
//     return result;
// }
// ```
// Finally, we verify that the result of the round-trip conversion is identical
// to the original value:
// ```
// unsigned int orig   = 92872498;
// unsigned int result = fromString(toString(orig));
//
// assert(orig == result);
// ```

#include <bslscm_version.h>

#include <bslstl_iosfwd.h>
#include <bslstl_string.h>
#include <bslstl_stringview.h>
#include <bslstl_stringviewlikeparam.h>

#include <bslalg_swaputil.h>

#include <bslma_isstdallocator.h>
#include <bslma_bslallocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_enableif.h>
#include <bslmf_issame.h>
#include <bslmf_movableref.h>

#include <bsls_assert.h>
#include <bsls_compilerfeatures.h>
#include <bsls_keyword.h>
#include <bsls_libraryfeatures.h>
#include <bsls_platform.h>

#include <algorithm>
#include <cstddef>
#include <ios>
#include <istream>
#include <ostream>
#include <streambuf>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
# include <utility>
#endif
#include <limits.h>      // for 'INT_MAX', 'INT_MIN'

#ifndef BDE_OMIT_INTERNAL_DEPRECATED
#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#endif // BDE_OMIT_INTERNAL_DEPRECATED

namespace BloombergLP {
// 'BSLS_ASSERT' filename fix -- See {'bsls_assertimputil'}
#ifdef BSLS_ASSERTIMPUTIL_AVOID_STRING_CONSTANTS
extern const char s_bslstl_stringbuf_h[];
#undef BSLS_ASSERTIMPUTIL_FILE
#define BSLS_ASSERTIMPUTIL_FILE BloombergLP::s_bslstl_stringbuf_h
#endif
}

namespace bsl {

using std::ios_base;

                            // =====================
                            // class basic_stringbuf
                            // =====================

/// This class implements a standard stream buffer providing an unformatted
/// character input sequence and an unformatted character output sequence
/// that may be initialized or accessed using a string value.
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
class basic_stringbuf
    : public std::basic_streambuf<CHAR_TYPE, CHAR_TRAITS> {

  private:
    // PRIVATE TYPES
    typedef std::basic_streambuf<CHAR_TYPE, CHAR_TRAITS>         BaseType;
    typedef bsl::basic_string<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR> StringType;
    typedef bsl::basic_string_view<CHAR_TYPE, CHAR_TRAITS>       ViewType;

    typedef BloombergLP::bslmf::MovableRefUtil                   MoveUtil;

  public:
    // TYPES
    typedef CHAR_TYPE                      char_type;
    typedef CHAR_TRAITS                    traits_type;
    typedef ALLOCATOR                      allocator_type;
    typedef typename traits_type::int_type int_type;
    typedef typename traits_type::off_type off_type;
    typedef typename traits_type::pos_type pos_type;

  private:
    // DATA
    StringType         d_str;   // internal character sequence buffer

    off_type           d_endHint;
                                // offset to one past the last known good
                                // character in `d_str`  (Note that to enable
                                // efficient buffering, `d_str` may be resized
                                // beyond the last written character, so
                                // `d_str.size()` may not accurately report
                                // the current length of the character
                                // sequence available for input.  Extending
                                // the size of `d_str` and updating `epptr`
                                // (the end-of-output pointer) allows the
                                // parent stream type to write additional
                                // characters without `overflow`.  However,
                                // care must be taken to refresh the cached
                                // `d_endHint` value as the parent stream will
                                // update the current output position `pptr`,
                                // without calling a method on this type.)

    ios_base::openmode d_mode;  // `stringbuf` open mode (`in`, `out`, or both)

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

  private:
    // PRIVATE MANIPULATORS

    /// Update the input pointers (`eback`, `gptr`, `egptr`) of this string
    /// buffer, setting the beginning of the input sequence, `eback`, to the
    /// address of the first character of the internal string
    /// representation, `d_str`, the current position of the input sequence,
    /// `gptr`, to the specified `currentInputPosition`, and the address
    /// past the end of the accessible sequence, `egptr`, to the last
    /// character in `d_ptr` (`&d_ptr[0] + d_endHint`).  Return the offset
    /// of the current position of the input sequence from the start of the
    /// sequence.  The behavior is undefined unless this buffer is in input
    /// mode and `currentInputPosition` is within the range of accessible
    /// characters in `d_ptr`.
    pos_type updateInputPointers(char_type *currentInputPosition);

    /// Update the output pointers (`pback`, `pptr`, `epptr`) of this string
    /// buffer, setting the beginning of the output sequence, `pback`, to
    /// the address of the first character of the internal string
    /// representation, `d_str`, the current position of the output
    /// sequence, `pptr`, to the specified `currentOutputPosition`, and the
    /// address past the end of the accessible sequence, `epptr`, to one
    /// past the last character in `d_ptr` (`&d_ptr[0] + d_ptr.size()`).
    /// Return the offset of the current position of the output sequence
    /// from the start of the sequence.  The behavior is undefined unless
    /// this buffer is in output mode, and `currentOutputPosition` is within
    /// the range of accessible characters in `d_ptr`.
    pos_type updateOutputPointers(char_type *currentOutputPosition);

    /// Update the input and output positions of this string buffer object
    /// according to the current state of the internal string representation
    /// `d_ptr`.  Optionally specify an `inputOffset` indicating the current
    /// input position's offset from the beginning of the sequence.
    /// Optionally specify an `outputOffset` indicating the current output
    /// position's offset from the beginning of the sequence.  If this
    /// buffer is in output mode, set the beginning of the output sequence,
    /// `pback`, to the address of the first character of `d_ptr`, the
    /// current output position, `pptr`, to `pback + outputOffset`, and the
    /// end of the output sequence, `epptr`, to one past the last character
    /// in `d_str` (`&d_ptr[0] + d_ptr.size()`).  If this buffer is in input
    /// mode, set the beginning of the input sequence, `eback`, to the
    /// address of the first character of `d_ptr`, the current input
    /// position, `gptr`, to `eback + inputOffset`, and the end of the input
    /// sequence, `egptr`, to the last written character in `d_str`
    /// (`&d_ptr[0] + d_endHint`).
    void updateStreamPositions(off_type inputOffset  = 0,
                               off_type outputOffset = 0);

    /// Attempt to expand the sequence of characters available for input
    /// (i.e., update the end of input buffer position, `egptr`) to
    /// incorporate additional characters that may have been written (as
    /// output) to the stream.  Return `true` if the input buffer was
    /// successfully extended, and `false` otherwise.  Note that the input
    /// area as described by `eback`, `gptr`, and `egptr` may become out of
    /// sync with the characters actually available in the buffer as the
    /// parent `basic_streambuf` type may perform writes into the output
    /// area (using `pbase`, `pptr`, and `epptr`) without calling any
    /// methods of this object.
    bool extendInputArea();

    // PRIVATE ACCESSORS

    /// Return the number of characters currently in the buffer.  Note this
    /// may not be `d_str.size()`, as this implementation resizes `d_str`
    /// beyond the number of written characters to provide more efficient
    /// buffering, and it also may not be `d_endHint`, as that value may
    /// currently be stale (as writes may have been performed through the
    /// parent `basic_streambuf` type without calling a method on this
    /// object).
    pos_type streamSize() const;

    /// Return `true` if pointers form a valid range
    /// (`first <= middle <= last`) and `first == d_str.data()` and
    /// `middle` and  `last` are in the range
    /// `[d_str.data() .. d_str.data() + d_str.size()]`, or all arguments
    /// are 0, and `false` otherwise.  Note that this function is called in
    /// defensive (i.e., "DEBUG" or "SAFE") build modes only.
    bool arePointersValid(const char_type *first,
                          const char_type *middle,
                          const char_type *last) const;

  protected:
    // PROTECTED MANIPULATORS

    /// Set the current input position or the current output position (or
    /// both) to the specified `offset` from the specified `whence`
    /// location.  Optionally specify a `modeBitMask` indicating whether to
    /// set the current input position, current output position, or both.
    /// If `whence` is `ios_base::beg`, set the current position to the
    /// indicated `offset` from the beginning of the stream; if `whence` is
    /// `ios_base::end`, set the current position to the indicated `offset`
    /// from the end of the stream; and if `whence` is `ios_base::cur`, set
    /// the current input or output position to the indicated `offset` from
    /// its current position.  If `whence` is `ios_base::cur`, then
    /// `modeBitMask` may be either `ios_base::in` or `ios_base::out`, but
    /// not both.  Return the offset of the new position on success, and
    /// `pos_type(off_type(-1))` otherwise.
    virtual pos_type seekoff(
                off_type           offset,
                ios_base::seekdir  whence,
                ios_base::openmode modeBitMask = ios_base::in | ios_base::out);

    /// Set the current input position or the current output position (or
    /// both) to the specified `absoluteOffset` from the beginning of the
    /// stream.  Optionally specify a `modeBitMask` indicating whether to
    /// set the current input position, current output position, or both.
    /// Return the offset of the new position on success, and
    /// `pos_type(off_type(-1))` otherwise.
    virtual pos_type seekpos(
             pos_type           absoluteOffset,
             ios_base::openmode modeBitMask = ios_base::in | ios_base::out);

    /// Read up to the specified `numCharacters` from this `stringbuf`
    /// object and store them in the specified `result` array.  Return the
    /// number of characters loaded into `result`.  Note that if fewer than
    /// `numCharacters` characters are available in the buffer, all
    /// available characters are loaded into `result`.  The behavior is
    /// undefined unless `result` refers to a contiguous sequence of at
    /// least `numCharacters` characters.
    virtual std::streamsize xsgetn(char_type       *result,
                                   std::streamsize  numCharacters);

    /// Return the character at the current input position, if a character
    /// is available, and `traits_type::eof()` otherwise.   Update the end
    /// of the input area, `egptr`, if additional characters are available
    /// (as may occur if additional characters have been written to the
    /// string buffer).  Note that this operation is similar to `uflow`,
    /// but does not advance the current input position.
    virtual int_type underflow();

    /// Return the character at the current input position and advance the
    /// input position by 1.  If no character is available at the current
    /// input position, return `traits_type::eof()`.   Update the end of the
    /// input area, `egptr`, if additional characters are available (as may
    /// occur if additional characters have been written to the string
    /// buffer).  Note that this operation is similar to `underflow`, but
    /// advances the current input position.
    virtual int_type uflow();

    /// Put back the specified `character` into the input sequence so that
    /// the next character read from the input sequence will be
    /// `character`.  If `character` is either `traits_type::eof()` or is
    /// the same as the previously read character from the input sequence,
    /// then adjust the current input position, `gptr`, back one position.
    /// If `character` is neither `traits_type::eof()` nor the character
    /// previously read from the input sequence, but this string buffer was
    /// opened for writing (`ios_base::out`), then adjust the input
    /// sequence back one position and write `character` to that position.
    /// Return the character that was put back on success and
    /// `traits_type::eof()` if either the input position is currently at
    /// the beginning of the sequence or if the previous character in the
    /// input sequence is not `character` and this buffer was not opened
    /// for writing.
    virtual int_type pbackfail(int_type character = traits_type::eof());

    /// Append the specified `numCharacters` from the specified
    /// `inputString` to the output sequence starting at the current output
    /// position (`pptr`).  Update the current output position of this
    /// string buffer to refer to the last appended character.  Return the
    /// number of characters that were appended.  The behavior is undefined
    /// unless `inputString` refers to a contiguous sequence of at least
    /// `numCharacters` characters.
    virtual std::streamsize xsputn(const char_type *inputString,
                                   std::streamsize  numCharacters);

    /// Append the specified `character` to the output sequence of this
    /// stream buffer at the current output position (`pptr`), and advance
    /// the output position by one.  This operation may update the end of
    /// output area (`epptr`) to allow for additional writes (e.g., by the
    /// base `basic_streambuf` type) to the output sequence without calling
    /// a method on this type.  Return the written character on success, and
    /// `traits_type::eof()` if `character` is `traits_type::eof()` or this
    /// stream buffer was not opened for writing.
    virtual int_type overflow(int_type character = traits_type::eof());

  public:
    // CREATORS

    /// Create a `basic_stringbuf` object.  Optionally specify a
    /// `modeBitMask` indicating whether this buffer may be read from,
    /// written to, or both.  If `modeBitMask` is not supplied, this buffer
    /// is created with `ios_base::in | ios_base::out`.  Optionally specify
    /// an `initialString` indicating the initial sequence of characters
    /// that this buffer will access or manipulate.  If `initialString` is
    /// not supplied, the initial sequence of characters will be empty.
    /// Optionally specify the `allocator` used to supply memory.  If
    /// `allocator` is not supplied, a default-constructed object of the
    /// (template parameter) `ALLOCATOR` type is used.  If the `ALLOCATOR`
    /// argument is of type `bsl::allocator` (the default), then
    /// `allocator`, if supplied, shall be convertible to
    /// `bslma::Allocator *`.  If the `ALLOCATOR` argument is of type
    /// `bsl::allocator` and `allocator` is not supplied, the currently
    /// installed default allocator will be used to supply memory.
    explicit
    basic_stringbuf(const allocator_type& allocator = allocator_type());
    explicit
    basic_stringbuf(ios_base::openmode    modeBitMask,
                    const allocator_type& allocator = allocator_type());
    explicit
    basic_stringbuf(const StringType&     initialString,
                    const allocator_type& allocator = allocator_type());
    basic_stringbuf(const StringType&     initialString,
                    ios_base::openmode    modeBitMask,
                    const allocator_type& allocator = allocator_type());

    /// Create a `basic_stringbuf` object.  Use the specified
    /// `initialString` indicating the initial sequence of characters that
    /// this buffer will access or manipulate.  Optionally specify a
    /// `modeBitMask` indicating whether this buffer may be read from,
    /// written to, or both.  If `modeBitMask` is not supplied, this buffer
    /// is created with `ios_base::in | ios_base::out`.  Optionally specify
    /// the `allocator` used to supply memory.  If `allocator` is not
    /// supplied, the allocator in `initialString` is used.  `initialString`
    /// is left in a valid but unspecified state.
    explicit
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType>
                                                 initialString);
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType>
                                                 initialString,
                    const allocator_type&        allocator);
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType>
                                                 initialString,
                    ios_base::openmode           modeBitMask);
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType>
                                                 initialString,
                    ios_base::openmode           modeBitMask,
                    const allocator_type&        allocator);

    /// Create a `basic_stringbuf` object.  Use the specified
    /// `initialString` indicating the initial sequence of characters that
    /// this buffer will access or manipulate.  Optionally specify the
    /// `allocator` used to supply memory.  If `allocator` is not supplied,
    /// a default-constructed object of the (template parameter) `ALLOCATOR`
    /// type is used.  If the `ALLOCATOR` argument is of type
    /// `bsl::allocator` (the default), then `allocator`, if supplied, shall
    /// be convertible to `bslma::Allocator *`.  If the `ALLOCATOR` argument
    /// is of type `bsl::allocator` and `allocator` is not supplied, the
    /// currently installed default allocator will be used to supply memory.
    template <class SALLOC>
    explicit
    basic_stringbuf(
        const bsl::basic_string<CHAR_TYPE, CHAR_TRAITS, SALLOC>&
                                                  initialString,
        const allocator_type&                     allocator = allocator_type(),
        typename bsl::enable_if<
                    !bsl::is_same<ALLOCATOR, SALLOC>::value, void *>::type = 0)

    : BaseType()
    , d_str(initialString.data(), initialString.size(), allocator)
    , d_endHint(initialString.size())
    , d_mode(ios_base::in | ios_base::out)
    {
        // Note: implemented inline due to Sun CC compilation error.
        updateStreamPositions();
    }

    /// Create a `basic_stringbuf` object.  Use the specified
    /// `initialString` indicating the initial sequence of characters that
    /// this buffer will access or manipulate.  Use the specified
    /// `modeBitMask` to indicate whether this buffer may be read from,
    /// written to, or both.  Optionally specify the `allocator` used to
    /// supply memory.  If `allocator` is not supplied, a
    /// default-constructed object of the (template parameter) `allocator_type`
    /// is used.  If the `ALLOCATOR` argument is of type `bsl::allocator` (the
    /// default), then `allocator`, if supplied, shall be convertible to
    /// `bslma::Allocator *`.  If the `ALLOCATOR` argument is of type
    /// `bsl::allocator` and `allocator` is not supplied, the currently
    /// installed default allocator will be used to supply memory.
    template <class SALLOC>
    basic_stringbuf(
        const bsl::basic_string<CHAR_TYPE, CHAR_TRAITS, SALLOC>&
                                                  initialString,
        ios_base::openmode                        modeBitMask,
        const allocator_type&                     allocator = allocator_type(),
        typename bsl::enable_if<
                    !bsl::is_same<ALLOCATOR, SALLOC>::value, void *>::type = 0)
    : BaseType()
    , d_str(initialString.data(), initialString.size(), allocator)
    , d_endHint(initialString.size())
    , d_mode(modeBitMask)
    {
        // Note: implemented inline due to Sun CC compilation error.
        updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
    }

    /// Create a `basic_stringbuf` object.  Use the specified `initialString`
    /// (of a type convertible to `basic_string_view<CHAR_TYPE, CHAR_TRAITS>`
    /// but not to `const CHAR_TYPE *`) indicating the initial sequence of
    /// characters that this buffer will access or manipulate.  Optionally
    /// specify the `allocator` used to supply memory.  If `allocator` is not
    /// supplied, a default-constructed object of the (template parameter)
    /// `ALLOCATOR` type is used.  If the `ALLOCATOR` argument is of type
    /// `bsl::allocator` (the default), then `allocator`, if supplied, shall be
    /// convertible to `bslma::Allocator *`.  If the `ALLOCATOR` argument is of
    /// type `bsl::allocator` and `allocator` is not supplied, the currently
    /// installed default allocator will be used to supply to supply memory.
    /// This buffer is created with `ios_base::in | ios_base::out`.
    template <class STRING_VIEW_LIKE_TYPE>
    explicit
    basic_stringbuf(
        const STRING_VIEW_LIKE_TYPE&              initialString,
        BSLSTL_STRINGVIEWLIKEPARAM_ONLY_ENABLE_IF_T(const allocator_type&)
                                                  allocator = allocator_type())
    : BaseType()
    , d_str(ViewType(initialString).data(),
            ViewType(initialString).size(),
            allocator)
    , d_endHint(ViewType(initialString).size())
    , d_mode(ios_base::in | ios_base::out)
    {
        // Note: implemented inline due to Sun CC compilation error.
        updateStreamPositions();
    }

    /// Create a `basic_stringbuf` object.  Use the specified `initialString`
    /// (of a type convertible to `basic_string_view<CHAR_TYPE, CHAR_TRAITS>`
    /// but not to `const CHAR_TYPE *`) indicating the initial sequence of
    /// characters that this buffer will access or manipulate.  Use the
    /// specified `modeBitMask` to indicate whether this buffer may be read
    /// from, written to, or both.  Optionally specify the `allocator` used to
    /// supply memory.  If `allocator` is not supplied, a default-constructed
    /// object of `allocator_type` is used.  If the `ALLOCATOR` argument is of
    /// type bsl::allocator` (the default), then `allocator`, if supplied,
    /// shall be convertible to `bslma::Allocator *`.  If the `ALLOCATOR`
    /// argument is of type `bsl::allocator` and `allocator` is not
    /// supplied, the currently installed default allocator will be used
    /// to supply memory.
    template <class STRING_VIEW_LIKE_TYPE>
    basic_stringbuf(const STRING_VIEW_LIKE_TYPE &initialString,
                    ios_base::openmode modeBitMask,
                    BSLSTL_STRINGVIEWLIKEPARAM_ONLY_ENABLE_IF_T(
                        const allocator_type &) allocator = allocator_type())
    : BaseType(),
    d_str(ViewType(initialString).data(),
          ViewType(initialString).size(),
          allocator)
    , d_endHint(ViewType(initialString).size())
    , d_mode(modeBitMask)
    {
        // Note: implemented inline due to Sun CC compilation error.
        updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
    }

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
    /// Create a `basic_stringbuf` object having the same value as the
    /// specified `original` object by moving the contents of `original` to
    /// the newly-created object.  Optionally specify the `allocator` used
    /// to supply memory.  `original` is left in a valid but unspecified
    /// state.
    // NOLINTNEXTLINE(performance-noexcept-move-constructor)
    basic_stringbuf(basic_stringbuf&&     original);
    basic_stringbuf(basic_stringbuf&&     original,
                    const allocator_type& allocator);
#endif

    /// Destroy this object.
    ~basic_stringbuf();

    // MANIPULATORS

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
    /// Assign to this object the value of the specified `rhs`, and return a
    /// reference providing modifiable access to this object.  The contents
    /// of `rhs` are move-assigned to this object.  `rhs` is left in a valid
    /// but unspecified state.
    // NOLINTNEXTLINE(performance-noexcept-move-constructor)
    basic_stringbuf& operator=(basic_stringbuf&& rhs);
#endif

    /// Reset the internally buffered sequence of characters to the
    /// specified `value`.  Update the beginning and end of both the input
    /// and output sequences to be the beginning and end of the updated
    /// buffer, update the current input position to be the beginning of the
    /// updated buffer, and update the current output position to be the end
    /// of the updated buffer.  If `value` is passed by `MovableRef`, then
    /// it is left in an unspecified but valid state.
    void str(const StringType& value);
    void str(BloombergLP::bslmf::MovableRef<StringType> value);
    template <class SALLOC>
    typename
        bsl::enable_if<!bsl::is_same<ALLOCATOR, SALLOC>::value, void>::type
    str(const basic_string<CHAR_TYPE, CHAR_TRAITS, SALLOC>& value)
    {
        // Note: implemented inline due to Sun CC compilation error.
        d_str.assign(value.data(), value.size());
        d_endHint = d_str.size();
        updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
    }

    /// Reset the internally buffered sequence of characters to the
    /// specified `value` (of a type convertible to
    /// `basic_string_view<CHAR_TYPE, CHAR_TRAITS>` but not to
    /// `const CHAR_TYPE *`).
    template <class STRING_VIEW_LIKE_TYPE>
    BSLSTL_STRINGVIEWLIKEPARAM_ONLY_ENABLE_IF_T(void)
    str(const STRING_VIEW_LIKE_TYPE& value)
    {
        // Note: implemented inline due to Sun CC compilation error.
        ViewType sv = value;
        d_str.assign(sv.data(), sv.size());
        d_endHint = d_str.size();
        updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
    }

#ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS
    /// Return the currently buffered sequence of characters.  If this
    /// object was created only in input mode, the resultant `StringType`
    /// contains the character sequence in the range `[eback(), egptr())`.
    /// If `modeBitMask & ios_base::out` specified at construction is
    /// nonzero then the resultant `StringType` contains the character
    /// sequence in the range `[pbase(), high_mark)`, where `high_mark`
    /// represents the position one past the highest initialized character
    /// in the buffer.  Otherwise this object has been created in neither
    /// input nor output mode and a zero length `StringType` is returned.
    /// This object is left in an empty state.
    StringType str() &&;
#endif

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    /// Efficiently exchange the value of this object with the value of the
    /// specified `other` object.  This method provides the no-throw
    /// exception-safety guarantee if `*this` and `other` allocators compare
    /// equal.  The behavior is undefined unless either `*this` and `other`
    /// allocators compare equal or `propagate_on_container_swap` is `true`.
    /// Note that this function is only available for C++11 (and later)
    /// language standards because it requires that `swap` be provided on
    /// the (platform supplied) base class for this type.
    // NOLINTNEXTLINE(performance-noexcept-swap)
    void swap(basic_stringbuf& other);
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

    // ACCESSORS

    /// Return the allocator used by the underlying string to supply memory.
    allocator_type get_allocator() const BSLS_KEYWORD_NOEXCEPT;

    /// Return the currently buffered sequence of characters.  If this
    /// object was created only in input mode, the resultant `StringType`
    /// contains the character sequence in the range `[eback(), egptr())`.
    /// If `modeBitMask & ios_base::out` specified at construction is
    /// nonzero then the resultant `StringType` contains the character
    /// sequence in the range `[pbase(), high_mark)`, where `high_mark`
    /// represents the position one past the highest initialized character
    /// in the buffer.  Otherwise this object has been created in neither
    /// input nor output mode and a zero length `StringType` is returned.
#ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS
    StringType str() const &;
#else
    StringType str() const;
#endif

#ifndef BSLS_PLATFORM_CMP_SUN
    // To be enabled once DRQS 168075157 is resolved

    /// Return the currently buffered sequence of characters in a
    /// `basic_string` that uses the specified `allocator`.  If this object
    /// was created only in input mode, the resultant `basic_string`
    /// contains the character sequence in the range `[eback(), egptr())`.
    /// If `modeBitMask & ios_base::out` specified at construction is
    /// nonzero then the resultant `basic_string` contains the character
    /// sequence in the range `[pbase(), high_mark)`, where `high_mark`
    /// represents the position one past the highest initialized character
    /// in the buffer.  Otherwise this object has been created in neither
    /// input nor output mode and a zero length `basic_string` is returned.
    template <class SALLOC>
    typename bsl::enable_if<
        bsl::IsStdAllocator<SALLOC>::value,
        basic_string<CHAR_TYPE, CHAR_TRAITS, SALLOC> >::type
    str(const SALLOC& allocator) const
    {
        // Note: implemented inline due to Sun CC compilation error.
        return basic_string<CHAR_TYPE, CHAR_TRAITS, SALLOC>(view(), allocator);
    }
#endif

    /// Return a `string_view` containing the currently buffered sequence of
    /// characters.  If this object was created only in input mode, the
    /// resultant `ViewType` contains the character sequence in the range
    /// `[eback(), egptr())`.  If `modeBitMask & ios_base::out` specified at
    /// construction is nonzero then the resultant `StringType` contains the
    /// character sequence in the range `[pbase(), high_mark)`, where
    /// `high_mark` represents the position one past the highest initialized
    /// character in the buffer.  Otherwise this object has been created in
    /// neither input nor output mode and a zero length `ViewType` is
    /// returned.
    ViewType view() const BSLS_KEYWORD_NOEXCEPT;
};

// FREE FUNCTIONS
#if defined(BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY)                  \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE)
/// Efficiently exchange the values of the specified `a` and `b` objects.
/// This method provides the no-throw exception-safety guarantee if `a` and
/// `b` allocators compare equal.  Note that this function is only available
/// for C++11 (and later) language standards.
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
void swap(basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>& a,
          basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>& b);
#endif

// STANDARD TYPEDEFS
typedef basic_stringbuf<char, char_traits<char>, allocator<char> >   stringbuf;
typedef basic_stringbuf<wchar_t, char_traits<wchar_t>, allocator<wchar_t> >
                                                                    wstringbuf;

                      // =========================
                      // struct StringBufContainer
                      // =========================

/// This class enables the implementation of string-stream types by
/// providing a trivial type containing a `basic_stringbuf` that is suitable
/// as a (`private`) base class for a string-stream.  Inheriting from
/// `StringBufContainer` allows the string-stream to ensure that the
/// contained `basic_stringbuf` is initialized before initializing other
/// base classes or data members without potentially overriding `virtual`
/// methods in the `basic_stringbuf` type.  Note that implementations of
/// string-stream types must pass the address of a string-buffer to their
/// `public` base class (e.g., `basic_stream`), so the string-stream must
/// ensure (using `private` inheritance) that the string-buffer is
/// initialized before constructing the `public` base class.  If a
/// string-stream implementation were to directly inherit from
/// `basic_streambuf`, then `virtual` methods defined in that string-stream
/// (e.g., `underflow`) might incorrectly override those in the
/// `basic_stringbuf` implementation.
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
class StringBufContainer {

  private:
    // PRIVATE TYPES
    typedef basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>   StreamBufType;
    typedef bsl::basic_string<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR> StringType;

    typedef BloombergLP::bslmf::MovableRefUtil                   MoveUtil;

    // DATA
    StreamBufType d_bufObj;  // contained 'basic_stringbuf'

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

  public:
    // CREATORS
    explicit
    StringBufContainer(const ALLOCATOR& allocator)
    : d_bufObj(allocator)
    {
    }

    StringBufContainer(ios_base::openmode modeBitMask,
                       const ALLOCATOR&   allocator)
    : d_bufObj(modeBitMask, allocator)
    {
    }

    StringBufContainer(const StringType& initialString,
                       const ALLOCATOR&  allocator)
    : d_bufObj(initialString, allocator)
    {
    }

    StringBufContainer(const StringType&  initialString,
                       ios_base::openmode modeBitMask,
                       const ALLOCATOR&   allocator)
    : d_bufObj(initialString, modeBitMask, allocator)
    {
    }

    StringBufContainer(
                      BloombergLP::bslmf::MovableRef<StringType> initialString,
                      ios_base::openmode                         modeBitMask,
                      const ALLOCATOR&                           allocator)
    : d_bufObj(MoveUtil::move(initialString), modeBitMask, allocator)
    {
    }

    StringBufContainer(
                      BloombergLP::bslmf::MovableRef<StringType> initialString,
                      ios_base::openmode                         modeBitMask)
    : d_bufObj(MoveUtil::move(initialString), modeBitMask)
    {
    }

    template <class STRING_ITER>
    StringBufContainer(STRING_ITER        first,
                       STRING_ITER        last,
                       ios_base::openmode modeBitMask,
                       const ALLOCATOR&   allocator)
    : d_bufObj(modeBitMask, allocator)
    {
        StringType tempStr(first, last, allocator);
        d_bufObj.str(MoveUtil::move(tempStr));
    }

    /// Create a `StringBufContainer` object.  Use the specified
    /// `initialString` (of a type convertible to
    /// `basic_string_view<CHAR_TYPE, CHAR_TRAITS>` but not to
    /// `const CHAR_TYPE *`) indicating the initial sequence of characters
    /// that this buffer will access or manipulate.  Use the specified
    /// `modeBitMask` to indicate whether this buffer may be read from,
    /// written to, or both.  Use the specified `allocator` to supply
    /// memory.
    template <class STRING_VIEW_LIKE_TYPE>
    StringBufContainer(
        const STRING_VIEW_LIKE_TYPE&  initialString,
        ios_base::openmode            modeBitMask,
        BSLSTL_STRINGVIEWLIKEPARAM_ONLY_ENABLE_IF_T(const ALLOCATOR&)
                                                             allocator)
    : d_bufObj(initialString, modeBitMask, allocator)
    {
    }

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
    /// Create a `StringBufContainer` object having the same value as the
    /// specified `original` object by moving the contents of `original` to
    /// the newly-created object.  `original` is left in a valid but
    /// unspecified state.
    // NOLINTNEXTLINE(performance-noexcept-move-constructor)
    StringBufContainer(StringBufContainer&& original)
    : d_bufObj(std::move(original.d_bufObj))
    {
    }

    /// Create a `StringBufContainer` object using the specified `allocator` to
    /// supply memory and having the same value as the specified `original`
    /// object by moving the contents of `original` to the newly-created object.
    /// `original` is left in a valid but unspecified state.
    StringBufContainer(StringBufContainer&& original,
                       const ALLOCATOR&     allocator)
    : d_bufObj(std::move(original.d_bufObj), allocator)
    {
    }
#endif

    //! ~StringBufContainer() = default;

    // MANIPULATORS

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
    /// Assign to this object the value of the specified `rhs`, and return a
    /// reference providing modifiable access to this object.  The contents
    /// of `rhs` are move-assigned to this object.  `rhs` is left in a valid
    /// but unspecified state.
    // NOLINTNEXTLINE(performance-noexcept-move-constructor)
    StringBufContainer& operator=(StringBufContainer&& rhs)
    {
        d_bufObj = std::move(rhs.d_bufObj);

        return *this;
    }
#endif

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    /// Efficiently exchange the value of this object with the value of the
    /// specified `other` object.
    // NOLINTNEXTLINE(performance-noexcept-swap)
    void swap(StringBufContainer& other)
    {
        d_bufObj.swap(other.d_bufObj);
    }
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

    // ACCESSORS
    StreamBufType *rdbuf() const
    {
        return const_cast<StreamBufType *>(&d_bufObj);
    }
};

// ============================================================================
//                       TEMPLATE FUNCTION DEFINITIONS
// ============================================================================

                            // ---------------------
                            // class basic_stringbuf
                            // ---------------------

// PRIVATE MANIPULATORS
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::pos_type
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
                           updateInputPointers(char_type *currentInputPosition)
{
    BSLS_ASSERT(d_mode & ios_base::in);
    BSLS_ASSERT(&d_str[0] <= currentInputPosition);
    BSLS_ASSERT(currentInputPosition <=
                        &d_str[0] + static_cast<std::ptrdiff_t>(streamSize()));

    char_type *dataPtr = &d_str[0];

    this->setg(dataPtr,
               currentInputPosition,
               dataPtr + static_cast<std::ptrdiff_t>(streamSize()));
    return pos_type(currentInputPosition - dataPtr);
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::pos_type
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
                         updateOutputPointers(char_type *currentOutputPosition)
{
    BSLS_ASSERT(d_mode & ios_base::out);
    BSLS_ASSERT(&d_str[0] <= currentOutputPosition);
    BSLS_ASSERT(currentOutputPosition <= &d_str[0] + d_str.size());

    char_type   *dataPtr  = &d_str[0];
    std::size_t  dataSize =  d_str.size();

    pos_type outputPos = currentOutputPosition - dataPtr;
    this->setp(dataPtr, dataPtr + dataSize);
    pos_type bumpAmount = outputPos;
    while (bumpAmount > INT_MAX) {
        this->pbump(INT_MAX);
        bumpAmount -= INT_MAX;
    }
    if (bumpAmount) {
        this->pbump(int(bumpAmount));
    }
    return outputPos;
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
void basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    updateStreamPositions(off_type inputOffset, off_type outputOffset)
{
    // Extend the internal buffer to the full capacity of the string to allow
    // us to use the full capacity for buffering output.

    d_str.resize(d_str.capacity());
    char_type *dataPtr = &d_str[0];

    if (d_mode & ios_base::out) {
        // Update the output position.

        std::size_t dataSize = d_str.size();
        this->setp(dataPtr, dataPtr + dataSize);
        off_type bumpAmount = outputOffset;
        while (bumpAmount < INT_MIN) {
            this->pbump(INT_MIN);
            bumpAmount -= INT_MIN;
        }
        while (bumpAmount > INT_MAX) {
            this->pbump(INT_MAX);
            bumpAmount -= INT_MAX;
        }
        if (bumpAmount) {
            this->pbump(int(bumpAmount));
        }
    }

    if (d_mode & ios_base::in) {
        // Update the input position.

        this->setg(dataPtr,
                   dataPtr + inputOffset,
                   dataPtr + static_cast<std::ptrdiff_t>(streamSize()));
    }
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
bool basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::extendInputArea()
{
    // Try to extend into written buffer.

    if (d_mode & ios_base::out && this->pptr() > this->egptr()) {
        d_endHint = streamSize();
        updateInputPointers(this->gptr());
        return true;                                                  // RETURN
    }

    return false;
}

// PRIVATE ACCESSORS
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::pos_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::streamSize() const
{
    pos_type size = std::max<off_type>(d_endHint,
                                       this->pptr() - this->pbase());

    BSLS_ASSERT(size <= off_type(d_str.size()));

    return size;
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
bool basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::arePointersValid(
                                                  const char_type *first,
                                                  const char_type *middle,
                                                  const char_type *last) const
{
    const bool isNull            = first == 0;
    const char_type *bufferBegin = isNull ? 0 : d_str.data();
    const char_type *bufferEnd   = isNull ? 0 : d_str.data() + d_str.size();
    return first  == bufferBegin
        && last   <= bufferEnd
        && first  <= middle
        && middle <= last;
}

// PROTECTED MANIPULATORS
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::pos_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::seekoff(
                                               off_type           offset,
                                               ios_base::seekdir  whence,
                                               ios_base::openmode modeBitMask)
{
    // If `whence` is `ios_base::cur` (the current position), `modeBitMask`
    // may not be both input and output mode.

    if (((modeBitMask & (ios_base::in | ios_base::out)) ==
                        (ios_base::in | ios_base::out))
        && whence == ios_base::cur) {
        return pos_type(off_type(-1));                                // RETURN
    }

    pos_type newPos = pos_type(off_type(-1));

    // Set the current input position.

    if ((modeBitMask & ios_base::in) && (d_mode & ios_base::in)) {
        char_type *inputPtr = 0;

        switch (whence) {
          case ios_base::beg: {
            inputPtr = this->eback() + offset;
          } break;
          case ios_base::cur: {
            inputPtr = this->gptr() + offset;
          } break;
          case ios_base::end: {
            inputPtr = this->eback()
                       + static_cast<std::ptrdiff_t>(streamSize())
                       + offset;
          } break;
          default: {
            BSLS_ASSERT_OPT_UNREACHABLE("Invalid seekdir argument");
          }
        }

        if (inputPtr < this->eback()
         || inputPtr >
                   this->eback() + static_cast<std::ptrdiff_t>(streamSize())) {
            // 'inputPtr' is outside the valid range of the string buffer.

            return pos_type(off_type(-1));                            // RETURN
        }

        newPos = updateInputPointers(inputPtr);
    }

    // Set the current output position.

    if ((modeBitMask & ios_base::out) && (d_mode & ios_base::out)) {
        char_type *outputPtr = 0;

        switch (whence) {
          case ios_base::beg: {
            outputPtr = this->pbase() + offset;
          } break;
          case ios_base::cur: {
            outputPtr = this->pptr() + offset;
          } break;
          case ios_base::end: {
            outputPtr = this->pbase()
                      + static_cast<std::ptrdiff_t>(streamSize())
                      + offset;
          } break;
          default: {
            BSLS_ASSERT_OPT_UNREACHABLE("Invalid seekdir argument");
          }
        }

        if (outputPtr < this->pbase()
         || outputPtr >
                   this->pbase() + static_cast<std::ptrdiff_t>(streamSize())) {
            // 'outputPtr' is outside the valid range of the string buffer.

            return pos_type(off_type(-1));                            // RETURN
        }

        newPos = updateOutputPointers(outputPtr);
    }

    return newPos;
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::pos_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::seekpos(
                                             pos_type           absoluteOffset,
                                             ios_base::openmode modeBitMask)
{
    return basic_stringbuf::seekoff(off_type(absoluteOffset),
                                    ios_base::beg,
                                    modeBitMask);
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
std::streamsize
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::xsgetn(
                                                char_type       *result,
                                                std::streamsize  numCharacters)
{
    if ((d_mode & ios_base::in) == 0) {
        return 0;                                                     // RETURN
    }

    // Additional characters may become available for reading when the input
    // area is extended to account for any characters newly written to the
    // output sequence.
    extendInputArea();

    if (this->gptr() != this->egptr()) {
        // There are characters available in this buffer.

        std::streamsize available = this->egptr() - this->gptr();
        std::streamsize readChars = std::min(available,
                                             numCharacters);

        traits_type::copy(result, this->gptr(), std::size_t(readChars));
        this->gbump(int(readChars));

        return readChars;                                             // RETURN
    }

    return 0;
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::int_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::underflow()
{
    if ((d_mode & ios_base::in) == 0) {
        return traits_type::eof();                                    // RETURN
    }

    if (this->gptr() != this->egptr()) {
        // There are characters available in this buffer.

        return traits_type::to_int_type(*this->gptr());               // RETURN
    }

    if (extendInputArea()) {
        // Additional characters may become available after the input area is
        // extended.

        return this->basic_stringbuf::underflow();                    // RETURN
    }

    return traits_type::eof();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::int_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::uflow()
{
    if ((d_mode & ios_base::in) == 0) {
        return traits_type::eof();                                    // RETURN
    }

    if (this->gptr() != this->egptr()) {
        // There are characters available in this buffer.

        int_type c = traits_type::to_int_type(*this->gptr());
        this->gbump(1);
        return c;                                                     // RETURN
    }

    if (extendInputArea()) {
        // Additional characters may become available after the input area is
        // extended.

        return this->basic_stringbuf::uflow();                        // RETURN
    }

    return traits_type::eof();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::int_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::pbackfail(
                                                            int_type character)
{
    if (this->gptr() == this->eback()) {
        // The current position is at the start of the buffer, so we cannot
        // push back a character.

        return traits_type::eof();                                    // RETURN
    }

    if (traits_type::eq_int_type(character, traits_type::eof())
     || traits_type::eq_int_type(
                              character,
                              traits_type::to_int_type(*(this->gptr() - 1)))) {
        // If 'character' is 'eof' or the previous input character,
        // simply move the current position back 1.

        this->gbump(-1);
        return traits_type::to_int_type(*this->gptr());               // RETURN
    }

    if (d_mode & ios_base::out) {
        // In output mode, if 'character' is not the previous input character,
        // overwrite the previous input character.

        this->gbump(-1);
        *this->gptr() = traits_type::to_char_type(character);
        return character;                                             // RETURN
    }

    return traits_type::eof();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
std::streamsize
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::xsputn(
                                                const char_type *inputString,
                                                std::streamsize  numCharacters)
{
    if ((d_mode & ios_base::out) == 0) {
        return 0;                                                     // RETURN
    }
    BSLS_ASSERT(this->pptr());
    BSLS_ASSERT(this->pbase());

    // Compute the space required.

    std::streamsize spaceLeft  = d_str.data() + d_str.size() - this->pptr();
    std::ptrdiff_t toOverwrite =
                            std::ptrdiff_t(std::min(spaceLeft, numCharacters));

    // Append the portion of 'inputString' that can be written without
    // resizing 'd_ptr'.

    traits_type::copy(this->pptr(), inputString, toOverwrite);

    off_type inputOffset = this->gptr() - this->eback();

    if (numCharacters == toOverwrite) {
        // If all of 'inputString' has been written, just update the stream
        // positions.

        off_type newHigh = numCharacters + this->pptr() - this->pbase();
        d_endHint        = std::max(d_endHint, newHigh);

        updateStreamPositions(inputOffset, newHigh);
    }
    else {
        // If some characters remain to be written, append them to 'd_str'
        // (resizing 'd_str' in the process).

        d_str.append(inputString + toOverwrite, inputString + numCharacters);

        // Update the last written character cache, and the input stream
        // positions.

        d_endHint = d_str.size();
        updateStreamPositions(inputOffset, d_endHint);
    }

    return numCharacters;
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::int_type
    basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::overflow(
                                                            int_type character)
{
    if ((d_mode & ios_base::out) == 0) {
        return traits_type::eof();                                    // RETURN
    }

    if (traits_type::eq_int_type(character, traits_type::eof())) {
        // Nothing to write, so just return success.

        return traits_type::not_eof(character);                       // RETURN
    }

    char_type c = traits_type::to_char_type(character);
    if (this->pptr() != this->epptr()) {
        // Additional space is available in 'd_str', so no need to resize the
        // buffer.

        *this->pptr() = c;
        this->pbump(1);

        d_endHint = streamSize();
    }
    else {
        // Store the input offset so it can be used to restore the input and
        // output positions after the next resize.

        off_type inputOffset = this->gptr() - this->eback();

        // Append the character, and expand the buffer.

        d_str.push_back(c);

        // Update the input sequence, restoring the current input position
        // from 'inputOffset', and updating the output sequence to reflect the
        // newly resized buffer.

        d_endHint = d_str.size();
        updateStreamPositions(inputOffset, d_endHint);
    }

    return character;
}

// CREATORS
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(const allocator_type& allocator)
: BaseType()
, d_str(allocator)
, d_endHint(0)
, d_mode(ios_base::in | ios_base::out)
{
    updateStreamPositions();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(ios_base::openmode    modeBitMask,
                    const allocator_type& allocator)
: BaseType()
, d_str(allocator)
, d_endHint(0)
, d_mode(modeBitMask)
{
    updateStreamPositions();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(const StringType&     initialString,
                    const allocator_type& allocator)
: BaseType()
, d_str(initialString, allocator)
, d_endHint(initialString.size())
, d_mode(ios_base::in | ios_base::out)
{
    updateStreamPositions();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(const StringType&     initialString,
                    ios_base::openmode    modeBitMask,
                    const allocator_type& allocator)
: BaseType()
, d_str(initialString, allocator)
, d_endHint(initialString.size())
, d_mode(modeBitMask)
{
    updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType> initialString)
: BaseType()
, d_str(MoveUtil::move(initialString))
, d_endHint(d_str.size())
, d_mode(ios_base::in | ios_base::out)
{
    updateStreamPositions();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType> initialString,
                    const allocator_type&                      allocator)
: BaseType()
, d_str(MoveUtil::move(initialString), allocator)
, d_endHint(d_str.size())
, d_mode(ios_base::in | ios_base::out)
{
    updateStreamPositions();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType> initialString,
                    ios_base::openmode                         modeBitMask)
: BaseType()
, d_str(MoveUtil::move(initialString))
, d_endHint(d_str.size())
, d_mode(modeBitMask)
{
    updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(BloombergLP::bslmf::MovableRef<StringType> initialString,
                    ios_base::openmode                         modeBitMask,
                    const allocator_type&                      allocator)
: BaseType()
, d_str(MoveUtil::move(initialString), allocator)
, d_endHint(d_str.size())
, d_mode(modeBitMask)
{
    updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(basic_stringbuf&& original)
: BaseType()
, d_str(std::move(original.d_str))
, d_endHint(std::move(original.d_endHint))
, d_mode(std::move(original.d_mode))
{
    // Capture the positions for later restoration

    const off_type  inputOffset = original.gptr() - original.eback();
    const off_type outputOffset = original.pptr() - original.pbase();
    updateStreamPositions(inputOffset, outputOffset);

    this->pubimbue(original.getloc());

    if (original.d_endHint > 0 &&
        static_cast<size_t>(original.d_endHint) > original.d_str.size()) {

        // The move has moved away the string

        original.d_endHint = 0;
        original.updateStreamPositions();
    }
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    basic_stringbuf(basic_stringbuf&&     original,
                    const allocator_type& allocator)
: BaseType()
, d_str(std::move(original.d_str), allocator)
, d_endHint(std::move(original.d_endHint))
, d_mode(std::move(original.d_mode))
{
    // Capture the positions for later restoration

    const off_type inputOffset  = original.gptr() - original.eback();
    const off_type outputOffset = original.pptr() - original.pbase();
    updateStreamPositions(inputOffset, outputOffset);

    this->pubimbue(original.getloc());

    if (original.d_endHint > 0 &&
        static_cast<size_t>(original.d_endHint) > original.d_str.size()) {

        // The move has moved away the string

        original.d_endHint = 0;
        original.updateStreamPositions();
    }
}
#endif

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    ~basic_stringbuf()
{
    if (d_mode & ios_base::in) {
        BSLS_ASSERT(arePointersValid(this->eback(),
                                     this->gptr(),
                                     this->egptr()));
    }

    if (d_mode & ios_base::out) {
        BSLS_ASSERT(arePointersValid(this->pbase(),
                                     this->pptr(),
                                     this->epptr()));
    }
}

// MANIPULATORS
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>&
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::
    operator=(basic_stringbuf&& rhs)
{
    // Capture the positions for later restoration

    const off_type inputOffset  = rhs.gptr()  - rhs.eback();
    const off_type inputSize    = rhs.egptr() - rhs.eback();
    const off_type outputOffset = rhs.pptr()  - rhs.pbase();
    const off_type outputSize   = rhs.epptr() - rhs.pbase();

    this->pubimbue(rhs.getloc());

    d_str     = std::move(rhs.d_str);
    d_endHint = std::move(rhs.d_endHint);
    d_mode    = std::move(rhs.d_mode);

    // Fix the stream-position pointers

    char_type *dataPtr = &d_str[0];

    // Update positions/pointers in the moved-to object

    this->setp(dataPtr, dataPtr + outputSize);
    this->pbump(static_cast<int>(outputOffset));
    this->setg(dataPtr,
               dataPtr + inputOffset,
               dataPtr + inputSize);

    // Reset positions/pointers in the moved-from object

    if (rhs.d_endHint > 0 &&
        static_cast<size_t>(rhs.d_endHint) > rhs.d_str.size()) {

        // The move has moved away the string

        rhs.d_endHint = 0;
        rhs.updateStreamPositions();
    }

    return *this;
}
#endif

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
void basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::str(
                                                       const StringType& value)
{
    d_str     = value;
    d_endHint = d_str.size();
    updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
void basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::str(
                              BloombergLP::bslmf::MovableRef<StringType> value)
{
    StringType& lvalue = value;

    d_str     = MoveUtil::move(lvalue);
    d_endHint = d_str.size();
    updateStreamPositions(0, d_mode & ios_base::ate ? d_endHint : 0);
}

#ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::StringType
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::str() &&
{
    if (d_mode & ios_base::out) {
        d_str.resize(static_cast<typename StringType::size_type>(streamSize()));
        this->setp(d_str.data(), d_str.data());
    }
    else if (d_mode & ios_base::in) {
        if (streamSize() > 0) {
            if (this->eback() != d_str.data()) {
                d_str.erase(0, this->eback() - d_str.data());
            }
            d_str.resize(static_cast<typename StringType::size_type>(
                                                                streamSize()));
            this->setg(d_str.data(), d_str.data(), d_str.data());
        }
    }

    StringType ret = std::move(d_str);
    d_endHint = 0;
    updateStreamPositions();
    return ret;
}
#endif

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
void basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::swap(
                                                        basic_stringbuf& other)
{
    BSLS_ASSERT(
         bsl::allocator_traits<ALLOCATOR>::propagate_on_container_swap::value
      || d_str.get_allocator() == other.d_str.get_allocator());
    // Capture the positions for the later restoration.  Formally,
    // 'std::basic_streambuf::swap' exchanges the internal pointers and the
    // locale object.  But 'bsl::string' swapping can invalidate pointers, so
    // we need to control this process manually.

    const off_type    inputOffset       = this->gptr() - this->eback();
    const off_type    outputOffset      = this->pptr() - this->pbase();
    const std::locale loc               = this->getloc();

    const off_type    otherInputOffset  = other.gptr() - other.eback();
    const off_type    otherOutputOffset = other.pptr() - other.pbase();
    const std::locale otherLoc          = other.getloc();

    // Parent method invocation.

    this->BaseType::swap(other);

    // Swapping data members.

    this->pubimbue(otherLoc);
    other.pubimbue(loc);

    d_str.swap(other.d_str);
    BloombergLP::bslalg::SwapUtil::swap(&this->d_endHint, &other.d_endHint);
    BloombergLP::bslalg::SwapUtil::swap(&this->d_mode,    &other.d_mode);

    // Fix the stream-position pointers.

    this->updateStreamPositions(otherInputOffset, otherOutputOffset);
    other.updateStreamPositions(     inputOffset,      outputOffset);
}
#endif  // BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY

// ACCESSORS
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
typename basic_stringbuf<CHAR_TYPE,CHAR_TRAITS,ALLOCATOR>::allocator_type
basic_stringbuf<CHAR_TYPE,CHAR_TRAITS,ALLOCATOR>::get_allocator() const
                                                          BSLS_KEYWORD_NOEXCEPT
{
    return d_str.get_allocator();
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::StringType
#ifdef BSLS_COMPILERFEATURES_SUPPORT_REF_QUALIFIERS
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::str() const &
#else
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::str() const
#endif
{
    return StringType(view());
}

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
inline
typename basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::ViewType
basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>::view()
                                                    const BSLS_KEYWORD_NOEXCEPT
{
    if (d_mode & ios_base::out) {
        return ViewType(d_str.begin(),
                        static_cast<std::ptrdiff_t>(streamSize()));   // RETURN
    }

    if (d_mode & ios_base::in) {
        return ViewType(this->eback(), this->egptr() - this->eback());// RETURN
    }

    return ViewType();
}

}  // close namespace bsl

// FREE FUNCTIONS
#if defined(BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY)                  \
 && defined(BSLS_LIBRARYFEATURES_HAS_CPP11_STREAM_MOVE)
template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
void bsl::swap(basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>& a,
               basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR>& b)
{
    typedef BloombergLP::bslmf::MovableRefUtil MoveUtil;

    if (a.get_allocator() == b.get_allocator()
    ||  bsl::allocator_traits<ALLOCATOR>::propagate_on_container_swap::value) {
        a.swap(b);
    }
    else {
        basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR> aCopy(
                                                            MoveUtil::move(a),
                                                            b.get_allocator());
        basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR> bCopy(
                                                            MoveUtil::move(b),
                                                            a.get_allocator());
        swap(a, bCopy);
        swap(b, aCopy);
    }
}
#endif

// ============================================================================
//                                TYPE TRAITS
// ============================================================================

namespace BloombergLP {
namespace bslma {

template <class CHAR_TYPE, class CHAR_TRAITS, class ALLOCATOR>
struct UsesBslmaAllocator<
        bsl::basic_stringbuf<CHAR_TYPE, CHAR_TRAITS, ALLOCATOR> >
    : bsl::true_type
{};

}  // close namespace bslma
}  // close enterprise namespace

// Undo 'BSLS_ASSERT' filename fix -- See {'bsls_assertimputil'}
#ifdef BSLS_ASSERTIMPUTIL_AVOID_STRING_CONSTANTS
#undef BSLS_ASSERTIMPUTIL_FILE
#define BSLS_ASSERTIMPUTIL_FILE BSLS_ASSERTIMPUTIL_DEFAULTFILE
#endif

#endif

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