// bdlbb_blob.h                                                       -*-C++-*-
#ifndef INCLUDED_BDLBB_BLOB
#define INCLUDED_BDLBB_BLOB

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

//@PURPOSE: Provide an indexed set of buffers from multiple sources.
//
//@CLASSES:
//  bdlbb::BlobBuffer: in-core representation of a shared buffer
//  bdlbb::BlobBufferFactory: factory of blob buffers
//  bdlbb::Blob: indexed sequence of buffers
//
//@SEE_ALSO: bslstl_sharedptr, bdlbb_pooledblobbufferfactory
//
//@DESCRIPTION: This component provides an indexed sequence (`bdlbb::Blob`) of
// `bdlbb::BlobBuffer` objects allocated from potentially multiple
// `bdlbb::BlobBufferFactory` objects.  A `bdlbb::BlobBuffer` is a simple
// in-core value object owning a shared pointer to a memory buffer.  Therefore,
// the lifetime of the underlying memory is determined by shared ownership
// between the blob buffer, the blob(s) that may contain it, and any other
// entities that may share ownership of the memory buffer.
//
// Logically, a `bdlbb::Blob` can be thought of as a sequence of bytes
// (although not contiguous).  Each buffer in a blob contributes its own size
// to the blob, with the total size of a blob being the sum of sizes over all
// its buffers.  A prefix of these bytes, collectively referred to as the data
// of the blob, are defined by the data length, which can be set by the user
// using the `setLength` method.  Note that the data length never exceeds the
// total size.  When setting the length to a value greater than the total size,
// the latter is increased automatically by adding buffers created from a
// factory passed at construction; the behavior is undefined if no factory was
// supplied at construction.
//
// The blob also updates its data length during certain operations (e.g.,
// insertion/removal/replacement of buffers containing some data bytes), as
// well as several attributes driven by the data length.  The first bytes
// numbered by the data length belong to the data buffers.  Note that all data
// buffers, except perhaps the last, contribute all their bytes to the
// `bdlbb::Blob` data.  The last data buffer contributes anywhere between one
// and all of its bytes to the `bdlbb::Blob` data.  The number of data buffers
// (returned by the `numDataBuffers` method), as well as the last data buffer
// length (returned by `lastDataBufferLength`), are maintained by `bdlbb::Blob`
// automatically when setting the length to a new value.
//
// Buffers which do not contain data are referred to as capacity buffers.  The
// total size of a blob does not decrease when setting the length to a value
// smaller than the current length.  Instead, any data buffer that no longer
// contains data after the call to `setLength` becomes a capacity buffer, and
// may become a data buffer again later if setting length past its prefix size.
//
// This design is intended to allow very efficient re-assignment of buffers (or
// part of buffers using shared pointer aliasing) between different blobs,
// without copying of the underlying data, while promoting efficient allocation
// of resources (via retaining capacity).  `bdlbb::Blob` is an advantageous
// choice when manipulation of the sequence, sharing of portions of the
// sequence, lifetime management of individual portions of the sequence, and
// the possibility of buffers in the sequence to have different sizes, are
// desired.
//
///Thread Safety
///-------------
// Different instances of the classes defined in this component can be
// concurrently modified by different threads.  Thread safety of a particular
// instance is not guaranteed, and therefore must be handled by the user.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: A Simple Blob Buffer Factory
///- - - - - - - - - - - - - - - - - - - -
// Classes that implement the `bdlbb::BlobBufferFactory` protocol are used to
// allocate `bdlbb::BlobBuffer` objects.  A simple implementation follows:
// ```
// /// This factory creates blob buffers of a fixed size specified at
// /// construction.
// class SimpleBlobBufferFactory : public bdlbb::BlobBufferFactory {
//
//     // DATA
//     bsl::size_t       d_bufferSize;
//     bslma::Allocator *d_allocator_p;
//
//   private:
//     // Not implemented:
//     SimpleBlobBufferFactory(const SimpleBlobBufferFactory&);
//     SimpleBlobBufferFactory& operator=(const SimpleBlobBufferFactory&);
//
//   public:
//     // CREATORS
//     explicit SimpleBlobBufferFactory(int               bufferSize = 1024,
//                                      bslma::Allocator *basicAllocator = 0);
//     ~SimpleBlobBufferFactory();
//
//     // MANIPULATORS
//     void allocate(bdlbb::BlobBuffer *buffer);
// };
//
// SimpleBlobBufferFactory::SimpleBlobBufferFactory(
//                                           int               bufferSize,
//                                           bslma::Allocator *basicAllocator)
// : d_bufferSize(bufferSize)
// , d_allocator_p(bslma::Default::allocator(basicAllocator))
// {
// }
//
// SimpleBlobBufferFactory::~SimpleBlobBufferFactory()
// {
// }
//
// void SimpleBlobBufferFactory::allocate(bdlbb::BlobBuffer *buffer)
// {
//     bsl::shared_ptr<char> shptr(
//                             (char *) d_allocator_p->allocate(d_bufferSize),
//                             d_allocator_p);
//
//     buffer->reset(shptr, d_bufferSize);
// }
// ```
// Note that should the user desire a blob buffer factory for his/her
// application, a better implementation that pools buffers is available in the
// `bdlbb_pooledblobbufferfactory` component.
//
///Simple Blob Usage
///- - - - - - - - -
// Blobs can be created just by passing a factory that is responsible to
// allocate the `bdlbb::BlobBuffer`.  The following simple program illustrates
// how.
// ```
// {
//     SimpleBlobBufferFactory myFactory(1024);
//
//     bdlbb::Blob blob(&myFactory);
//     assert(0    == blob.length());
//     assert(0    == blob.totalSize());
//
//     blob.setLength(512);
//     assert( 512 == blob.length());
//     assert(1024 == blob.totalSize());
// ```
// Users need to access buffers directly in order to read/write data.
// ```
//     char data[] = "12345678901234567890"; // 20 bytes
//     assert(0 != blob.numBuffers());
//     assert(static_cast<int>(sizeof(data)) <= blob.buffer(0).size());
//     bsl::memcpy(blob.buffer(0).data(), data, sizeof(data));
//
//     blob.setLength(sizeof(data));
//     assert(sizeof data == blob.length());
//     assert(       1024 == blob.totalSize());
// ```
// A `bdlbb::BlobBuffer` can easily be re-assigned from one blob to another
// with no copy.  In that case, the memory held by the buffer will be returned
// to its factory when the last blob referencing the buffer is destroyed.  For
// the following example, a blob will be created using the default constructor.
// In this case, the `bdlbb::Blob` object will not able to grow on its own.
// Calling `setLength` for a number equal or greater than `totalSize()` will
// result in undefined behavior.
// ```
//     bdlbb::Blob dest;
//     assert(   0 == dest.length());
//     assert(   0 == dest.totalSize());
//
//     assert(0 != blob.numBuffers());
//     dest.appendBuffer(blob.buffer(0));
//     assert(   0 == dest.length());
//     assert(1024 == dest.totalSize());
// ```
// Note that at this point, the logical length (returned by `length`) of this
// object has not changed.  `setLength` must be called explicitly by the user
// if the logical length of the `bdlbb::Blob` must be changed:
// ```
//     dest.setLength(dest.buffer(0).size());
//     assert(1024 == dest.length());
//     assert(1024 == dest.totalSize());
// ```
// Sharing only a part of a buffer is also possible through shared pointer
// aliasing.  In the following example, a buffer that contains only bytes 11-16
// from the first buffer of `blob` will be appended to `blob`.
// ```
//     assert(0 != blob.numBuffers());
//     assert(16 <= blob.buffer(0).size());
//
//     bsl::shared_ptr<char> shptr(blob.buffer(0).buffer(),
//                                 blob.buffer(0).data() + 10);
//         // 'shptr' is now an alias of 'blob.buffer(0).buffer()'.
//
//     bdlbb::BlobBuffer partialBuffer(shptr, 6);
//     dest.appendBuffer(partialBuffer);
//         // The last buffer of 'dest' contains only bytes 11-16 from
//         // 'blob.buffer(0)'.
// }
// ```
//
///Example 2: Data-Oriented Manipulation of a Blob
///- - - - - - - - - - - - - - - - - - - - - - - -
// There are several typical ways of manipulating a blob: the simplest lets the
// blob automatically manage the length, by using only `prependBuffer`,
// `appendBuffer`, and `insertBuffer`.  Consider the following typical
// utilities (these utilities are to illustrate usage, they are not meant to be
// copy-pasted into application programs although they can provide a foundation
// for application utilities):
// ```
// /// Prepend the specified `prolog` of the specified `length` to the
// /// specified `blob`, using the optionally specified `allocator` to
// /// supply any memory (or the currently installed default allocator if
// /// `allocator` is 0).  The behavior is undefined unless
// /// `blob->totalSize() <= INT_MAX - length - sizeof(int)` and
// /// `blob->numBuffers() < INT_MAX`.
// void prependProlog(bdlbb::Blob        *blob,
//                    const char         *prolog,
//                    int                 length,
//                    bslma::Allocator   *allocator = 0);
//
// /// Load into the specified `blob` the data composed of the specified
// /// `prolog` and of the payload in the `numVectors` buffers pointed to
// /// by the specified `vectors` of the respective `vectorSizes`.
// /// Ownership of the vectors is transferred to the `blob` which will use
// /// the specified `deleter` to destroy them.  Use the optionally
// /// specified `allocator` to supply memory, or the currently installed
// /// default allocator if `allocator` is 0.  Note that any buffer
// /// belonging to `blob` prior to composing the message is not longer in
// /// `blob` after composing the message.  Note also that `blob` need not
// /// have been created with a blob buffer factory.  The behavior is
// /// undefined unless `blob` points to an initialized `bdlbb::Blob`
// /// instance.
// template <class DELETER>
// void composeMessage(bdlbb::Blob        *blob,
//                     const bsl::string&  prolog,
//                     char * const       *vectors,
//                     const int          *vectorSizes,
//                     int                 numVectors,
//                     const DELETER&      deleter,
//                     bslma::Allocator   *allocator = 0);
//
// /// Insert a timestamp data buffer immediately after the prolog buffer
// /// and prior to any payload buffer.  Return the number of bytes
// /// inserted.  Use the optionally specified `allocator` to supply
// /// memory, or the currently installed default allocator if `allocator`
// /// is 0.  The behavior is undefined unless the specified `blob` points
// /// to an initialized `bdlbb::Blob` instance with at least one data
// /// buffer.
// int timestampMessage(bdlbb::Blob *blob, bslma::Allocator *allocator = 0);
// ```
// A possible implementation using only `prependBuffer`, `appendBuffer`, and
// `insertBuffer` could be as follows:
// ```
// void prependProlog(bdlbb::Blob        *blob,
//                    const char         *prolog,
//                    int                 length,
//                    bslma::Allocator   *allocator)
// {
//     assert(blob);
//     assert(blob->totalSize() <=
//                          INT_MAX - length - static_cast<int>(sizeof(int)));
//     assert(blob->numBuffers() < INT_MAX);
//
//     (void)allocator;
//
//     int                     prologBufferSize =
//                                     static_cast<int>(length + sizeof(int));
//     SimpleBlobBufferFactory fa(prologBufferSize);
//     bdlbb::BlobBuffer       prologBuffer;
//     fa.allocate(&prologBuffer);
//
//     bslx::MarshallingUtil::putInt32(prologBuffer.data(), length);
//     bsl::memcpy(prologBuffer.data() + sizeof(int),
//                 prolog,
//                 length);
//     assert(prologBuffer.size() == prologBufferSize);
//
//     blob->prependDataBuffer(prologBuffer);
// }
// ```
// Note that the length of `blob` in the above implementation is automatically
// incremented by `prologBuffer.size()`.  Consider instead:
// ```
//     blob->insertBuffer(0, prologBuffer);
// ```
// which inserts the prologBuffer before the first buffer of `blob`.  This call
// will almost always adjust the length properly *except* if the length of
// `blob` is 0 before the insertion (i.e., the message has an empty payload).
// In that case, the resulting `blob` will still be empty after
// `prependProlog`, which, depending on the intention of the programmer, could
// be intended (avoid sending empty messages) or could be (most likely) a
// mistake.
//
// The `composeMessage` implementation is simplified by using `prependProlog`:
// ```
// template <class DELETER>
// void composeMessage(bdlbb::Blob        *blob,
//                     const char         *prolog,
//                     int                 prologLength,
//                     char * const       *vectors,
//                     const int          *vectorSizes,
//                     int                 numVectors,
//                     const DELETER&      deleter,
//                     bslma::Allocator   *allocator)
// {
//     assert(blob);
//     assert(vectors);
//     assert(0 <= numVectors);
//
//     blob->removeAll();
//     prependProlog(blob, prolog, prologLength, allocator);
//
//     for (int i = 0; i < numVectors; ++i) {
//         bsl::shared_ptr<char> shptr(vectors[i], deleter, allocator);
//         bdlbb::BlobBuffer partialBuffer(shptr, vectorSizes[i]);
//         blob->appendDataBuffer(partialBuffer);
//             // The last buffer of 'dest' contains only bytes 11-16 from
//             // 'blob.buffer(0)'.
//     }
// }
// ```
// Note that the `deleter` is used to destroy the buffers transferred by
// `vectors`, but not the prolog buffer.
//
// Timestamping a message is done by creating a buffer holding a timestamp, and
// inserting it after the prolog and before the payload of the message.  Note
// that in typical messages, timestamps would be part of the prolog itself, so
// this is a somewhat contrived example for exposition only.
// ```
// int timestampMessage(bdlbb::Blob *blob, bslma::Allocator *allocator)
// {
//     assert(blob);
//     assert(0 < blob->numDataBuffers());
//
//     bdlbb::BlobBuffer buffer;
//     bdlt::Datetime now = bdlt::CurrentTime::utc();
//
//     SimpleBlobBufferFactory fa(128, allocator);
//     bdlbb::BlobBuffer timestampBuffer;
//     fa.allocate(&timestampBuffer);
//
//     bslx::ByteOutStream bdexStream(20150826);
//     now.bdexStreamOut(bdexStream, 1);
//     assert(bdexStream);
//     assert(bdexStream.length() < 128);
//     bsl::memcpy(timestampBuffer.data(),
//                 bdexStream.data(),
//                 bdexStream.length());
//     timestampBuffer.setSize(static_cast<int>(bdexStream.length()));
// ```
// Now that we have fabricated the buffer holding the current data and time, we
// must insert it into the blob after the first buffer (i.e., before the buffer
// at index 1).  Note however that the payload could be empty, a condition
// tested by the fact that there is only one data buffer in `blob`.  In that
// case, it would be a mistake to use `insertBuffer` since it would not modify
// the length of the blob.
// ```
//     if (1 < blob->numDataBuffers()) {
//         blob->insertBuffer(1, timestampBuffer);
//     } else {
//         blob->appendDataBuffer(timestampBuffer);
//     }
//
//     return static_cast<int>(bdexStream.length());
// }
// ```
// Note that the call to `appendDataBuffer` also takes care of the possibility
// that the first buffer of `blob` may not be full to capacity (if the length
// of the blob was smaller than the buffer size, only the first
// `blob->length()` bytes would contain prolog data).  In that case, that
// buffer is trimmed before appending the `timestampBuffer` so that the first
// byte of the `timestampBuffer` appears immediately next to the last prolog
// byte, and the blob length is automatically incremented by the size of the
// `timestampBuffer`.

