// bdlma_sequentialallocator.h                                        -*-C++-*-
#ifndef INCLUDED_BDLMA_SEQUENTIALALLOCATOR
#define INCLUDED_BDLMA_SEQUENTIALALLOCATOR

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

//@PURPOSE: Provide a managed allocator using dynamically-allocated buffers.
//
//@CLASSES:
//   bdlma::SequentialAllocator: managed allocator using dynamic buffers
//
//@SEE_ALSO: bdlma_infrequentdeleteblocklist, bdlma_sequentialpool
//
//@DESCRIPTION: This component provides a concrete mechanism,
// 'bdlma::SequentialAllocator', that implements the 'bdlma::ManagedAllocator'
// protocol and efficiently allocates heterogeneous memory blocks (of varying,
// user-specified sizes) from a dynamically-allocated internal buffer:
//..
//   ,--------------------------.
//  ( bdlma::SequentialAllocator )
//   `--------------------------'
//                |         ctor/dtor
//                |         allocateAndExpand
//                |         reserveCapacity
//                |         rewind
//                |         truncate
//                V
//    ,-----------------------.
//   ( bdlma::ManagedAllocator )
//    `-----------------------'
//                |         release
//                V
//       ,----------------.
//      ( bslma::Allocator )
//       `----------------'
//                          allocate
//                          deallocate
//..
// If an allocation request exceeds the remaining free memory space in the
// internal buffer, the allocator either replenishes its buffer with new memory
// to satisfy the request, or returns a separate memory block, depending on
// whether the request size exceeds an optionally-specified maximum buffer
// size.  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.
//
// The main difference between a 'bdlma::SequentialAllocator' and a
// 'bdlma::SequentialPool' is that, very often, a 'bdlma::SequentialAllocator'
// is managed through a 'bslma::Allocator' pointer.  Hence, every call to the
// 'allocate' method invokes a virtual function call, which is slower than
// invoking the non-virtual 'allocate' method on a 'bdlma::SequentialPool'.
// However, since 'bslma::Allocator *' is widely used across BDE interfaces,
// 'bdlma::SequentialAllocator' is more general purpose than a
// 'bdlma::SequentialPool'.
//
///Optional 'initialSize' Parameter
///--------------------------------
// An optional 'initialSize' parameter can be supplied at construction to
// specify the initial size of the internal buffer.  If 'initialSize' is not
// supplied, an implementation-defined value is used for the initial size of
// the internal buffer.
//
///Optional 'maxBufferSize' Parameter
/// - - - - - - - - - - - - - - - - -
// If 'initialSize' is specified, an optional 'maxBufferSize' parameter can be
// supplied at construction to specify the maximum buffer size 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
// 'maxBufferSize >= initialSize'.  Note that 'reserveCapacity' always ensures
// that the requested number of bytes is available (allocating a new internal
// buffer if necessary) regardless of whether the size of the request exceeds
// 'maxBufferSize'.
//
///Optional 'growthStrategy' Parameter
///-----------------------------------
// An optional 'growthStrategy' parameter can be supplied at construction to
// specify the growth rate of the dynamically-allocated buffers.  The buffers
// can grow either geometrically or remain constant in size.  If
// 'growthStrategy' is not specified, geometric growth is used.  See
// 'bsls_blockgrowth' for more details.
//
///Optional 'alignmentStrategy' Parameter
///--------------------------------------
// An optional 'alignmentStrategy' parameter can be supplied at construction to
// specify the memory alignment strategy.  Allocated memory blocks can either
// follow maximum alignment, natural alignment, or 1-byte alignment.  If
// 'alignmentStrategy' is not specified, natural alignment is used.  See
// 'bsls_alignment' for more details.
//
///Usage
///-----
// Allocators are often supplied, at construction, to objects requiring
// dynamically-allocated memory.  For example, consider the following
// 'my_DoubleStack' class whose constructor takes a 'bslma::Allocator *':
//..
//  // my_doublestack.h
//  // ...
//
//  class my_DoubleStack {
//      // This class implements a stack that stores 'double' values.
//
//      // DATA
//      double           *d_stack_p;      // dynamically-allocated array
//      int               d_size;         // physical capacity of stack
//      int               d_length;       // next available index in stack
//      bslma::Allocator *d_allocator_p;  // memory allocator (held, not owned)
//
//    private:
//      // PRIVATE MANIPULATORS
//      void increaseCapacity();
//          // Increase the capacity of this stack by at least one element.
//
//      // Not implemented:
//      my_DoubleStack(const my_DoubleStack&);
//
//    public:
//      // CREATORS
//      explicit my_DoubleStack(bslma::Allocator *basicAllocator = 0);
//          // Create a stack that stores 'double' values.  Optionally specify
//          // a 'basicAllocator' used to supply memory.  If 'basicAllocator'
//          // is 0, the currently installed default allocator is used.
//
//      ~my_DoubleStack();
//          // Destroy this stack and all elements held by it.
//
//      // ...
//
//      // MANIPULATORS
//      void push(double value);
//          // Push the specified 'value' onto this stack.
//
//      // ...
//  };
//
//  // ...
//
//  // MANIPULATORS
//  inline
//  void my_DoubleStack::push(double value)
//  {
//      if (d_length >= d_size) {
//          increaseCapacity();
//      }
//      d_stack_p[d_length++] = value;
//  }
//
//  // ...
//
//  // my_doublestack.cpp
//
//  // PRIVATE MANIPULATORS
//  void my_DoubleStack::increaseCapacity()
//  {
//      // Implementation elided.
//      // ...
//  }
//
//  // CREATORS
//  my_DoubleStack::my_DoubleStack(bslma::Allocator *basicAllocator)
//  : d_size(1)
//  , d_length(0)
//  , d_allocator_p(bslma::Default::allocator(basicAllocator))
//  {
//      d_stack_p = static_cast<double *>(
//                        d_allocator_p->allocate(d_size * sizeof *d_stack_p));
//  }
//..
// Note that, when the allocator passed in is a 'bdlma::SequentialAllocator',
// the 'deallocate' method is a no-op, and all memory is reclaimed during the
// destruction of the allocator:
//..
//  my_DoubleStack::~my_DoubleStack()
//  {
//      // CLASS INVARIANTS
//      assert(d_allocator_p);
//      assert(d_stack_p);
//      assert(0 <= d_length);
//      assert(0 <= d_size);
//      assert(d_length <= d_size);
//
//      d_allocator_p->deallocate(d_stack_p);
//  }
//
//  // ...
//..
// In 'main', users can create a 'bdlma::SequentialAllocator' and pass it to
// the constructor of 'my_DoubleStack':
//..
//      bdlma::SequentialAllocator sequentialAlloc;
//      my_DoubleStack dstack(&sequentialAlloc);
//..

