Quick Links:

bal | bbl | bdl | bsl

Namespaces | Functions

Component bslma_sequentialpool
[Package bslma]

Provide fast variable-size memory pool with allocation methods. More...

Namespaces

namespace  bslma

Functions

void * operator new (std::size_t size, BloombergLP::bslma::SequentialPool &pool)
void operator delete (void *address, BloombergLP::bslma::SequentialPool &pool)

Detailed Description

Outline
Purpose:
Provide fast variable-size memory pool with allocation methods.
Deprecated:
Use bdlma_sequentialpool instead.
Classes:
bslma::SequentialPool fast variable-size memory pool
See also:
Component bdlma_sequentialpool
Description:
This component implements a memory pool, bslma::SequentialPool, that dispenses memory blocks of any requested size from an internal buffer or an optional user-supplied buffer. If an allocation request exceeds the remaining free memory space in the pool, the pool 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. By default, buffer growth is not capped. The release method releases all memory allocated through this pool, as does the destructor. Note, however, that individual allocated blocks of memory cannot be separately deallocated.
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 instance of a fundamental type (int, etc.) is naturally aligned when it's size evenly divides its address. An instance 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::SequentialPool object at construction in which case the pool will try to satisfy allocation requests using this buffer before switching to a dynamically-allocated internal pool. Once the object is using an internal pool, it will not try to satisfy any subsequent allocation requests from the supplied buffer. Note that the pool 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::SequentialPool 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:
The bslma::SequentialPool can be used to allocate memory for containers of non-homogeneous elements, such as MyList below. Note that the use of a sequential pool allows the operator= and removeAll methods to quickly deallocate memory of all elements by calling the release method of the pool. Similarly, the destructor of MyList simply allows the pool's destructor to deallocate memory for all elements:
  // MyList.h
  #include <bsls_types.h>
  #include <bslma_sequentialpool.h>

  namespace bslma { class Allocator; }

  class MyList {
      char                   *d_typeArray_p;
      void                  **d_list_p;
      int                     d_length;
      int                     d_size;
      bslma::Allocator       *d_allocator_p;
      bslma::SequentialPool   d_pool;

      // NOT IMPLEMENTED
      MyList(const MyList&);

    private:
      MyList(char* buffer, int bufferSize, bslma::Allocator *basicAllocator);
      void increaseSize();

    public:
      enum Type { INT, DOUBLE, INT64 };

      MyList(bslma::Allocator *basicAllocator);

      ~MyList();
      MyList& operator=(const MyList& rhs);
      void append(int value);
      void append(double value);
      void append(bsls::Types::Int64 value);
      void removeAll();

      const int *theInt(int index) const;
      const double *theDouble(int index) const;
      const bsls::Types::Int64 *theInt64(int index) const;
      const Type type(int index) const;
      int length() const;
  };

  inline
  void MyList::removeAll()
  {
      d_pool.release();
      d_length = 0;
  }

  inline
  const int *MyList::theInt(int index) const
  {
      return (int *) d_list_p[index];
  }

  inline
  const double *MyList::theDouble(int index) const
  {
      return (double *) d_list_p[index];
  }

  inline
  const bsls::Types::Int64 *MyList::theInt64(int index) const
  {
      return (bsls::Types::Int64 *) d_list_p[index];
  }

  inline
  const MyList::Type MyList::type(int index) const
  {
      return (Type) d_typeArray_p[index];
  }

  inline
  int MyList::length() const
  {
      return d_length;
  }

  // ...

  // MyList.cpp
  #include <MyList.h>
  #include <bslma_allocator.h>

  enum { INITIAL_SIZE = 1, GROW_FACTOR = 2 };

  static
  void copyElement(void **list, MyList::Type type, int index,
                   void *srcElement, bslma::SequentialPool *pool)
      // Copy the value of the specified 'srcElement' of the specified 'type'
      // to the specified 'index' position in the specified 'list'.  Use the
      // specified 'pool' to supply memory.
  {
      assert(list);
      assert(0 <= index);
      assert(srcElement);
      assert(pool);

      typedef bsls::Types::Int64 Int64;

      switch (type) {
        case MyList::INT:
          list[index] = new(pool->allocate(sizeof(int)))
                        int(*((int *) srcElement));
          break;
        case MyList::DOUBLE:
          list[index] = new(pool->allocate(sizeof(double)))
                        double(*((double *) srcElement));
          break;
        case MyList::INT64:
          list[index] = new(pool->allocate(sizeof(Int64)))
                        Int64(*((Int64 *) srcElement));
          break;
        default:
          assert(0 && "ERROR (MyList): Invalid element type.");
      }
  }

  static
  void reallocate(void ***list, char **typeArray, int *size,
                  int newSize, int length, bslma::Allocator *basicAllocator)
      // Reallocate memory in the specified 'list' and 'typeArray' using the
      // specified 'basicAllocator' and update the specified size to the
      // specified 'newSize'.  The specified 'length' number of leading
      // elements are preserved in 'list' and 'typeArray'.  If 'allocate'
      // should throw an exception, this function has no effect.  The
      // behavior is undefined unless 1 <= newSize, 0 <= length, and
      // newSize <= length.
  {
      assert(list);
      assert(*list);
      assert(typeArray);
      assert(*typeArray);
      assert(size);
      assert(1 <= newSize);
      assert(0 <= length);
      assert(length <= *size);    // sanity check
      assert(length <= newSize);  // ensure class invariant
      assert(basicAllocator);

      void **newList =
          (void **) basicAllocator->allocate(newSize * sizeof *newList);
      char *newTypeArray =
          (char *) basicAllocator->allocate(newSize * sizeof *newTypeArray);
      memcpy(newList, *list, length * sizeof **list);
      memcpy(newTypeArray, *typeArray, length * sizeof **typeArray);
      basicAllocator->deallocate(*list);
      basicAllocator->deallocate(*typeArray);
      *list = newList;
      *typeArray = newTypeArray;
      *size = newSize;
  }

  void MyList::increaseSize()
  {
       int newSize = d_size * GROW_FACTOR;
       reallocate(&d_list_p, &d_typeArray_p, &d_size, newSize,
                  d_length, d_allocator_p);
  }

  MyList::MyList(char* buffer, int bufferSize,
                   bslma::Allocator *basicAllocator)
  : d_length()
  , d_size(MY_INITIAL_SIZE)
  , d_pool(buffer, bufferSize, basicAllocator)
  , d_allocator_p(basicAllocator)
  {
      assert(d_allocator_p);

      d_typeArray_p =
          (char *) d_allocator_p->allocate(d_size * sizeof *d_typeArray_p);
      d_list_p =
          (void **) d_allocator_p->allocate(d_size * sizeof *d_list_p);
  }

  MyList::MyList(bslma::Allocator *basicAllocator)
  : d_size(INITIAL_SIZE)
  , d_length(0)
  , d_pool(basicAllocator)
  , d_allocator_p(basicAllocator)
  {
      assert(d_allocator_p);

      d_typeArray_p =
          (char *) d_allocator_p->allocate(d_size * sizeof *d_typeArray_p);
      d_list_p =
          (void **) d_allocator_p->allocate(d_size * sizeof *d_list_p);
  }

  MyList::~MyList()
  {
      assert(d_typeArray_p);
      assert(d_list_p);
      assert(0 <= d_size);
      assert(0 <= d_length);  assert(d_length <= d_size);
      assert(d_allocator_p);

      d_allocator_p->deallocate(d_typeArray_p);
      d_allocator_p->deallocate(d_list_p);
  }

  MyList& MyList::operator=(const MyList& rhs)
  {
      if (&rhs != this) {
          // not aliased
          d_pool.release();
          d_length = 0;

          int newLength = rhs.d_length;
          if (newLength > d_size) {
              reallocate(&d_list_p, &d_typeArray_p, &d_size,
                         newLength, d_length, d_allocator_p);
          }
          for (int i = 0; i < newLength; ++i) {
              d_typeArray_p[i] = rhs.d_typeArray_p[i];
              copyElement(d_list_p, (Type) d_typeArray_p[i], i,
                          rhs.d_list_p[i], &d_pool);
          }
          d_length = newLength;
      }
      return *this;
  }

  void MyList::append(int value)
  {
      if (d_length >= d_size) {
          increaseSize();
      }
      int *item = (int *) d_pool.allocate(sizeof *item);
      *item = value;
      d_typeArray_p[d_length] = (char) MyList::INT;
      d_list_p[d_length++] = item;
  }

  void MyList::append(double value)
  {
      if (d_length >= d_size) {
          increaseSize();
      }
      double *item = (double *) d_pool.allocate(sizeof *item);
      *item = value;
      d_typeArray_p[d_length] = (char) MyList::DOUBLE;
      d_list_p[d_length++] = item;
  }

  void MyList::append(bsls::Types::Int64 value)
  {
      typedef bsls::Types::Int64 Int64;

      if (d_length >= d_size) {
          increaseSize();
      }
      Int64 *item = (Int64 *) d_pool.allocate(sizeof *item);
      *item = value;
      d_typeArray_p[d_length] = (char) MyList::INT64;
      d_list_p[d_length++] = item;
  }

Function Documentation

void* operator new ( std::size_t  size,
BloombergLP::bslma::SequentialPool &  pool 
) [inline]

Return the memory allocated from the specified pool. The behavior is undefined unless size is the same as objectSize that pool has been constructed with. Note that an object may allocate additional memory internally, requiring the allocator to be passed in as a constructor argument:

      my_Type *newMyType(bslma::Pool *pool, bslma::Allocator *basicAllocator)
      {
          return new (*pool) my_Type(..., basicAllocator);
      }

Note also that the analogous version of operator delete should not be called directly. Instead, this component provides a static template member function deleteObject parameterized by TYPE that performs the following:

      void deleteMyType(bslma::Pool *pool, my_Type *t)
      {
          t->~my_Type();
          pool->deallocate(t);
      }
void operator delete ( void *  address,
BloombergLP::bslma::SequentialPool &  pool 
) [inline]

Use the specified pool to deallocate the memory at the specified address. The behavior is undefined unless address was allocated using pool and has not already been deallocated. This operator is supplied solely to allow the compiler to arrange for it to be called in case of an exception.