#include <bdlscm_version.h>

#include <bslma_allocator.h>

#include <bslmf_isbitwisemoveable.h>
#include <bslmf_movableref.h>

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

#include <bsl_iosfwd.h>
#include <bsl_memory.h>
#include <bsl_vector.h>

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

namespace BloombergLP {
namespace bdlbb {

                              // ================
                              // class BlobBuffer
                              // ================

/// `BlobBuffer` is a simple in-core representation of a shared buffer.
/// This class is exception-neutral with no guarantee of rollback: if an
/// exception is thrown during the invocation of a method on a pre-existing
/// instance, the container is left in a valid state, but its value is
/// undefined.  In no event is memory leaked.
class BlobBuffer {

    // PRIVATE TYPES

    /// Used in move construction and assignment to make lines shorter.
    typedef bslmf::MovableRefUtil MoveUtil;

    // DATA
    bsl::shared_ptr<char> d_buffer;  // shared buffer
    int                   d_size;    // buffer size (in bytes)

    // FRIENDS
    friend bool operator==(const BlobBuffer&, const BlobBuffer&);

    friend bool operator!=(const BlobBuffer&, const BlobBuffer&);

  public:
    // CREATORS

    /// Create a blob buffer representing a null buffer.  Note that the
    /// `size` and `data` methods of a default-constructed blob buffer both
    /// return 0.
    BlobBuffer();