#include <bdlscm_version.h>

#include <bdlma_managedallocator.h>
#include <bdlma_sequentialpool.h>

#include <bslma_allocator.h>

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

namespace BloombergLP {
namespace bdlma {

                        // =========================
                        // class SequentialAllocator
                        // =========================

class SequentialAllocator : 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 a sequence of dynamically-allocated buffers.
    // Memory for the internal buffers is supplied by an (optional) allocator
    // supplied at construction; if no allocator is supplied, the currently
    // installed default allocator is used.  If an allocation exceeds the
    // remaining free memory space in the current buffer, the allocator
    // replenishes its internal buffer with new memory to satisfy the request.
    // This class is *exception* *neutral*: If memory cannot be allocated, the
    // behavior is defined by the (optional) allocator specified at
    // construction.

    // DATA
    SequentialPool d_sequentialPool;  // manager for allocated memory blocks

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

  public:
    // CREATORS
    explicit SequentialAllocator(bslma::Allocator *basicAllocator = 0);
    explicit SequentialAllocator(
                              bsls::BlockGrowth::Strategy  growthStrategy,
                              bslma::Allocator            *basicAllocator = 0);
    explicit SequentialAllocator(
                                bsls::Alignment::Strategy  alignmentStrategy,
                                bslma::Allocator          *basicAllocator = 0);
    SequentialAllocator(bsls::BlockGrowth::Strategy  growthStrategy,
                        bsls::Alignment::Strategy    alignmentStrategy,
                        bslma::Allocator            *basicAllocator = 0);
        // Create a sequential allocator for allocating memory blocks from a
        // sequence of dynamically-allocated buffers.  Optionally specify a
        // 'basicAllocator' used to supply memory for the dynamically-allocated
        // buffers.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.  Optionally specify a 'growthStrategy' used to
        // control buffer growth.  If no 'growthStrategy' is specified,
        // geometric growth is used.  Optionally specify an 'alignmentStrategy'
        // used to control alignment of allocated memory blocks.  If no
        // 'alignmentStrategy' is specified, natural alignment is used.  Note
        // that no limit is imposed on the size of the internal buffers when
        // geometric growth is used.

