// bdlbb_blobstreambuf.h                                              -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BDLBB_BLOBSTREAMBUF
#define INCLUDED_BDLBB_BLOBSTREAMBUF

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

//@PURPOSE: Provide blob implementing the 'streambuf' interface.
//
//@CLASSES:
//  bdlbb::InBlobStreamBuf: 'bdlbb::Blob' input 'streambuf'
//  bdlbb::OutBlobStreamBuf: 'bdlbb::Blob' output 'streambuf'
//
//@SEE_ALSO: bdlbb_blob
//
//@DESCRIPTION: This component implements the input and output
// 'bsl::basic_streambuf' protocol using a user-supplied 'bdlbb::Blob'.  Method
// names necessarily correspond to the protocol-specified method names.  Refer
// to the C++ Standard, Section 27.5.2, for a full specification of the
// interface.
//
// A 'bdlbb::Blob' is an indexed sequence of 'bdlbb::BlobBuffer' of potentially
// different sizes.  The number of buffers in the sequence can increase or
// decrease, but the order of the buffers cannot change.  Therefore, the blob
// behaves logically as a single indexed buffer.  'bdlbb::InBlobStreamBuf' and
// 'bdlbb::OutBlobStreamBuf' can therefore respectively read from and write to
// this buffer as if there were a single continuous index.

#include <bdlscm_version.h>

#include <bdlbb_blob.h>

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

#include <bsl_ios.h>  // for 'bsl::streamsize'
#include <bsl_streambuf.h>
#include <bsl_cstddef.h>  // bsl::size_t

namespace BloombergLP {
namespace bdlbb { class Blob; }
namespace bdlbb {

                           // =====================
                           // class InBlobStreamBuf
                           // =====================

class InBlobStreamBuf : public bsl::streambuf {
    // This class implements the input functionality of the 'basic_streambuf'
    // protocol, using a client-supplied 'bdlbb::Blob'.

    // PRIVATE TYPES
    typedef bsl::ios_base ios_base;

    // DATA
    const bdlbb::Blob *d_blob_p;          // "streamed" blob (held)
    int                d_getBufferIndex;  // index of current buffer
    int d_previousBuffersLength;          // length of buffers before the
                                          // current one

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

  private:
    // PRIVATE MANIPULATORS
    void setGetPosition(bsl::size_t position);
        // Set the current location to the specified 'position'.

    // PRIVATE ACCESSORS
    int checkInvariant() const;
        // Check this object's invariant.

  protected:
    // PROTECTED VIRTUAL FUNCTIONS
    virtual int_type overflow(int_type c = bsl::streambuf::traits_type::eof());
        // Return 'traits_type::eof()' unconditionally.

    virtual int_type pbackfail(
                              int_type c = bsl::streambuf::traits_type::eof());
        // Adjust the underlying blob and put the optionally specified
        // character 'c' at the newly valid 'gptr()'.  Return 'c' (or
        // '~traits_type::eof' if 'c == traits_type::eof') on success, and
        // 'traits_type::eof()' otherwise.

    virtual pos_type seekoff(
       off_type                offset,
       bsl::ios_base::seekdir  fixedPosition,
       bsl::ios_base::openmode which = bsl::ios_base::in | bsl::ios_base::out);
        // Set the location from which the next I/O operation indicated by the
        // optionally specified 'which' mode will occur to the specified
        // 'offset' position from the location indicated by the specified
        // 'fixedPosition'.  Return the new offset on success, and
        // 'off_type(-1)' otherwise.  'offset' may be negative.  Note that this
        // method will fail if 'bsl::ios_base::out' is set.

    virtual pos_type seekpos(
       pos_type                position,
       bsl::ios_base::openmode which = bsl::ios_base::in | bsl::ios_base::out);
        // Set the location from which the next I/O operation indicated by the
        // optionally specified 'which' mode will occur to the specified
        // 'position'.  Return 'position' on success, and 'off_type(-1)'
        // otherwise.  Note that this method will fail if 'bsl::ios_base::out'
        // is set.

    virtual bsl::streamsize showmanyc();
        // Return the number of characters currently available for reading from
        // this stream buffer, or 0 if there are none.

