Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bdlma_blocklist
[Package bdlma]

Provide allocation and management of a sequence of memory blocks. More...

Namespaces

namespace  bdlma

Detailed Description

Outline
Purpose:
Provide allocation and management of a sequence of memory blocks.
Classes:
bdlma::BlockList memory manager that allocates and manages memory blocks
See also:
Component bdlma_infrequentdeleteblocklist
Description:
This component implements a low-level memory manager, bdlma::BlockList, that allocates and manages a sequence of memory blocks, each of a potentially different size as specified during the allocate method's invocation. The release method of a bdlma::BlockList object deallocates the entire sequence of outstanding memory blocks, as does its destructor. Note that a bdlma::BlockList, at a minor memory expense, allows for individual items to be deallocated.
Usage:
This section illustrates intended use of this component.
Example 1: Using a bdlma::BlockList in a Memory Pool:
A bdlma::BlockList object is commonly used to supply memory to more elaborate memory managers that distribute parts of each (larger) allocated memory block supplied by the bdlma::BlockList object. The my_StrPool memory pool manager shown below requests relatively large blocks of memory from its bdlma::BlockList member object and distributes, via its allocate method, memory chunks of varying sizes from each block.
First, we define the interface of our my_StrPool class:
  // my_strpool.h

  class my_StrPool {

      // DATA
      bsls::Types::size_type  d_blockSize;  // size of current memory block

      char                   *d_block_p;    // current free memory block

      bsls::Types::IntPtr     d_cursor;     // offset to next available byte
                                            // in block

      bdlma::BlockList        d_blockList;  // supplies managed memory blocks

    private:
      // PRIVATE MANIPULATORS
      void *allocateBlock(bsls::Types::size_type numBytes);
          // Request a new memory block of at least the specified 'numBytes'
          // size and allocate the initial 'numBytes' from this block.
          // Return the address of the allocated memory.  The behavior is
          // undefined unless '0 < numBytes'.

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

    public:
      // CREATORS
      my_StrPool(bslma::Allocator *basicAllocator = 0);
          // Create a memory manager.  Optionally specify a 'basicAllocator'
          // used to supply memory.  If 'basicAllocator' is 0, the currently
          // installed default allocator is used.

      ~my_StrPool();
          // Destroy this object and release all associated memory.

      // MANIPULATORS
      void *allocate(bsls::Types::size_type numBytes);
          // Allocate the specified 'numBytes' of memory and return its
          // address.  If 'numBytes' is 0, return 0 with no other effect.

      void release();
          // Release all memory currently allocated through this object.
  };

  // MANIPULATORS
  inline
  void my_StrPool::release()
  {
      d_blockList.release();
      d_block_p = 0;
  }
Finally, we provide the implementation of our my_StrPool class:
  // my_strpool.cpp

  enum {
      k_INITIAL_SIZE  = 128,  // initial block size

      k_GROWTH_FACTOR =   2,  // multiplicative factor by which to grow block

      k_THRESHOLD     = 128   // size beyond which an individual block may be
                              // allocated if it doesn't fit in current block
  };

  // PRIVATE MANIPULATORS
  void *my_StrPool::allocateBlock(bsls::Types::size_type numBytes)
  {
      assert(0 < numBytes);

      if (k_THRESHOLD < numBytes) {
          // Alloc separate block if above threshold.

          return (char *)d_blockList.allocate(numBytes);            // RETURN
      }
      else {
          if (d_block_p) {
              // Do not increase block size if no current block.

              d_blockSize *= k_GROWTH_FACTOR;
          }
          d_block_p = (char *)d_blockList.allocate(d_blockSize);
          d_cursor = numBytes;
          return d_block_p;                                         // RETURN
      }
  }

  // CREATORS
  my_StrPool::my_StrPool(bslma::Allocator *basicAllocator)
  : d_blockSize(k_INITIAL_SIZE)
  , d_block_p(0)
  , d_blockList(basicAllocator)  // the blocklist knows about 'bslma_default'
  {
  }

  my_StrPool::~my_StrPool()
  {
      assert(k_INITIAL_SIZE <= d_blockSize);
      assert(!d_block_p || (0 <= d_cursor &&
                 static_cast<bsls::Types::Uint64>(d_cursor) <= d_blockSize));
  }

  // MANIPULATORS
  void *my_StrPool::allocate(bsls::Types::size_type numBytes)
  {
      if (0 == numBytes) {
          return 0;                                                 // RETURN
      }

      if (d_block_p && numBytes + d_cursor <= d_blockSize) {
          char *p = d_block_p + d_cursor;
          d_cursor += numBytes;
          return p;                                                 // RETURN
      }
      else {
          return allocateBlock(numBytes);                           // RETURN
      }
  }
In the code shown above, the my_StrPool memory manager allocates from its bdlma::BlockList member object an initial memory block of size k_INITIAL_SIZE. This size is multiplied by k_GROWTH_FACTOR each time a depleted memory block is replaced by a newly-allocated block. The allocate method distributes memory from the current memory block piecemeal, except when the requested size either (1) is not available in the current block, or (2) exceeds the k_THRESHOLD_SIZE, in which case a separate memory block is allocated and returned. When the my_StrPool memory manager is destroyed, its bdlma::BlockList member object is also destroyed, which, in turn, automatically deallocates all of its managed memory blocks.