    explicit SequentialAllocator(int initialSize);
    explicit SequentialAllocator(bsls::Types::size_type  initialSize,
                                 bslma::Allocator       *basicAllocator = 0);
    SequentialAllocator(bsls::Types::size_type       initialSize,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        bslma::Allocator            *basicAllocator = 0);
    SequentialAllocator(bsls::Types::size_type     initialSize,
                        bsls::Alignment::Strategy  alignmentStrategy,
                        bslma::Allocator          *basicAllocator = 0);
    SequentialAllocator(bsls::Types::size_type       initialSize,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        bsls::Alignment::Strategy    alignmentStrategy,
                        bslma::Allocator            *basicAllocator = 0);
        // Create a sequential allocator for allocating memory blocks from a
        // sequence of dynamically-allocated buffers, of which the initial
        // buffer has the specified 'initialSize' (in bytes).  Optionally
        // specify a 'basicAllocator' used to supply memory for the
        // dynamically-allocated buffers.  If 'basicAllocator' is 0, the
        // currently installed default allocator is used.  Optionally specify a
        // 'growthStrategy' used to control buffer growth.  If no
        // 'growthStrategy' is specified, geometric growth is used.  Optionally
        // specify an 'alignmentStrategy' used to control alignment of
        // allocated memory blocks.  If no 'alignmentStrategy' is specified,
        // natural alignment is used.  An implementation-defined value is used
        // as the initial size of the internal buffer.  The behavior is
        // undefined unless '0 < initialSize'.  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 the
        // implementation-defined value.  Also note that
        // 'SequentialAllocator(int initialSize)' is provided to avoid
        // ambiguous definitions.

    SequentialAllocator(bsls::Types::size_type  initialSize,
                        bsls::Types::size_type  maxBufferSize,
                        bslma::Allocator       *basicAllocator = 0);
    SequentialAllocator(bsls::Types::size_type       initialSize,
                        bsls::Types::size_type       maxBufferSize,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        bslma::Allocator            *basicAllocator = 0);
    SequentialAllocator(bsls::Types::size_type     initialSize,
                        bsls::Types::size_type     maxBufferSize,
                        bsls::Alignment::Strategy  alignmentStrategy,
                        bslma::Allocator          *basicAllocator = 0);
    SequentialAllocator(bsls::Types::size_type       initialSize,
                        bsls::Types::size_type       maxBufferSize,
                        bsls::BlockGrowth::Strategy  growthStrategy,
                        bsls::Alignment::Strategy    alignmentStrategy,
                        bslma::Allocator            *basicAllocator = 0);
        // Create a sequential allocator for allocating memory blocks from a
        // sequence of dynamically-allocated buffers, of which the initial
        // buffer has the specified 'initialSize' (in bytes), and the buffer
        // growth is limited to the specified 'maxBufferSize'.  Optionally
        // specify a 'basicAllocator' used to supply memory for the
        // dynamically-allocated buffers.  If 'basicAllocator' is 0, the
        // currently installed default allocator is used.  Optionally specify a
        // 'growthStrategy' used to control buffer growth.  If no
        // 'growthStrategy' is specified, geometric growth is used.  Optionally
        // specify an 'alignmentStrategy' used to control alignment of
        // allocated memory blocks.  If no 'alignmentStrategy' is specified,
        // natural alignment is used.  The behavior is undefined unless
        // '0 < initialSize' and 'initialSize <= maxBufferSize'.  Note that
        // when constant growth is used, the size of the internal buffers will
        // always be the same as 'initialSize'.

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

    // MANIPULATORS
    virtual void *allocate(bsls::Types::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 current internal buffer, use the allocator
        // supplied at construction to allocate a new internal buffer, then
        // allocate memory from the new buffer.

    void *allocateAndExpand(bsls::Types::size_type *size);
        // Return the address of a contiguous block of memory of at least the
        // specified '*size' (in bytes), and load the actual amount of memory
        // allocated into '*size'.  If '*size' is 0, return 0 with no effect.
        // If the allocation request exceeds the remaining free memory space in
        // the current internal buffer, use the allocator supplied at
        // construction to allocate a new internal buffer, then allocate memory
        // from the new buffer.

    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.

    virtual void release();
        // Release all memory allocated through this allocator and return to
        // the underlying allocator *all* memory.  The allocator is reset to
        // its default-constructed state, 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.

    void reserveCapacity(bsls::Types::size_type numBytes);
        // Reserve sufficient memory to satisfy allocation requests for at
        // least the specified 'numBytes' without replenishment (i.e., without
        // dynamic allocation).  If 'numBytes' is 0, no memory is reserved.
        // Note that, when the 'numBytes' is distributed over multiple
        // 'allocate' requests - due to alignment effects - it is possible that
        // not all 'numBytes' of memory will be used for allocation before
        // triggering dynamic allocation.

    bsls::Types::size_type truncate(void                   *address,
                                    bsls::Types::size_type  originalSize,
                                    bsls::Types::size_type  newSize);
        // Reduce the amount of memory allocated at the specified 'address' of
        // the specified 'originalSize' (in bytes) to the specified 'newSize'.
        // Return 'newSize' after truncating, or 'originalSize' if the memory
        // block at 'address' cannot be truncated.  This method can only
        // 'truncate' the memory block returned by the most recent 'allocate'
        // request from this allocator, and otherwise has no effect.  The
        // behavior is undefined unless the memory block at 'address' was
        // originally allocated by this allocator, the size of the memory block
        // at 'address' is 'originalSize', 'newSize <= originalSize', and
        // 'release' was not called after allocating the memory block at
        // 'address'.
};

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

