// bdlma_bufferedsequentialallocator.h                                -*-C++-*-
#ifndef INCLUDED_BDLMA_BUFFEREDSEQUENTIALALLOCATOR
#define INCLUDED_BDLMA_BUFFEREDSEQUENTIALALLOCATOR

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

//@PURPOSE: Provide an efficient managed allocator using an external buffer.
//
//@CLASSES:
//  bdlma::BufferedSequentialAllocator: allocator using an external buffer
//
//@SEE_ALSO: bdlma_bufferedsequentialpool, bdlma_sequentialallocator
//
//@DESCRIPTION: This component provides a concrete mechanism,
// 'bdlma::BufferedSequentialAllocator', that implements the
// 'bdlma::ManagedAllocator' protocol to very efficiently allocate
// heterogeneous memory blocks (of varying, user-specified sizes) from an
// external buffer supplied at construction:
//..
//   ,----------------------------------.
//  ( bdlma::BufferedSequentialAllocator )
//   `----------------------------------'
//                   |        ctor/dtor
//                   |        rewind
//                   |
//                   V
//       ,-----------------------.
//      ( bdlma::ManagedAllocator )
//       `-----------------------'
//                   |        release
//                   V
//          ,----------------.
//         ( bslma::Allocator )
//          `----------------'
//                            allocate
//                            deallocate
//..
// If an allocation request exceeds the remaining free memory space in the
// external buffer, the allocator will fall back to a sequence of
// dynamically-allocated buffers.  Users can optionally specify a growth
// strategy at construction that governs the growth rate of the
// dynamically-allocated buffers.  If no growth strategy is specified at
// construction, geometric growth is used.  Users can also optionally specify
// an alignment strategy at construction that governs the alignment of
// allocated memory blocks.  If no alignment strategy is specified at
// construction, natural alignment is used.  The 'release' method releases all
// memory allocated through the allocator, as does the destructor.  The
// 'rewind' method releases all memory allocated through the allocator and
// returns to the underlying allocator *only* memory that was allocated outside
// of the typical internal buffer growth of the allocator (i.e., large blocks).
// Note that individually allocated memory blocks cannot be separately
// deallocated.
//
// 'bdlma::BufferedSequentialAllocator' is typically used when users have a
// reasonable estimation of the amount of memory needed.  This amount of memory
// would typically be created directly on the program stack, and used as the
// initial external buffer of the allocator for very fast memory allocation.
// While the buffer has sufficient capacity, memory allocations using the pool
// will not trigger *any* dynamic memory allocation, will have optimal locality
// of reference, and will not require deallocation upon destruction.
//
// Once the external buffer is exhausted, subsequent allocation requests
// require dynamic memory allocation, and the performance of the allocator
// degrades.
//
// The main difference between a 'bdlma::BufferedSequentialAllocator' and a
// 'bdlma::BufferedSequentialPool' is that, very often, the allocator is
// maintained through a 'bslma::Allocator' pointer - hence, every call to
// 'allocate' is a virtual function call, which is slower than invoking
// 'allocate' with the pool directly.  However, the allocator interface is much
// more widely accepted across objects, and hence more general purpose.
//
///Optional 'maxBufferSize' Parameter
/// - - - - - - - - - - - - - - - - -
// An optional 'maxBufferSize' parameter can be supplied at construction to
// specify the maximum size (in bytes) of the dynamically-allocated buffers for
// geometric growth.  Once the internal buffer grows up to the 'maxBufferSize',
// further requests that exceed this size will be served by a separate memory
// block instead of the internal buffer.  The behavior is undefined unless
// 'size <= maxBufferSize', where 'size' is the extent (in bytes) of the
// external buffer supplied at construction.
//
///Warning
///-------
// Note that, even when a buffer having 'n' bytes of memory is supplied at
// construction, it does *not* mean that 'n' bytes of memory are available
// before dynamic memory allocation is triggered.  This is due to memory
// alignment requirements.  If the buffer supplied is not aligned, the first
// call to the 'allocate' method will automatically skip one or more bytes such
// that the memory allocated is properly aligned.  The number of bytes that are
// wasted depends on whether natural alignment, maximum alignment, or 1-byte
// alignment is used (see 'bsls_alignment' for more details).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using 'bdlma::BufferedSequentialAllocator' with Exact Calculation
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we need to implement a method, 'calculate', that performs
// calculations (where the specifics are not important to illustrate the use of
// this component), which require three vectors of 'double' values.
// Furthermore, suppose we know that we need to store at most 100 values for
// each vector:
//..
//  double calculate(const bsl::vector<double>& data)
//  {
//..
// Since the amount of memory needed is known in advance, we can optimize the
// memory allocation by using a 'bdlma::BufferedSequentialAllocator' to supply
// memory for the vectors.  We can also prevent the vectors from resizing
// (which triggers more allocations) by reserving for the specific capacity we
// need:
//..
//      enum { k_SIZE = 3 * 100 * sizeof(double) };
//..
// In the above calculation, we assume that the only memory allocation
// requested by the vector is the allocation for the array that stores the
// 'double' values.  Furthermore, we assume that the 'reserve' method allocates
// the exact amount of memory for the number of items specified (in this case,
// of type 'double').  Note that both of these assumptions are true for BDE's
// implementation of 'bsl::vector'.
//
// To avoid alignment issues described in the "Warning" section (above), we
// create a 'bsls::AlignedBuffer':
//..
//      bsls::AlignedBuffer<k_SIZE> bufferStorage;
//
//      bdlma::BufferedSequentialAllocator alloc(bufferStorage.buffer(),
//                                               k_SIZE);
//
//      bsl::vector<double> v1(&alloc);     v1.reserve(100);
//      bsl::vector<double> v2(&alloc);     v2.reserve(100);
//      bsl::vector<double> v3(&alloc);     v3.reserve(100);
//
//      return data.empty() ? 0.0 : data.front();
//  }
//..
// By making use of a 'bdlma::BufferedSequentialAllocator', *all* dynamic
// memory allocation is eliminated in the above example.
//
///Example 2: Using 'bdlma::BufferedSequentialAllocator' with Fallback
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Suppose we are receiving updates for price quotes for a list of securities
// through the following function:
//..
//  void receivePriceQuotes(bsl::map<bsl::string, double> *updateMap);
//      // Load into the specified 'updateMap' updates for price quotes for a
//      // list of securities.
//..
// Furthermore, suppose the number of securities we are interested in is
// limited.  We can then use a 'bdlma::BufferedSequentialAllocator' to optimize
// memory allocation for the 'bsl::map'.  We first create a buffer on the
// stack:
//..
//  enum {
//      k_NUM_SECURITIES = 100,
//
//      k_TREE_NODE_SIZE = sizeof(bsl::map<bsl::string, double>::value_type)
//                       + sizeof(void *) * 4,
//
//      k_AVERAGE_SECURITY_LENGTH = 5,
//
//      k_TOTAL_SIZE = k_NUM_SECURITIES *
//                               (k_TREE_NODE_SIZE + k_AVERAGE_SECURITY_LENGTH)
//  };
//
//  bsls::AlignedBuffer<k_TOTAL_SIZE> bufferStorage;
//..
// The calculation of the amount of memory needed is just an estimate, as we
// used the average security size instead of the maximum security size.  We
// also assume that a 'bsl::map's node size is roughly the size of 4 pointers.
//..
//  bdlma::BufferedSequentialAllocator bsa(bufferStorage.buffer(),
//                                         k_TOTAL_SIZE,
//                                         &objectAllocator);
//  bsl::map<bsl::string, double> updateMap(&bsa);
//
//  receivePriceQuotes(&updateMap);
//..
// With the use of a 'bdlma::BufferedSequentialAllocator', we can be reasonably
// assured that the memory allocation performance is optimized (i.e., minimal
// use of dynamic allocation).