    virtual int sync();
        // Return 0 unconditionally.

    virtual int_type underflow();
        // Adjust the underlying blob so that the next read position is valid.
        // Return the character at 'gptr()' on success and 'traits_type::eof()'
        // otherwise.

    virtual bsl::streamsize xsgetn(char_type       *destination,
                                   bsl::streamsize  numChars);
        // Read the specified 'numChars' to the specified 'destination'.
        // Return the number of characters successfully read.  The behavior is
        // undefined unless 0 <= 'numChars'.

    virtual bsl::streamsize xsputn(const char_type *source,
                                   bsl::streamsize  numChars);
        // Return 0 unconditionally.

  public:
    // CREATORS
    explicit InBlobStreamBuf(const bdlbb::Blob *blob);
        // Create a 'BlobStreamBuf' using the specified 'blob'.  The behavior
        // is undefined unless 'blob' remains valid and externally unmodified
        // for the lifetime of this 'streambuf'.

    ~InBlobStreamBuf();
        // Destroy this stream buffer.

    // MANIPULATORS
    void reset(const bdlbb::Blob *blob = 0);
        // Reset the get areas.  Optionally set the underlying 'bdlbb::Blob'
        // value to the optionally specified 'blob' if 'blob' is not 0.  The
        // behavior is undefined unless 'blob' remains valid and externally
        // unmodified for the lifetime of this 'streambuf'.

    // ACCESSORS
    int currentBufferIndex() const;
        // Return the index of the current buffer.  The behavior is undefined
        // unless the "streamed" blob has at least one buffer.

    const bdlbb::Blob *data() const;
        // Return the address of the blob held by this stream buffer.

    int previousBuffersLength() const;
        // Return the number of bytes contained in the buffers located before
        // the current one.  The behavior is undefined unless the "streamed"
        // blob has at least one buffer.
};

                           // ======================
                           // class OutBlobStreamBuf
                           // ======================

class OutBlobStreamBuf : public bsl::streambuf {
    // This class implements the output functionality of the 'basic_streambuf'
    // protocol, using a client-supplied 'bdlbb::Blob'.

    // PRIVATE TYPES
    typedef bsl::ios_base ios_base;

    // DATA
    bdlbb::Blob *d_blob_p;                 // "streamed" blob (held)
    int          d_putBufferIndex;         // index of current buffer
    int          d_previousBuffersLength;  // length of buffers before

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

  private:
    // PRIVATE MANIPULATORS
    void setPutPosition(bsl::size_t position);
        // Set the current location to the specified 'position'.

    // PRIVATE ACCESSORS
    int checkInvariant() const;
        // Check this object's invariants and return 0.

  protected:
    // PROTECTED VIRTUAL FUNCTIONS
    virtual int_type overflow(int_type c = bsl::streambuf::traits_type::eof());
        // Append the optionally specified character 'c' to this streambuf, and
        // return 'c'.  By default, 'traits_type::eof()' is appended.

    virtual int_type pbackfail(
                              int_type c = bsl::streambuf::traits_type::eof());
        // Return 'traits_type::eof()' unconditionally.

    virtual pos_type seekoff(
       off_type                offset,
       bsl::ios_base::seekdir  fixedPosition,
       bsl::ios_base::openmode which = bsl::ios_base::in | bsl::ios_base::out);
        // Set the location from which the next I/O operation indicated by the
        // optionally specified 'which' mode will occur to the specified
        // 'offset' position from the location indicated by the specified
        // 'fixedPosition'.  Return the new offset on success, and
        // 'off_type(-1)' otherwise.  'offset' may be negative.  Note that this
        // method will fail if 'bsl::ios_base::in' is set.

    virtual pos_type seekpos(
       pos_type                position,
       bsl::ios_base::openmode which = bsl::ios_base::in | bsl::ios_base::out);
        // Set the location from which the next I/O operation indicated by the
        // optionally specified 'which' mode will occur to the specified
        // 'position'.  Return 'position' on success, and 'off_type(-1)'
        // otherwise.  Note that this method will fail if 'bsl::ios_base::in'
        // is set.