    /// Create a blob buffer representing the specified `buffer` of the
    /// specified `size`.  The behavior is undefined unless `0 <= size` and
    /// the `buffer` refers to a continuous block of memory of at least
    /// `size` bytes.
    BlobBuffer(const bsl::shared_ptr<char>& buffer, int size);

    /// Create a blob buffer representing the specified moveable `buffer` of
    /// the specified `size`.  The behavior is undefined unless `0 <= size`
    /// and the `buffer` refers to a continuous block of memory of at least
    /// `size` bytes.
    BlobBuffer(bslmf::MovableRef<bsl::shared_ptr<char> > buffer, int size);

    /// Create a blob buffer having the same value as the specified
    /// `original` blob buffer.
    BlobBuffer(const BlobBuffer& original);

    /// Create a blob buffer 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.
    BlobBuffer(bslmf::MovableRef<BlobBuffer> original) BSLS_KEYWORD_NOEXCEPT;

    /// Destroy this blob buffer.
    ~BlobBuffer();

    // MANIPULATORS

    /// Assign to this blob buffer the value of the specified `rhs` blob
    /// buffer, and return a reference to this modifiable blob buffer.
    BlobBuffer& operator=(const BlobBuffer& rhs);

    /// 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.
    BlobBuffer& operator=(bslmf::MovableRef<BlobBuffer> rhs);