#include <bdlscm_version.h>

#include <bdlma_bufferedsequentialpool.h>
#include <bdlma_managedallocator.h>

#include <bslma_allocator.h>

#include <bsls_alignment.h>
#include <bsls_blockgrowth.h>
#include <bsls_performancehint.h>
#include <bsls_types.h>

namespace BloombergLP {
namespace bdlma {

                    // =================================
                    // class BufferedSequentialAllocator
                    // =================================

class BufferedSequentialAllocator : public ManagedAllocator {
    // This class implements the 'ManagedAllocator' protocol to provide a fast
    // allocator that dispenses heterogeneous blocks of memory (of varying,
    // user-specified sizes) from an external buffer whose address and size (in
    // bytes) are supplied at construction.  If an allocation request exceeds
    // the remaining free memory space in the external buffer, memory will be
    // supplied by an (optional) allocator also supplied at construction; if no
    // allocator is supplied, the currently installed default allocator is
    // used.  This class is *exception* *neutral*: If memory cannot be
    // allocated, the behavior is defined by the (optional) allocator supplied
    // at construction.  Note that in no case will the buffered sequential
    // allocator attempt to deallocate the external buffer.

    // DATA
    BufferedSequentialPool d_pool;  // manager for allocated memory blocks

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

  public:
    // CREATORS
    BufferedSequentialAllocator(char                   *buffer,
                                bsls::Types::size_type  size,
                                bslma::Allocator       *basicAllocator = 0);
    BufferedSequentialAllocator(
                              char                        *buffer,
                              bsls::Types::size_type       size,
                              bsls::BlockGrowth::Strategy  growthStrategy,
                              bslma::Allocator            *basicAllocator = 0);
    BufferedSequentialAllocator(char                      *buffer,
                                bsls::Types::size_type     size,
                                bsls::Alignment::Strategy  alignmentStrategy,
                                bslma::Allocator          *basicAllocator = 0);
    BufferedSequentialAllocator(
                              char                        *buffer,
                              bsls::Types::size_type       size,
                              bsls::BlockGrowth::Strategy  growthStrategy,
                              bsls::Alignment::Strategy    alignmentStrategy,
                              bslma::Allocator            *basicAllocator = 0);
        // Create a buffered sequential allocator for allocating memory blocks
        // from the specified external 'buffer' having the specified 'size' (in
        // bytes).  Optionally specify a 'growthStrategy' used to control
        // buffer growth.  If a 'growthStrategy' is not specified, geometric
        // growth is used.  Optionally specify an 'alignmentStrategy' used to
        // align allocated memory blocks.  If an 'alignmentStrategy' is not
        // specified, natural alignment is used.  Optionally specify a
        // 'basicAllocator' used to supply memory should the capacity of
        // 'buffer' be exhausted.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.  The behavior is undefined
        // unless '0 < size', and 'buffer' has at least 'size' bytes.  Note
        // that, due to alignment effects, it is possible that not all 'size'
        // bytes of memory in 'buffer' can be used for allocation.  Also note
        // that no limit is imposed on the size of the internal buffers when
        // geometric growth is used.  Also note that when constant growth is
        // used, the size of the internal buffers will always be the same as
        // 'size'.