    virtual bsl::streamsize showmanyc();
        // Return 0 unconditionally.

    virtual int sync();
        // Synchronize the put position in the blob of this stream buffer.
        // Return 0 unconditionally.

    virtual int_type underflow();
        // Return 'traits_type::eof()' unconditionally.

    virtual bsl::streamsize xsgetn(char_type       *destination,
                                   bsl::streamsize  numChars);
        // Return 0 unconditionally.

    virtual bsl::streamsize xsputn(const char_type *source,
                                   bsl::streamsize  numChars);
        // Copy the specified 'numChars' from the specified 'source' to the
        // blob held by this streambuf, starting at the current put area
        // location.  The behavior is undefined unless 0 <= 'numChars'.

  public:
    // CREATORS
    explicit OutBlobStreamBuf(bdlbb::Blob *blob);
        // Create a 'OutBlobStreamBuf' using the specified 'blob', and set the
        // location at which the next write operation will occur to
        // 'blob->length()'.  The behavior is undefined unless 'blob' remains
        // valid and externally unmodified for the lifetime of this
        // 'streambuf'.

    ~OutBlobStreamBuf();
        // Destroy this stream buffer.

    // MANIPULATORS
    bdlbb::Blob *data();
        // Return the address of the blob held by this stream buffer.

    void reset(bdlbb::Blob *blob = 0);
        // Reset the put position of this buffer to the first location,
        // available for writing in the underlying 'bdlbb::Blob'. Optionally
        // specify a 'blob' used to change current underlying 'bdlbb::Blob'
        // value for.  The behavior is undefined unless 'blob' remains valid
        // and externally unmodified for the lifetime of this 'streambuf'.

    // ACCESSORS
    int currentBufferIndex() const;
        // Return the index of the current buffer.  The behavior is undefined
        // unless the "streamed" blob has at least one buffer.

    const bdlbb::Blob *data() const;
        // Return the address of the blob held by this stream buffer.

    int previousBuffersLength() const;
        // Return the number of bytes contained in the buffers located before
        // the current one.  The behavior is undefined unless the "streamed"
        // blob has at least one buffer.
};

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

                           // =====================
                           // class InBlobStreamBuf
                           // =====================

// MANIPULATORS
inline
void InBlobStreamBuf::reset(const bdlbb::Blob *blob)
{
    if (blob) {
        d_blob_p                = blob;
        d_getBufferIndex        = 0;
        d_previousBuffersLength = 0;
        setg(0, 0, 0);
        if (0 == d_blob_p->length()) {
            return;                                                   // RETURN
        }
    }
    setGetPosition(0);
}

// ACCESSORS
inline
int InBlobStreamBuf::currentBufferIndex() const
{
    BSLS_ASSERT(d_getBufferIndex < d_blob_p->numBuffers());
    return d_getBufferIndex;
}

inline
const bdlbb::Blob *InBlobStreamBuf::data() const
{
    return d_blob_p;
}

inline
int InBlobStreamBuf::previousBuffersLength() const
{
    return d_previousBuffersLength;
}

                           // ======================
                           // class OutBlobStreamBuf
                           // ======================

// MANIPULATORS
inline
bdlbb::Blob *OutBlobStreamBuf::data()
{
    return d_blob_p;
}

inline
void OutBlobStreamBuf::reset(bdlbb::Blob *blob)
{
    if (blob) {
        d_blob_p                = blob;
        d_putBufferIndex        = 0;
        d_previousBuffersLength = 0;
        setp(0, 0);
        if (0 == d_blob_p->totalSize()) {
            return;                                                   // RETURN
        }
    }
    setPutPosition(d_blob_p->length());
}

// ACCESSORS
inline
int OutBlobStreamBuf::currentBufferIndex() const
{
    BSLS_ASSERT(d_putBufferIndex < d_blob_p->numBuffers());
    return d_putBufferIndex;
}

inline
const bdlbb::Blob *OutBlobStreamBuf::data() const
{
    return d_blob_p;
}

inline
int OutBlobStreamBuf::previousBuffersLength() const
{
    return d_previousBuffersLength;
}
}  // close package namespace

}  // close enterprise namespace

#endif

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