// bdlsb_fixedmemoutput.h                                             -*-C++-*-
#ifndef INCLUDED_BDLSB_FIXEDMEMOUTPUT
#define INCLUDED_BDLSB_FIXEDMEMOUTPUT

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

//@PURPOSE: Provide a basic output stream buffer using a client buffer.
//
//@CLASSES:
//  bdlsb::FixedMemOutput: basic output stream buffer using client memory
//
//@SEE_ALSO: bdlsb_fixedmemoutstreambuf
//
//@DESCRIPTION: This component implements the output portion of the
// `bsl::basic_streambuf` protocol using a client-supplied memory buffer.
// Method names correspond to the protocol-specified method names.  Clients
// supply the character buffer at stream buffer construction, and can later
// reinitialize the stream buffer with a different character buffer by calling
// the `pubsetbuf` method.  The only difference between this component and
// `bdlsb_fixedmemoutstreambuf` is that the class `bdlsb::FixedMemOutput` does
// *not* derive from a `bsl::streambuf` and does not support locales.  This is
// advantageous for performance reasons, as the overhead of the initialization
// and virtual function calls of a `bsl::streambuf` can be undesirable.  The
// `bdlsb::FixedMemOutput` is designed to be used by generic template code that
// must be instantiated on a type that matches the interface of
// `bsl::streambuf`, but does not require an actual `bsl::streambuf`, in
// particular `bslx_genericoutstream`.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Use of `bdlsb::FixedMemOutput`
///- - - - - - - - - - - - - - - - - - - - - - - -
// This example demonstrates instantiating a template, bslx::GenericOutStream',
// on a `bdlsb::FixedMemOutput` object and using the `bslx::GenericOutStream`
// object to stream out some data.
//
// First, we create an object of our stream buffer:
// ```
// enum { k_STREAMBUF_CAPACITY = 30 };
//
// char                  buffer[k_STREAMBUF_CAPACITY];
// bdlsb::FixedMemOutput streamBuf(buffer, k_STREAMBUF_CAPACITY);
// ```
// Then, we create an instance of `bslx::GenericOutStream` using `streamBuf`,
// with an arbitrary value for its `versionSelector`, and externalize some
// values:
// ```
// bslx::GenericOutStream<bdlsb::FixedMemOutput> outStream(&streamBuf,
//                                                         20150707);
// outStream.putInt32(1);
// outStream.putInt32(2);
// outStream.putInt8('c');
// outStream.putString(bsl::string("hello"));
// ```
// Finally, we compare the contents of the buffer to the expected value:
// ```
// assert(15 == streamBuf.length());
// assert( 0 == bsl::memcmp(streamBuf.data(),
//                          "\x00\x00\x00\x01\x00\x00\x00\x02""c\x05""hello",
//                          15));
// ```

#include <bdlscm_version.h>

#include <bsls_assert.h>
#include <bsls_platform.h>
#include <bsls_review.h>
#include <bsls_types.h>

#include <bsl_algorithm.h>
#include <bsl_cstdlib.h>
#include <bsl_cstring.h>
#include <bsl_ios.h>  // 'bsl::streamsize'
#include <bsl_iosfwd.h>
#include <bsl_locale.h>
#include <bsl_streambuf.h>

namespace BloombergLP {
namespace bdlsb {

                              // ==============
                              // FixedMemOutput
                              // ==============

/// This class, like `bdlsb::FixedMemOutStreamBuf`, implements the output
/// functionality of the `basic_streambuf` interface, using client-supplied
/// `char *` memory.  It has an identical interface to
/// `bdlsb::FixedMemOutStreamBuf` but does *not* inherit from `bsl::streambuf`.
/// Thus, it is suitable for use as template parameter to
/// `bslx::GenericOutStream` (but not to `bslx::StreambufOutStream`).  Note
/// that this class is not designed to be derived from.
class FixedMemOutput {

  public:
    // TYPES
    typedef char                             char_type;
    typedef bsl::char_traits<char>::int_type int_type;
    typedef bsl::char_traits<char>::pos_type pos_type;
    typedef bsl::char_traits<char>::off_type off_type;
    typedef bsl::char_traits<char>           traits_type;

  private:
    // PRIVATE TYPE
    typedef bsls::Types::IntPtr              IntPtr;

    // PRIVATE DATA MEMBERS
    char            *d_buffer_p;  // output buffer
    bsl::streamsize  d_capacity;  // length of output buffer
    pos_type         d_pos;       // output cursor

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

  public:
    // CREATORS

    /// Create an empty stream buffer that uses the specified character
    /// `buffer` of the specified `length`.  The behavior is undefined
    /// unless `length == 0` or `length > 0 && buffer != 0`.
    /// Note that `buffer` is held but not owned.
    FixedMemOutput(char *buffer, bsl::streamsize length);

    /// Destroy this object.
    //! ~FixedMemOutput() = default;

    // MANIPULATORS

    /// Return a pointer providing modifiable access to the character buffer
    /// held by this stream buffer (supplied at construction).
    char *data();