    /// Reset this blob buffer to its default-constructed state.
    void reset();

    /// Set the buffer represented by this object to the specified `buffer`
    /// of the specified `size`.  The behavior is undefined unless
    /// `0 <= size` and the `buffer` refers to a continuous block of memory
    /// of at least `size` bytes.
    void reset(const bsl::shared_ptr<char>& buffer, int size);

    /// Set the buffer represented by this object to the specified moveable
    /// `buffer` of the specified `size`.  The behavior is undefined unless
    /// `0 <= size` and the `buffer` refers to a continuous block of memory
    /// of at least `size` bytes.
    void reset(bslmf::MovableRef<bsl::shared_ptr<char> > buffer, int size);

    /// Return a reference to the shared pointer to the modifiable buffer
    /// represented by this object.
    bsl::shared_ptr<char>& buffer();

    /// Set the size of this blob buffer to the specified `size`.  The
    /// behavior is undefined unless `0 <= size` and the capacity of the
    /// buffer returned by the `buffer` method is at least `size` bytes.
    void setSize(int size);

    /// Efficiently exchange the value of this object with the value of the
    /// specified `other` object.  This method provides the no-throw
    /// exception-safety guarantee.
    void swap(BlobBuffer& other);

    /// Reduce this buffer to the specified `toSize` and return the
    /// leftover.  The behaviour is undefined unless '0 <= toSize && toSize
    /// <= size()'.
    BlobBuffer trim(int toSize);

    // ACCESSORS

    /// Return a reference to the non-modifiable shared pointer to the
    /// buffer represented by this object.
    const bsl::shared_ptr<char>& buffer() const;

    /// Return the address of the modifiable buffer represented by this
    /// object.
    char *data() const;

    /// Return the size of the buffer represented by this object.
    int size() const;

