// 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). Thus, 'bdlbb::Blob' is an // advantageous replacement for 'bdlbb::PooledBufferChain' when manipulation of // the sequence, sharing of portions of the sequence, and lifetime management // of individual portions of the sequence, are desired. Another added // flexibility of 'bdlbb::Blob' is the possibility for buffers in the sequence // to have different sizes (as opposed to a uniform fixed size for // 'bdlbb::PooledBufferChain'). When choosing whether to use a 'bdlbb::Blob' // vs. a 'bdlbb::PooledBufferChain', one must consider the added flexibility // versus the added cost of shared ownership for each individual buffer and // random access to the buffer. // ///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: //.. // class SimpleBlobBufferFactory : public bdlbb::BlobBufferFactory { // // This factory creates blob buffers of a fixed size specified at // // construction. // // // 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): //.. // void prependProlog(bdlbb::Blob *blob, // const char *prolog, // int length, // bslma::Allocator *allocator = 0); // // 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'. // // 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); // // 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. // // int timestampMessage(bdlbb::Blob *blob, 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. //.. // 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(×tampBuffer); // // 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 // ================ 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. // PRIVATE TYPES typedef bslmf::MovableRefUtil MoveUtil; // Used in move construction and assignment to make lines shorter. // 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 BlobBuffer(); // 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(const bsl::shared_ptr<char>& buffer, int size); // 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(bslmf::MovableRef<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(const BlobBuffer& original); // Create a blob buffer having the same value as the specified // 'original' blob buffer. BlobBuffer(bslmf::MovableRef<BlobBuffer> original) BSLS_KEYWORD_NOEXCEPT; // 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(); // Destroy this blob buffer. // MANIPULATORS BlobBuffer& operator=(const BlobBuffer& rhs); // Assign to this blob buffer the value of the specified 'rhs' blob // buffer, and return a reference to this modifiable blob buffer. BlobBuffer& operator=(bslmf::MovableRef<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. void reset(); // Reset this blob buffer to its default-constructed state. void reset(const bsl::shared_ptr<char>& buffer, int size); // 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(bslmf::MovableRef<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. bsl::shared_ptr<char>& buffer(); // Return a reference to the shared pointer to the modifiable buffer // represented by this object. void setSize(int size); // 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 swap(BlobBuffer& other); // Efficiently exchange the value of this object with the value of the // specified 'other' object. This method provides the no-throw // exception-safety guarantee. BlobBuffer trim(int toSize); // Reduce this buffer to the specified 'toSize' and return the // leftover. The behaviour is undefined unless '0 <= toSize && toSize // <= size()'. // ACCESSORS const bsl::shared_ptr<char>& buffer() const; // Return a reference to the non-modifiable shared pointer to the // buffer represented by this object. char *data() const; // Return the address of the modifiable buffer represented by this // object. int size() const; // Return the size of the buffer represented by this object. bsl::ostream& print(bsl::ostream& stream, int level = 0, int spacesPerLevel = 4) 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. }; } // 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 bool operator==(const BlobBuffer& lhs, const BlobBuffer& rhs); // 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. bsl::ostream& operator<<(bsl::ostream& stream, const BlobBuffer& buffer); // Format the specified blob 'buffer' to the specified output 'stream', and // return a reference to the modifiable 'stream'. // FREE FUNCTIONS void swap(BlobBuffer& a, BlobBuffer& b); // Efficiently exchange the values of the specified 'a' and 'b' objects. // This method provides the no-throw exception-safety guarantee. // ======================= // class BlobBufferFactory // ======================= class BlobBufferFactory { // This class defines a base-level protocol for a 'BlobBuffer' factory. public: // CREATORS virtual ~BlobBufferFactory(); // Destroy this blob buffer factory. // MANIPULATORS virtual void allocate(BlobBuffer *buffer) = 0; // Allocate a blob buffer from this blob buffer factory, and load it // into the specified 'buffer'. }; // ========== // class Blob // ========== 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. // PRIVATE TYPES typedef bslmf::MovableRefUtil MoveUtil; // Used in move construction and assignment to make lines shorter. // 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 void slowSetLength(int length); // 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. // PRIVATE ACCESSORS int assertInvariants() const; // Assert the invariants of this object and return 0 on success. public: // CREATORS explicit Blob(bslma::Allocator *basicAllocator = 0); // 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(BlobBufferFactory *factory, 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. Blob(const BlobBuffer *buffers, int numBuffers, 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 Blob& original, 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, 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(bslmf::MovableRef<Blob> original) BSLS_KEYWORD_NOEXCEPT; // 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, bslma::Allocator *basicAllocator); // 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(); // Destroy this blob. // MANIPULATORS Blob& operator=(const Blob& rhs); // Assign to this blob the value of the specified 'rhs' blob, and // return a reference to this modifiable blob. Blob& operator=(bslmf::MovableRef<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. void appendBuffer(const BlobBuffer& buffer); // 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(bslmf::MovableRef<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 appendDataBuffer(const 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(bslmf::MovableRef<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 insertBuffer(int index, const 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 _unchanged_ 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, bslmf::MovableRef<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 _unchanged_ 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 prependDataBuffer(const 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(bslmf::MovableRef<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 removeAll(); // Remove all blob buffers from this blob, and set its length to 0. void removeBuffer(int index); // 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 removeBuffers(int index, int numBuffers); // 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 removeUnusedBuffers(); // 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 replaceDataBuffer(int index, const BlobBuffer& buffer); // 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 reserveBufferCapacity(int numBuffers); // 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 setLength(int length); // 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 swap(Blob& other); // 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 swapBufferRaw(int index, BlobBuffer *srcBuffer); // 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. BlobBuffer trimLastDataBuffer(); // 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. void moveBuffers(Blob *srcBlob); // 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 moveDataBuffers(Blob *srcBlob); // Remove all blob buffers from this blob and move the data buffers // held by the specified 'srcBlob' to this blob. void moveAndAppendDataBuffers(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'. // ACCESSORS bslma::Allocator *allocator() const; // Return the allocator used by this object to supply memory. const BlobBuffer& buffer(int index) 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()'. BlobBufferFactory *factory() const; // Return the factory used by this object. int lastDataBufferLength() const; // Return the length of the last blob buffer in this blob, or 0 if this // blob is of 0 length. int length() const; // Return the length of this blob. int numDataBuffers() const; // Return the number of blob buffers containing data in this blob. int numBuffers() const; // Return the number of blob buffers held by this blob. int totalSize() const; // Return the sum of the sizes of all blob buffers in this blob (i.e., // the capacity of this blob). }; } // 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 bool operator==(const Blob& lhs, const Blob& rhs); // 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. // FREE FUNCTIONS void swap(Blob& a, Blob& b); // 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. // ============================================================================ // 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 ----------------------------------