                             // *** 27.5.2.2.1 locales: ***

    /// Associate the specified locale `loc` to this stream buffer.  Operation
    /// has no effect, because locales are not supported by this component.
    /// Return default constructed bsl::locale object.
    bsl::locale pubimbue(const bsl::locale& loc);

                             // *** 27.5.2.2.2 buffer and positioning: ***

    /// Reset the internal buffer of this stream to the specified `buffer` of
    /// the specified `length`.  Note that the next write operation will start
    /// at the beginning of `buffer`.
    FixedMemOutput *pubsetbuf(char            *buffer,
                              bsl::streamsize  length);

    /// Set the position indicator to the relative specified `offset` from the
    /// base position indicated by the specified `fixedPosition` 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 than the value returned by `length`.
    pos_type pubseekoff(off_type                offset,
                        bsl::ios_base::seekdir  fixedPosition,
                        bsl::ios_base::openmode which =
                            bsl::ios_base::in | bsl::ios_base::out);

    /// Set the position indicator to the specified `position` and return the
    /// resulting absolute position on success or pos_type(-1) on failure.
    /// Optionally specify `which` area of the stream buffer.  The `seekpos`
    /// operation will fail if `which` does not include the flag
    /// `bsl::ios_base::out` or if position is less then zero or greater than
    /// the value returned by `length`.
    pos_type pubseekpos(pos_type                position,
                        bsl::ios_base::openmode which =
                            bsl::ios_base::in | bsl::ios_base::out);

    /// Synchronizes the controlled character sequence (the buffers) with the
    /// associated character sequence.  Operation has no effect, because the
    /// stream is always kept in sync (no buffered output).  Return 0.
    int pubsync();

                             // *** 27.5.2.2.5 Put area: ***

    /// Write the specified character `c` to this buffer.  Return `c`, or
    /// `traits_type::eof()` if the end of the write buffer is reached.
    int_type sputc(char c);

    /// Write the specified `length` characters at the specified address `s` to
    /// this buffer.  Return the number of characters written, which is either
    /// `length` or the distance from the current write position to the end of
    /// the write buffer, whichever is smaller, and move the write cursor
    /// position by this amount.
    bsl::streamsize sputn(const char *s, bsl::streamsize length);

    // ACCESSORS

    /// Return the size in bytes of the buffer held by this stream buffer.
    bsl::streamsize capacity() const;

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

    /// Return the number of characters from the beginning of the buffer to the
    /// current write position.
    bsl::streamsize length() const;

                             // *** 27.5.2.2.1 locales: ***

    /// Return the current default locale.  Operation has no effect, because
    /// locales are not supported by this component.  Return default
    /// constructed bsl::locale object.
    bsl::locale getloc() const;

};

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

                              // --------------
                              // FixedMemOutput
                              // --------------

// CREATORS
inline
FixedMemOutput::FixedMemOutput(char            *buffer,
                               bsl::streamsize  length)
: d_buffer_p(buffer)
, d_capacity(length)
, d_pos(0)
{
    BSLS_ASSERT(buffer || 0 == length);
    BSLS_ASSERT(0 <= length);
}

// MANIPULATORS
inline
char *FixedMemOutput::data()
{
    return d_buffer_p;
}

inline
bsl::locale FixedMemOutput::pubimbue(const bsl::locale&)
{
    return bsl::locale();
}

inline
FixedMemOutput *FixedMemOutput::pubsetbuf(char            *buffer,
                                          bsl::streamsize  length)
{
    BSLS_ASSERT(buffer || 0 == length);
    BSLS_ASSERT(0 <= length);

    d_buffer_p = buffer;
    d_capacity = length;
    d_pos      = 0;

    return this;
}

inline
int FixedMemOutput::pubsync()
{
    // Nothing to do, the buffer is always up to date.
    return 0;
}

inline
FixedMemOutput::int_type FixedMemOutput::sputc(char c)
{
    if (d_pos >= d_capacity) {
        return traits_type::eof();                                    // RETURN
    }
    d_buffer_p[static_cast<IntPtr>(d_pos)] = c;
    d_pos += 1;
    return traits_type::to_int_type(c);
}

inline
bsl::streamsize FixedMemOutput::sputn(const char      *s,
                                      bsl::streamsize  length)
{
    BSLS_ASSERT(s);

    length = bsl::min<bsl::streamsize>(
                             length,
                             d_capacity - static_cast<bsl::streamsize>(d_pos));
    if (0 < length) {
        bsl::memcpy(d_buffer_p + static_cast<IntPtr>(d_pos), s, length);
        d_pos += length;
    }
    else {
        BSLS_ASSERT(0 == length);
    }

    return length;
}

// ACCESSORS
inline
bsl::streamsize FixedMemOutput::capacity() const
{
    return d_capacity;
}

inline
const char *FixedMemOutput::data() const
{
    return d_buffer_p;
}

inline
bsl::locale FixedMemOutput::getloc() const
{
    return bsl::locale();
}

inline
bsl::streamsize FixedMemOutput::length() const
{
    return bsl::streamsize(d_pos);
}

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