    /// Format this object as a hexadecimal dump on the specified `stream`,
    /// and return a reference to the modifiable `stream`.  Note that the
    /// optionally specified `level` and `spacesPerLevel` arguments are
    /// specified for interface compatibility only and are effectively
    /// ignored.
    bsl::ostream& print(bsl::ostream& stream,
                        int           level          = 0,
                        int           spacesPerLevel = 4) const;
};
}  // close package namespace

// TYPE TRAITS

namespace bslmf {

template <>
struct IsBitwiseMoveable<BloombergLP::bdlbb::BlobBuffer>
: IsBitwiseMoveable<bsl::shared_ptr<char> >::type {
};

}  // close namespace bslmf

namespace bdlbb {

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` blob buffers have the
/// same value, and `false` otherwise.  Two blob buffers have the same value
/// if they represent the same buffer of the same size.
bool operator==(const BlobBuffer& lhs, const BlobBuffer& rhs);

/// Return `true` if the specified `lhs` and `rhs` blob buffers do not have
/// the same value, and `false` otherwise.  Two blob buffers do not have the
/// same value if they do not represent the same buffer of the same size.
bool operator!=(const BlobBuffer& lhs, const BlobBuffer& rhs);

/// Format the specified blob `buffer` to the specified output `stream`, and
/// return a reference to the modifiable `stream`.
bsl::ostream& operator<<(bsl::ostream& stream, const BlobBuffer& buffer);

// FREE FUNCTIONS

/// Efficiently exchange the values of the specified `a` and `b` objects.
/// This method provides the no-throw exception-safety guarantee.
void swap(BlobBuffer& a, BlobBuffer& b);

                          // =======================
                          // class BlobBufferFactory
                          // =======================

/// This class defines a base-level protocol for a `BlobBuffer` factory.
class BlobBufferFactory {

  public:
    // CREATORS

    /// Destroy this blob buffer factory.
    virtual ~BlobBufferFactory();

    // MANIPULATORS

    /// Allocate a blob buffer from this blob buffer factory, and load it
    /// into the specified `buffer`.
    virtual void allocate(BlobBuffer *buffer) = 0;
};

                                 // ==========
                                 // class Blob
                                 // ==========

/// `Blob` is an in-core container for `BlobBuffer` objects.  This class is
/// exception-neutral with no guarantee of rollback: if an exception is
/// thrown during the invocation of a method on a pre-existing instance, the
/// container is left in a valid state, but its value is undefined.  In no
/// event is memory leaked.
class Blob {

    // PRIVATE TYPES

    /// Used in move construction and assignment to make lines shorter.
    typedef bslmf::MovableRefUtil MoveUtil;

    // DATA
    bsl::vector<BlobBuffer>  d_buffers;             // buffer sequence

    int                      d_totalSize;           // capacity of blob (in
                                                    // bytes)

    int                      d_dataLength;          // length (in bytes) of
                                                    // user-managed data

    int                      d_dataIndex;           // index of the last data
                                                    // buffer, or -1 if the
                                                    // blob has no data buffers

    int                      d_preDataIndexLength;  // sum of the lengths of
                                                    // all data buffers,
                                                    // excluding the last one

    BlobBufferFactory       *d_bufferFactory_p;     // factory used to grow
                                                    // blob (held)

    // FRIENDS
    friend bool operator==(const Blob&, const Blob&);
    friend bool operator!=(const Blob&, const Blob&);

  private:
    // PRIVATE MANIPULATORS

    /// Set the length of this blob to the specified `length` and, if
    /// `length` is greater than its total size, grow this blob by appending
    /// buffers allocated using this object's underlying
    /// `BlobBufferFactory`.  This function implements the "slow-path" for
    /// `setLength`, handling the cases where the supplied `length` is lies
    /// beyond the boundaries of the last data buffer. The behavior is
    /// undefined if `length` is a negative value, if the new length
    /// requires growing the blob and this blob has no underlying factory,
    /// or if the `length` lies within the boundaries of the last data
    /// buffer.
    void slowSetLength(int length);

    // PRIVATE ACCESSORS

    /// Assert the invariants of this object and return 0 on success.
    int assertInvariants() const;

  public:
    // CREATORS

    /// Create an empty blob having no factory to allocate blob buffers.
    /// Since there is no factory, the behavior is undefined if the length
    /// of the blob is set beyond the total size.  Optionally specify a
    /// `basicAllocator` used to supply memory.  If `basicAllocator` is 0,
    /// the currently installed default allocator is used.
    explicit Blob(bslma::Allocator *basicAllocator = 0);

    /// Create an empty blob using the specified `factory` to allocate blob
    /// buffers.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.
    explicit Blob(BlobBufferFactory *factory,
                  bslma::Allocator  *basicAllocator = 0);

    /// Create a blob that initially holds the specified `numBuffers`
    /// buffers referenced by the specified `buffers`, and uses the
    /// specified `factory` to allocate blob buffers.  Optionally specify a
    /// `basicAllocator` used to supply memory.  If `basicAllocator` is 0,
    /// the currently installed default allocator is used.
    Blob(const BlobBuffer  *buffers,
         int                numBuffers,
         BlobBufferFactory *factory,
         bslma::Allocator  *basicAllocator = 0);

    /// Create a blob that holds the same buffers as the specified
    /// `original` blob, and uses the specified `factory` to allocate blob
    /// buffers.  Optionally specify a `basicAllocator` used to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.
    Blob(const Blob&        original,
         BlobBufferFactory *factory,
         bslma::Allocator  *basicAllocator = 0);

    /// Create a blob that holds the same buffers as the specified
    /// `original` blob, and has no factory to allocate blob buffers.  Since
    /// there is no factory, the behavior is undefined if the length of the
    /// blob is set beyond the total size.  Optionally specify a
    /// `basicAllocator` used to supply memory.  If `basicAllocator` is 0,
    /// the currently installed default allocator is used.
    Blob(const Blob& original, bslma::Allocator *basicAllocator = 0);

    /// Create a blob object having the same value as the specified
    /// `original` object by moving the contents of `original` to the
    /// newly-created object.  The allocator associated with `original` is
    /// propagated for use in the newly-created object.  `original` is left
    /// in a valid but unspecified state.
    Blob(bslmf::MovableRef<Blob> original) BSLS_KEYWORD_NOEXCEPT;

    /// Create a blob object having the same value as the specified
    /// `original` object that uses the specified `basicAllocator` to supply
    /// memory.  If `basicAllocator` is 0, the currently installed default
    /// allocator is used.  The contents of `original` are moved to the
    /// newly-created object.  `original` is left in a valid but unspecified
    /// state.
    Blob(bslmf::MovableRef<Blob>  original,
         bslma::Allocator        *basicAllocator);

    /// Destroy this blob.
    ~Blob();

    // MANIPULATORS

    /// Assign to this blob the value of the specified `rhs` blob, and
    /// return a reference to this modifiable blob.
    Blob& operator=(const Blob& rhs);

    /// 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.
    Blob& operator=(bslmf::MovableRef<Blob> rhs);

    /// Append the specified `buffer` after the last buffer of this blob.
    /// The length of this blob is unaffected.  The behavior is undefined
    /// unless neither the total size of the resulting blob nor its total
    /// number of buffers exceeds `INT_MAX`.  Note that this operation is
    /// equivalent to `insertBuffer(numBuffers(), buffer)`, but is more
    /// efficient.
    void appendBuffer(const BlobBuffer& buffer);

    /// Append the specified move-insertable `buffer` after the last buffer
    /// of this blob.  The `buffer` is left in a valid but unspecified
    /// state.  The length of this blob is unaffected.  The behavior is
    /// undefined unless neither the total size of the resulting blob nor
    /// its total number of buffers exceeds `INT_MAX`.  Note that this
    /// operation is equivalent to `insertBuffer(numBuffers(), buffer)`, but
    /// is more efficient.
    void appendBuffer(bslmf::MovableRef<BlobBuffer> buffer);

    /// Append the specified `buffer` after the last *data* buffer of this
    /// blob; the last data buffer is trimmed, if necessary.  The length of
    /// this blob is incremented by the size of `buffer`.  The behavior is
    /// undefined unless neither the total size of the resulting blob nor
    /// its total number of buffers exceeds `INT_MAX`.  Note that this
    /// operation is equivalent to:
    /// ```
    /// const int n = blob.length();
    /// blob.trimLastDataBuffer();
    /// blob.insertBuffer(numDataBuffers(), buffer);
    /// blob.setLength(n + buffer.size());
    /// ```
    /// but is more efficient.
    void appendDataBuffer(const BlobBuffer& buffer);

