Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslma_sequentialallocator
[Package bslma]

Support fast memory allocation for objects of varying sizes. More...

Namespaces

namespace  bslma

Detailed Description

Outline
Purpose:
Support fast memory allocation for objects of varying sizes.
Deprecated:
Use bdlma_bufferedsequentialallocator instead.
Classes:
bslma::SequentialAllocator fast variable-size memory allocator
See also:
Component bdlma_sequentialallocator, Component bdlma_bufferedsequentialallocator
Description:
This component provides an allocator, bslma::SequentialAllocator, that implements the bslma::ManagedAllocator protocol and allocates memory blocks of any requested size, from an internal buffer (pool) or a user-supplied buffer. If an allocation request exceeds the remaining free memory space in the pool, the pool either 1) replenishes its buffer with new memory to satisfy the request, or 2) returns a separate memory block, depending on whether the request size exceeds an optionally specified maximum buffer size. By default, buffer growth is not capped. The release method releases all memory allocated through this allocator, as does the destructor. Note, however, that individual allocated blocks of memory cannot be separately deallocated.
   ,--------------------------.
  ( bslma::SequentialAllocator )
   `--------------------------'
                |         ctor/dtor
                |         allocateAndExpand
                |         expand
                |         reserveCapacity
                |         truncate
                V
    ,-----------------------.
   ( bslma::ManagedAllocator )
    `-----------------------'
                |        release
                V
       ,-----------------.
      (  bslma::Allocator )
       `-----------------'
                       allocate
                       deallocate
Alignment Strategy:
The bslma::SequentialPool allocates memory using one of the two alignment strategies (defined in bslma_bufferallocator) optionally specified at construction: 1) MAXIMUM ALIGNMENT or 2) NATURAL ALIGNMENT.
  1. MAXIMUM ALIGNMENT: This strategy always allocates memory aligned with the most restrictive alignment on the host platform. The value is defined by bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT.
  2. NATURAL ALIGNMENT: This strategy allocates memory whose alignment depends on the requested number of bytes. An object of a fundamental type (int, etc.) is naturally aligned when it's size evenly divides its address. An object of an aggregate type has natural alignment if the alignment of the most-restrictively aligned sub-object evenly divides the address of the aggregate. Natural alignment is always at least as restrictive as the compiler's required alignment. When only the size of an aggregate is known, and not its composition, we compute the alignment by finding the largest integral power of 2 (up to and including bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT) that divides the requested (non-zero) number of bytes. This computed alignment is guaranteed to be at least as restrictive as any sub-object within the aggregate.
The default strategy is NATURAL ALIGNMENT.
Optional buffer Parameter:
A buffer can be supplied to a bslma::SequentialAllocator object at construction in which case the allocator will try to satisfy allocation requests using this buffer before switching to a dynamically-allocated internal pool. Once the allocator is using an internal pool, it will not try to satisfy any subsequent allocation requests from the supplied buffer. Note that the allocator does not take ownership of the buffer. Also note that bufferSize may be specified using a positive or negative value to indicate a buffer growth strategy (see "Internal Buffer Growth").
Optional initialSize Parameter:
In lieu of an externally-supplied buffer, a value for the initialSize parameter may be supplied at construction to specify the initial size of the internal pool. If neither a buffer nor an initialSize is specified, an implementation-defined value is used for an initial size of the internal pool. Note that initialSize may be specified using a positive or negative value to indicate a buffer growth strategy (see "Internal Buffer Growth").
Internal Buffer Growth:
A bslma::SequentialAllocator replenishes its internal buffer if the current buffer cannot satisfy an allocation request. It does so by one of two growth strategies:
Constant Growth: The new buffer is always of the same size as the current buffer (possibly supplied at construction).
Geometric Growth: The new buffer will be geometrically larger than the current buffer up to an optionally-specified maximum limit.
If a bufferSize (and corresponding buffer) or initialSize is supplied at construction, the sign of its value implicitly specifies which growth strategy to use. A positive value indicates Constant Growth, whereas a a negative value indicates Geometric Growth. If neither bufferSize nor initialSize is supplied, Geometric Growth is used. The optional maxBufferSize parameter may be used to place a cap on Geometric Growth (maxBufferSize is ignored if Constant Growth is in effect). If no value is specified for maxBufferSize, there is no cap on Geometric Growth. Note that reserveCapacity always ensures that the requested number of bytes is available (allocating a new internal pool if necessary) irrespective of whether the size of the request exceeds maxBufferSize.
Usage:
Allocators are often supplied to objects requiring dynamically-allocated memory at construction. For example, consider the following my_DoubleStack class, parameterized by a bslma::Allocator:
  // my_doublestack.h
  // ...

  namespace bslma { class Allocator; }

  class my_DoubleStack {
      // DATA
      double           *d_stack_p;      // dynamically-allocated array
      int               d_size;         // physical capacity this stack
      int               d_length;       // next available index in stack
      bslma::Allocator *d_allocator_p;  // memory allocator (held, not owned)

      // FRIENDS
      friend class my_DoubleStackIter;

    private:
      // PRIVATE MANIPULATORS
      void increaseSize();
          // Increase the capacity of this stack by at least one element.

    public:
      // CREATORS
      my_DoubleStack(bslma::Allocator       *basicAllocator = 0);
      my_DoubleStack(const my_DoubleStack&  original,
                     bslma::Allocator       *basicAllocator = 0);
      ~my_DoubleStack();

      // MANIPULATORS
      my_DoubleStack& operator=(const my_DoubleStack& rhs);
      void push(double value);
      void pop();

      // ACCESSORS
      const double& top() const;
      bool isEmpty() const;
  };

  // ...

  // MANIPULATORS
  inline
  void my_DoubleStack::push(double value)
  {
      if (d_length >= d_size) {
          increaseSize();
      }
      d_stack_p[d_length++] = item;
  }

  // ...
The stack interface takes an optional basicAllocator supplied only at construction. (We avoid use of the name allocator so as not to conflict with the STL use of the word, which differs slightly.) If non-zero, the stack holds a pointer to this allocator, but does not own it. If no allocator is supplied, the implementation itself must either conditionally invoke global new and delete explicitly whenever dynamic memory must be managed (BAD IDEA) or (GOOD IDEA) install a default allocator that adapts use of these global operators to the bslma_allocator interface. In actual practice, however, we might want the default to be run-time settable from a central location (see bslma_default).
  // my_doublestack.cpp
  // ...
  #include <my_doublestack.h>
  #include <bslma_allocator.h>
  #include <bslma_default.h>  // adapter for 'new' and 'delete'

  enum { INITIAL_SIZE = 1, GROW_FACTOR = 2 };

  // ...

  // CREATORS
  my_DoubleStack::my_DoubleStack(bslma::Allocator *basicAllocator)
  : d_size(INITIAL_SIZE)
  , d_length(0)
  , d_allocator_p(basicAllocator)
  {
      assert(d_allocator_p);
      d_stack_p = (double *)
                  d_allocator_p->allocate(d_size * sizeof *d_stack_p);
  }

  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);
  }
Even in this simplified implementation, all use of the allocator protocol is relegated to the .cpp file. Subsequent use of the allocator is demonstrated by the following file-scope static reallocation function:
  static
  void reallocate(double **array, int newSize, int length,
                  bslma::Allocator *basicAllocator)
      // Reallocate memory in the specified 'array' to have the specified
      // 'newSize' using the specified 'basicAllocator'.  The specified
      // 'length' number of leading elements are preserved.  Given that the
      // internal policy of class 'my_DoubleStack' requires that the physical
      // capacity of the container may grow but never shrink, the behavior is
      // undefined unless length <= newSize.
  {
      assert(array);
      assert(1 <= newSize);
      assert(0 <= length);
      assert(basicAllocator);
      assert(length <= newSize);        // enforce class invariant

      double *tmp = *array;             // support exception neutrality
      *array = (double *) basicAllocator->allocate(newSize * sizeof **array);

      // COMMIT POINT

      std::memcpy(*array, tmp, length * sizeof **array);
      basicAllocator->deallocate(tmp);
  }

  void my_DoubleStack::increaseSize()
  {
      int proposedNewSize = d_size * GROW_FACTOR;     // reallocate can throw
      assert(proposedNewSize > d_length);
      reallocate(&d_stack_p, proposedNewSize, d_length, d_allocator_p);
      d_size = proposedNewSize;                       // we're committed
  }