    BufferedSequentialAllocator(char                   *buffer,
                                bsls::Types::size_type  size,
                                bsls::Types::size_type  maxBufferSize,
                                bslma::Allocator       *basicAllocator = 0);
    BufferedSequentialAllocator(
                              char                        *buffer,
                              bsls::Types::size_type       size,
                              bsls::Types::size_type       maxBufferSize,
                              bsls::BlockGrowth::Strategy  growthStrategy,
                              bslma::Allocator            *basicAllocator = 0);
    BufferedSequentialAllocator(char                      *buffer,
                                bsls::Types::size_type     size,
                                bsls::Types::size_type     maxBufferSize,
                                bsls::Alignment::Strategy  alignmentStrategy,
                                bslma::Allocator          *basicAllocator = 0);
    BufferedSequentialAllocator(
                              char                        *buffer,
                              bsls::Types::size_type       size,
                              bsls::Types::size_type       maxBufferSize,
                              bsls::BlockGrowth::Strategy  growthStrategy,
                              bsls::Alignment::Strategy    alignmentStrategy,
                              bslma::Allocator            *basicAllocator = 0);
        // Create a buffered sequential allocator for allocating memory blocks
        // from the specified external 'buffer' having the specified 'size' (in
        // bytes), or from an internal buffer (after the external 'buffer' is
        // exhausted) where the buffer growth is limited to the specified
        // 'maxBufferSize' (in bytes).  Optionally specify a 'growthStrategy'
        // used to control buffer growth.  If a 'growthStrategy' is not
        // specified, geometric growth is used.  Optionally specify an
        // 'alignmentStrategy' used to align allocated memory blocks.  If an
        // 'alignmentStrategy' is not specified, natural alignment is used.
        // Optionally specify a 'basicAllocator' used to supply memory should
        // the capacity of 'buffer' be exhausted.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  The behavior is
        // undefined unless '0 < size', 'size <= maxBufferSize', and 'buffer'
        // has at least 'size' bytes.  Note that, due to alignment effects, it
        // is possible that not all 'size' bytes of memory in 'buffer' can be
        // used for allocation.  Also note that when constant growth is used,
        // the size of the internal buffers will always be the same as 'size'.

    virtual ~BufferedSequentialAllocator();
        // Destroy this buffered sequential allocator.  All memory allocated
        // from this allocator is released.

    // MANIPULATORS
    virtual void *allocate(size_type size);
        // Return the address of a contiguous block of memory of the specified
        // 'size' (in bytes) according to the alignment strategy specified at
        // construction.  If 'size' is 0, no memory is allocated and 0 is
        // returned.  If the allocation request exceeds the remaining free
        // memory space in the external buffer supplied at construction, use
        // memory obtained from the allocator supplied at construction.