    /// Append the specified move-insertable `buffer` after the last *data*
    /// buffer of this blob; the last data buffer is trimmed, if necessary.
    /// The `buffer` is left in a valid but unspecified state.  The length
    /// of this blob is incremented by the size of `buffer`.  The behavior
    /// is undefined unless neither the total size of the resulting blob nor
    /// its total number of buffers exceeds `INT_MAX`.  Note that this
    /// operation is equivalent to:
    /// ```
    /// const int n = blob.length();
    /// blob.trimLastDataBuffer();
    /// blob.insertBuffer(numDataBuffers(), MoveUtil::move(buffer));
    /// blob.setLength(n + buffer.size());
    /// ```
    /// but is more efficient.
    void appendDataBuffer(bslmf::MovableRef<BlobBuffer> buffer);

    /// Insert the specified `buffer` at the specified `index` in this blob.
    /// Increment the length of this blob by the size of `buffer` if
    /// `buffer` is inserted *before* the logical end of this blob.  The
    /// length of this blob is <u>unchanged</u> if inserting at a position
    /// following all data buffers (e.g., inserting into an empty blob or
    /// inserting a buffer to increase capacity); in that case, the blob
    /// length must be changed by an explicit call to `setLength`.  Buffers
    /// at `index` and higher positions (if any) are shifted up by one index
    /// position.  The behavior is undefined unless
    /// `0 <= index <= numBuffers()` and neither the total size of the
    /// resulting blob nor its total number of buffers exceeds `INT_MAX`.
    void insertBuffer(int index, const BlobBuffer& buffer);

    /// Insert the specified move-insertable `buffer` at the specified
    /// `index` in this blob.  Increment the length of this blob by the size
    /// of `buffer` if `buffer` is inserted *before* the logical end of this
    /// blob.  The length of this blob is <u>unchanged</u> if inserting at a
    /// position following all data buffers (e.g., inserting into an empty
    /// blob or inserting a buffer to increase capacity); in that case, the
    /// blob length must be changed by an explicit call to `setLength`.
    /// Buffers at `index` and higher positions (if any) are shifted up by
    /// one index position.  The `buffer` is left in a valid but unspecified
    /// state.  The behavior is undefined unless
    /// `0 <= index <= numBuffers()` and neither the total size of the
    /// resulting blob nor its total number of buffers exceeds `INT_MAX`.
    void insertBuffer(int index, bslmf::MovableRef<BlobBuffer> buffer);

    /// Insert the specified `buffer` before the beginning of this blob.
    /// The length of this blob is incremented by the length of the
    /// prepended buffer.  The behavior is undefined unless neither the
    /// total size of the resulting blob nor its total number of buffers
    /// exceeds `INT_MAX`.  Note that this operation is equivalent to:
    /// ```
    /// const int n = blob.length();
    /// blob.insertBuffer(0, buffer);
    /// blob.setLength(n + buffer.size());
    /// ```
    /// but is more efficient.
    void prependDataBuffer(const BlobBuffer& buffer);

    /// Insert the specified move-insertable `buffer` before the beginning
    /// of this blob.  The length of this blob is incremented by the length
    /// of the prepended buffer.  The `buffer` is left in a valid but
    /// unspecified state.  The behavior is undefined unless neither the
    /// total size of the resulting blob nor its total number of buffers
    /// exceeds `INT_MAX`.  Note that this operation is equivalent to:
    /// ```
    /// const int n = blob.length();
    /// blob.insertBuffer(0, MoveUtil::move(buffer));
    /// blob.setLength(n + buffer.size());
    /// ```
    /// but is more efficient.
    void prependDataBuffer(bslmf::MovableRef<BlobBuffer> buffer);

    /// Remove all blob buffers from this blob, and set its length to 0.
    void removeAll();

    /// Remove the buffer at the specified `index` from this blob, and
    /// decrement the length of this blob by the size of `buffer` if the
    /// buffer at `index` contains data bytes (i.e., if the first byte of
    /// `buffer` occurs before the logical end of this blob).  Buffers at
    /// positions higher than `index` (if any) are shifted down by one index
    /// position.  The behavior is undefined unless
    /// `0 <= index < numBuffers()`.
    void removeBuffer(int index);