                        // -------------------------
                        // class SequentialAllocator
                        // -------------------------

// CREATORS
inline
SequentialAllocator::
SequentialAllocator(bslma::Allocator *basicAllocator)
: d_sequentialPool(basicAllocator)
{
}

inline
SequentialAllocator::
SequentialAllocator(bsls::BlockGrowth::Strategy  growthStrategy,
                    bslma::Allocator            *basicAllocator)
: d_sequentialPool(growthStrategy, basicAllocator)
{
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Alignment::Strategy  alignmentStrategy,
                    bslma::Allocator          *basicAllocator)
: d_sequentialPool(alignmentStrategy, basicAllocator)
{
}

inline
SequentialAllocator::
SequentialAllocator(bsls::BlockGrowth::Strategy  growthStrategy,
                    bsls::Alignment::Strategy    alignmentStrategy,
                    bslma::Allocator            *basicAllocator)
: d_sequentialPool(growthStrategy, alignmentStrategy, basicAllocator)
{
}

inline
SequentialAllocator::
SequentialAllocator(int initialSize)
: d_sequentialPool(initialSize)
{
    BSLS_ASSERT(0 < initialSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type  initialSize,
                    bslma::Allocator       *basicAllocator)
: d_sequentialPool(initialSize, basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type       initialSize,
                    bsls::BlockGrowth::Strategy  growthStrategy,
                    bslma::Allocator            *basicAllocator)
: d_sequentialPool(initialSize, growthStrategy, basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type     initialSize,
                    bsls::Alignment::Strategy  alignmentStrategy,
                    bslma::Allocator          *basicAllocator)
: d_sequentialPool(initialSize, alignmentStrategy, basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type       initialSize,
                    bsls::BlockGrowth::Strategy  growthStrategy,
                    bsls::Alignment::Strategy    alignmentStrategy,
                    bslma::Allocator            *basicAllocator)
: d_sequentialPool(initialSize,
                   growthStrategy,
                   alignmentStrategy,
                   basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type  initialSize,
                    bsls::Types::size_type  maxBufferSize,
                    bslma::Allocator       *basicAllocator)
: d_sequentialPool(initialSize, maxBufferSize, basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
    BSLS_ASSERT(initialSize <= maxBufferSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type       initialSize,
                    bsls::Types::size_type       maxBufferSize,
                    bsls::BlockGrowth::Strategy  growthStrategy,
                    bslma::Allocator            *basicAllocator)
: d_sequentialPool(initialSize, maxBufferSize, growthStrategy, basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
    BSLS_ASSERT(initialSize <= maxBufferSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type     initialSize,
                    bsls::Types::size_type     maxBufferSize,
                    bsls::Alignment::Strategy  alignmentStrategy,
                    bslma::Allocator          *basicAllocator)
: d_sequentialPool(initialSize,
                   maxBufferSize,
                   alignmentStrategy,
                   basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
    BSLS_ASSERT(initialSize <= maxBufferSize);
}

inline
SequentialAllocator::
SequentialAllocator(bsls::Types::size_type       initialSize,
                    bsls::Types::size_type       maxBufferSize,
                    bsls::BlockGrowth::Strategy  growthStrategy,
                    bsls::Alignment::Strategy    alignmentStrategy,
                    bslma::Allocator            *basicAllocator)
: d_sequentialPool(initialSize,
                   maxBufferSize,
                   growthStrategy,
                   alignmentStrategy,
                   basicAllocator)
{
    BSLS_ASSERT(0 < initialSize);
    BSLS_ASSERT(initialSize <= maxBufferSize);
}

// MANIPULATORS
inline
void *SequentialAllocator::allocate(bsls::Types::size_type size)
{
    return d_sequentialPool.allocate(size);
}

inline
void *SequentialAllocator::allocateAndExpand(bsls::Types::size_type *size)
{
    return d_sequentialPool.allocateAndExpand(size);
}

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

inline
void SequentialAllocator::release()
{
    d_sequentialPool.release();
}

inline
void SequentialAllocator::reserveCapacity(bsls::Types::size_type numBytes)
{
    d_sequentialPool.reserveCapacity(numBytes);
}

inline
void SequentialAllocator::rewind()
{
    d_sequentialPool.rewind();
}

inline
bsls::Types::size_type SequentialAllocator::truncate(
                                          void                   *address,
                                          bsls::Types::size_type  originalSize,
                                          bsls::Types::size_type  newSize)
{
    return d_sequentialPool.truncate(address, originalSize, newSize);
}

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