    virtual void deallocate(void *address);
        // This method has no effect on the memory block at the specified
        // 'address' as all memory allocated by this allocator is managed.  The
        // behavior is undefined unless 'address' is 0, or was allocated by
        // this allocator and has not already been deallocated.  The effect of
        // using 'address' after this call is undefined.

    virtual void release();
        // Release all memory allocated through this allocator and return to
        // the underlying allocator *all* memory except the external buffer
        // supplied at construction.  The allocator is reset to its
        // default-constructed state, making the memory from the entire
        // external buffer supplied at construction available for subsequent
        // allocations, retaining the alignment and growth strategies, and the
        // initial and maximum buffer sizes in effect following construction.
        // The effect of subsequently - to this invokation of 'release' - using
        // a pointer obtained from this object prior to this call to 'release'
        // is undefined.

    virtual void rewind();
        // Release all memory allocated through this allocator and return to
        // the underlying allocator *only* memory that was allocated outside of
        // the typical internal buffer growth of this allocator (i.e., large
        // blocks).  All retained memory will be used to satisfy subsequent
        // allocations.  The effect of subsequently - to this invokation of
        // 'rewind' - using a pointer obtained from this object prior to this
        // call to 'rewind' is undefined.
};

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

                    // ---------------------------------
                    // class BufferedSequentialAllocator
                    // ---------------------------------

// CREATORS
inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                        char                   *buffer,
                                        bsls::Types::size_type  size,
                                        bslma::Allocator       *basicAllocator)
: d_pool(buffer, size, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                   char                        *buffer,
                                   bsls::Types::size_type       size,
                                   bsls::BlockGrowth::Strategy  growthStrategy,
                                   bslma::Allocator            *basicAllocator)
: d_pool(buffer, size, growthStrategy, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                  char                      *buffer,
                                  bsls::Types::size_type     size,
                                  bsls::Alignment::Strategy  alignmentStrategy,
                                  bslma::Allocator          *basicAllocator)
: d_pool(buffer, size, alignmentStrategy, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                char                        *buffer,
                                bsls::Types::size_type       size,
                                bsls::BlockGrowth::Strategy  growthStrategy,
                                bsls::Alignment::Strategy    alignmentStrategy,
                                bslma::Allocator            *basicAllocator)
: d_pool(buffer, size, growthStrategy, alignmentStrategy, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                        char                   *buffer,
                                        bsls::Types::size_type  size,
                                        bsls::Types::size_type  maxBufferSize,
                                        bslma::Allocator       *basicAllocator)
: d_pool(buffer, size, maxBufferSize, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                   char                        *buffer,
                                   bsls::Types::size_type       size,
                                   bsls::Types::size_type       maxBufferSize,
                                   bsls::BlockGrowth::Strategy  growthStrategy,
                                   bslma::Allocator            *basicAllocator)
: d_pool(buffer, size, maxBufferSize, growthStrategy, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                  char                      *buffer,
                                  bsls::Types::size_type     size,
                                  bsls::Types::size_type     maxBufferSize,
                                  bsls::Alignment::Strategy  alignmentStrategy,
                                  bslma::Allocator          *basicAllocator)
: d_pool(buffer, size, maxBufferSize, alignmentStrategy, basicAllocator)
{
}

inline
BufferedSequentialAllocator::BufferedSequentialAllocator(
                                char                        *buffer,
                                bsls::Types::size_type       size,
                                bsls::Types::size_type       maxBufferSize,
                                bsls::BlockGrowth::Strategy  growthStrategy,
                                bsls::Alignment::Strategy    alignmentStrategy,
                                bslma::Allocator            *basicAllocator)
: d_pool(buffer,
         size,
         maxBufferSize,
         growthStrategy,
         alignmentStrategy,
         basicAllocator)
{
}

// MANIPULATORS
inline
void *BufferedSequentialAllocator::allocate(size_type size)
{
    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(0 == size)) {
        BSLS_PERFORMANCEHINT_UNLIKELY_HINT;
        return 0;                                                     // RETURN
    }

    return d_pool.allocate(size);
}

inline
void BufferedSequentialAllocator::deallocate(void *)
{
}

inline
void BufferedSequentialAllocator::release()
{
    d_pool.release();
}

inline
void BufferedSequentialAllocator::rewind()
{
    d_pool.rewind();
}

}  // close package namespace
}  // close enterprise namespace

#endif

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