    /// Remove the specified `numBuffers` starting at the specified `index`
    /// from this blob.  Buffers at positions higher than `index` (if any)
    /// are shifted down by `numBuffers` index positions.  The behavior is
    /// undefined unless `0 <= index`, `0 <= numBuffers`, and
    /// `index + numBuffers <= numBuffers()`.
    void removeBuffers(int index, int numBuffers);

    /// Remove any unused capacity buffers from this blob.  Note that this
    /// method does not trim the last data buffer, and that the resulting
    /// `totalSize` will be `length` plus any unused capacity in the last
    /// buffer having data.
    void removeUnusedBuffers();

    /// Replace the data buffer at the specified `index` with the specified
    /// `buffer`.  The behavior is undefined unless
    /// `0 <= index < numDataBuffers()` and the total size of the resulting
    /// blob does not exceed `INT_MAX`.  Note that this operation is
    /// equivalent to:
    /// ```
    /// blob.removeBuffer(index);
    /// const int n = blob.length();
    /// blob.insertBuffer(index, buffer);
    /// blob.setLength(n + buffer.size());
    /// ```
    /// but is more efficient.
    void replaceDataBuffer(int index, const BlobBuffer& buffer);

    /// Allocate sufficient capacity to store at least the specified
    /// `numBuffers` buffers.  The behavior is undefined unless
    /// `0 <= numBuffers`.  Note that this method does not change the length
    /// of this blob or add any buffers to it.  Note also that the internal
    /// capacity will be increased to maintain a geometric growth factor.
    void reserveBufferCapacity(int numBuffers);

    /// Set the length of this blob to the specified `length` and, if
    /// `length` is greater than its total size, grow this blob by appending
    /// buffers allocated using this object's underlying
    /// `BlobBufferFactory`.  The behavior is undefined if `length` is a
    /// negative value, or if the new length requires growing the blob and
    /// this blob has no underlying factory.
    void setLength(int length);

    /// Efficiently exchange the value of this object with the value of the
    /// specified `other` object.  This method provides the no-throw
    /// exception-safety guarantee.  The behavior is undefined unless this
    /// object was created with the same allocator as `other`.
    void swap(Blob& other);

    /// Swap the blob buffer at the specified `index` with the specified
    /// `srcBuffer`.  The behavior is undefined unless
    /// `0 <= index < numBuffers()` and
    /// `srcBuffer->size() == buffer(index).size()`.  Note that other than
    /// the buffer swap the state of this object remains unchanged.
    void swapBufferRaw(int index, BlobBuffer *srcBuffer);

    /// Set the size of the last data buffer to `lastDataBufferLength()`.
    /// If there are no data buffers, or if the last data buffer is full
    /// (i.e., its size is `lastDataBufferLength()`), then this method has
    /// no effect.  Return the leftover of the trimmed buffer or default
    /// constructed `BlobBuffer` if nothing to trim.  Note that the length
    /// of the blob is unchanged, and that capacity buffers (i.e., of
    /// indices `numDataBuffers()` and higher) are *not* removed.
    BlobBuffer trimLastDataBuffer();

    /// Remove all blob buffers from this blob and move the buffers held by
    /// the specified `srcBlob` to this blob.  Note that this method is
    /// logically equivalent to:
    /// ```
    /// *this = *srcBlob;
    /// srcBlob->removeAll();
    /// ```
    /// but its implementation is more efficient.
    void moveBuffers(Blob *srcBlob);

    /// Remove all blob buffers from this blob and move the data buffers
    /// held by the specified `srcBlob` to this blob.
    void moveDataBuffers(Blob *srcBlob);

    /// Move the data buffers held by the specified `srcBlob` to this blob
    /// appending them to the current data buffers of this blob.  The
    /// behavior is undefined unless the total size of the resulting blob
    /// and the total number of buffers in this blob are less than or
    /// equal to `INT_MAX`.
    void moveAndAppendDataBuffers(Blob *srcBlob);

    // ACCESSORS

    /// Return the allocator used by this object to supply memory.
    bslma::Allocator *allocator() const;

    /// Return a reference to the non-modifiable blob buffer at the
    /// specified `index` in this blob.  The behavior is undefined unless
    /// `0 <= index < numBuffers()`.
    const BlobBuffer& buffer(int index) const;

    /// Return the factory used by this object.
    BlobBufferFactory *factory() const;

    /// Return the length of the last blob buffer in this blob, or 0 if this
    /// blob is of 0 length.
    int lastDataBufferLength() const;

    /// Return the length of this blob.
    int length() const;

    /// Return the number of blob buffers containing data in this blob.
    int numDataBuffers() const;

    /// Return the number of blob buffers held by this blob.
    int numBuffers() const;

    /// Return the sum of the sizes of all blob buffers in this blob (i.e.,
    /// the capacity of this blob).
    int totalSize() const;
};
}  // close package namespace

// TYPE TRAITS

namespace bslmf {

template <>
struct IsBitwiseMoveable<BloombergLP::bdlbb::Blob>
: IsBitwiseMoveable<bsl::vector<BloombergLP::bdlbb::BlobBuffer> >::type {
};
}  // close namespace bslmf

namespace bslma {

template <>
struct UsesBslmaAllocator<BloombergLP::bdlbb::Blob> : bsl::true_type {
};
}  // close namespace bslma

namespace bdlbb {

// FREE OPERATORS

/// Return `true` if the specified `lhs` and `rhs` blobs have the same
/// value, and `false` otherwise.  Two blobs have the same value if they
/// hold the same buffers, and have the same length.
bool operator==(const Blob& lhs, const Blob& rhs);

/// Return `true` if the specified `lhs` and `rhs` blobs do not have the
/// same value, and `false` otherwise.  Two blobs do not have the same value
/// if they do not hold the same buffers, or do not have the same length.
bool operator!=(const Blob& lhs, const Blob& rhs);

// FREE FUNCTIONS

/// Efficiently exchange the values of the specified `a` and `b` objects.
/// This method provides the no-throw exception-safety guarantee if both
/// objects were created with the same allocator.
void swap(Blob& a, Blob& b);

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

                              // ----------------
                              // class BlobBuffer
                              // ----------------

// CREATORS
inline
BlobBuffer::BlobBuffer()
: d_size(0)
{
}

inline
BlobBuffer::BlobBuffer(const bsl::shared_ptr<char>& buffer, int size)
: d_buffer(buffer)
, d_size(size)
{
    BSLS_ASSERT(0 <= size);
    BSLS_ASSERT(size == 0 || buffer);
}

inline
BlobBuffer::BlobBuffer(bslmf::MovableRef<bsl::shared_ptr<char> > buffer,
                       int                                       size)
: d_buffer(MoveUtil::move(buffer))
, d_size(size)
{
    BSLS_ASSERT(0 <= size);
    BSLS_ASSERT(size == 0 || d_buffer);
}

inline
BlobBuffer::BlobBuffer(const BlobBuffer& original)
: d_buffer(original.d_buffer)
, d_size(original.d_size)
{
}

inline
BlobBuffer::BlobBuffer(bslmf::MovableRef<BlobBuffer> original)
                                                          BSLS_KEYWORD_NOEXCEPT
: d_buffer(MoveUtil::move(MoveUtil::access(original).d_buffer))
, d_size(MoveUtil::move(MoveUtil::access(original).d_size))
{
    MoveUtil::access(original).d_size = 0;
}

inline
BlobBuffer::~BlobBuffer()
{
}

// MANIPULATORS
inline
bsl::shared_ptr<char>& BlobBuffer::buffer()
{
    return d_buffer;
}

inline
void BlobBuffer::setSize(int size)
{
    BSLS_ASSERT(0 <= size);

    d_size = size;
}

// ACCESSORS
inline
const bsl::shared_ptr<char>& BlobBuffer::buffer() const
{
    return d_buffer;
}

inline
char *BlobBuffer::data() const
{
    return d_buffer.get();
}

inline
int BlobBuffer::size() const
{
    return d_size;
}
}  // close package namespace

// FREE OPERATORS
inline
bool bdlbb::operator==(const BlobBuffer& lhs, const BlobBuffer& rhs)
{
    return lhs.d_buffer.get() == rhs.d_buffer.get() &&
           lhs.d_size == rhs.d_size;
}

inline
bool bdlbb::operator!=(const BlobBuffer& lhs, const BlobBuffer& rhs)
{
    return lhs.d_buffer.get() != rhs.d_buffer.get() ||
           lhs.d_size != rhs.d_size;
}

namespace bdlbb {

                                 // ----------
                                 // class Blob
                                 // ----------

// MANIPULATORS
inline
void Blob::appendBuffer(const BlobBuffer& buffer)
{
    BlobBuffer objectToMove(buffer);
    appendBuffer(MoveUtil::move(objectToMove));
}

inline
void Blob::appendDataBuffer(const BlobBuffer& buffer)
{
    BlobBuffer objectToMove(buffer);
    appendDataBuffer(MoveUtil::move(objectToMove));
}

inline
void Blob::insertBuffer(int index, const BlobBuffer& buffer)
{
    BlobBuffer objectToMove(buffer);
    insertBuffer(index, MoveUtil::move(objectToMove));
}

inline
void Blob::prependDataBuffer(const BlobBuffer& buffer)
{
    BlobBuffer objectToMove(buffer);
    prependDataBuffer(MoveUtil::move(objectToMove));
}

inline
void Blob::reserveBufferCapacity(int numBuffers)
{
    BSLS_ASSERT(0 <= numBuffers);

    typedef bsl::vector<BlobBuffer>::size_type size_t;
    size_t newCapacity = static_cast<size_t>(numBuffers);
    if (newCapacity > d_buffers.capacity()) {
        size_t geometric = d_buffers.capacity() * 2;
        newCapacity = geometric > newCapacity ? geometric : newCapacity;
        d_buffers.reserve(newCapacity);
    }
}

// ACCESSORS
inline
bslma::Allocator *Blob::allocator() const
{
    return d_buffers.get_allocator().mechanism();
}

inline
const BlobBuffer& Blob::buffer(int index) const
{
    BSLS_ASSERT_SAFE(0 <= index);
    BSLS_ASSERT_SAFE(index < static_cast<int>(d_buffers.size()));

    return d_buffers[index];
}

inline
BlobBufferFactory *Blob::factory() const
{
    return d_bufferFactory_p;
}

inline
int Blob::lastDataBufferLength() const
{
    return d_dataLength - d_preDataIndexLength;
}

inline
int Blob::length() const
{
    return d_dataLength;
}

inline
int Blob::numBuffers() const
{
    return static_cast<int>(d_buffers.size());
}

inline
int Blob::numDataBuffers() const
{
    return d_dataIndex + 1;
}

inline
int Blob::totalSize() const
{
    return d_totalSize;
}

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