// bdlma_pool.h                                                       -*-C++-*-
#ifndef INCLUDED_BDLMA_POOL
#define INCLUDED_BDLMA_POOL

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

//@PURPOSE: Provide efficient allocation of memory blocks of uniform size.
//
//@CLASSES:
//  bdlma::Pool: memory manager that allocates memory blocks of uniform size
//
//@DESCRIPTION: This component implements a memory pool, 'bdlma::Pool', that
// allocates and manages maximally-aligned memory blocks of some uniform size
// specified at construction.  A 'bdlma::Pool' object maintains an internal
// linked list of free memory blocks, and dispenses one block for each
// 'allocate' method invocation.  When a memory block is deallocated, it is
// returned to the free list for potential reuse.
//
// Whenever the linked list of free memory blocks is depleted, the
// 'bdlma::Pool' replenishes the list by first allocating a large, contiguous
// "chunk" of memory, then splitting the chunk into multiple memory blocks.  A
// chunk and its constituent memory blocks can be depicted visually:
//..
//     +-----+--- memory blocks of uniform size
//     |     |
//   ----- ----- ------------
//  |     |     |     ...    |
//   =====^=====^============
//
//   \___________ __________/
//               V
//           a "chunk"
//..
// Note that the size of the allocated chunk is determined by both the growth
// strategy and maximum blocks per chunk, either of which can be optionally
// specified at construction (see the "Configuration at Construction" section).
//
///Configuration at Construction
///-----------------------------
// When creating a 'bdlma::Pool', clients must specify the specific block size
// managed and dispensed by the pool.  Furthermore, clients can optionally
// configure:
//
//: 1 GROWTH STRATEGY -- geometrically growing chunk size starting from 1 (in
//:   terms of the number of memory blocks per chunk), or fixed chunk size.  If
//:   the growth strategy is not specified, geometric growth is used.
//: 2 MAX BLOCKS PER CHUNK -- the maximum number of memory blocks within a
//:   chunk.  If the maximum blocks per chunk is not specified, an
//:   implementation-defined default value is used.
//: 3 BASIC ALLOCATOR -- the allocator used to supply memory to replenish the
//:   internal pool.  If not specified, the currently installed default
//:   allocator is used (see 'bslma_default').
//
// For example, if geometric growth is used and the maximum blocks per chunk is
// specified as 30, the chunk size grows geometrically, starting from 1, until
// the specified maximum blocks per chunk, as follows:
//..
//  1, 2, 4, 8, 16, 30, 30, 30 ...
//..
// If constant growth is used, the chunk size is always the specified maximum
// blocks per chunk (or an implementation-defined value if the maximum blocks
// per chunk is not specified), for example:
//..
//  30, 30, 30 ...
//..
// A default-constructed pool has an initial chunk size of 1 (i.e., the number
// of memory blocks of a given size allocated at once to replenish a pool's
// memory), and the pool's chunk size grows geometrically until it reaches an
// implementation-defined maximum, at which it is capped.  Finally, unless
// otherwise specified, all memory comes from the allocator that was the
// currently installed default allocator at the time the 'bdlma::Pool' was
// created.
//
///Overloaded Global Operator 'new'
///--------------------------------
// This component overloads the global 'operator new' to allow convenient
// syntax for the construction of objects using a 'bdlma::Pool'.  The 'new'
// operator supplied in this component takes a 'bdlma::Pool' argument
// indicating the source of the memory.  Consider the following use of standard
// placement 'new' syntax (supplied by 'bsl_new.h') along with a 'bdlma::Pool'
// to allocate an object of type 'T'.  Note that the size of 'T' must be the
// same or smaller than the 'blockSize' with which the pool is constructed:
//..
//  void f(bdlma::Pool *pool)
//  {
//      assert(pool->blockSize() >= sizeof(T));
//
//      T *t = new (pool->allocate()) T(...);
//
//      // ...
//  }
//..
// This usage style is not exception-safe.  If the constructor of 'T' throws an
// exception, 'pool->deallocate' is never called.
//
// Supplying an overloaded global 'operator new':
//..
//  ::operator new(bsl::size_t size, BloombergLP::bdlma::Pool& pool);
//..
// allows for the following cleaner usage, which does not require the size
// calculation and guarantees that 'pool->deallocate' *is* called in the case
// of an exception:
//..
//  void f(bdlma::Pool *pool)
//  {
//      assert(pool->blockSize() >= sizeof(T));
//
//      T *t = new (*pool) T(...);
//
//      // ...
//..
// Also note that the analogous version of operator 'delete' should *not* be
// called directly.  Instead, this component provides a static template member
// function 'deleteObject', parameterized on 'TYPE':
//..
//      pool->deleteObject(t);
//  }
//..
// The above 'deleteObject' call is equivalent to performing the following:
//..
//  t->~TYPE();
//  pool->deallocate(t);
//..
// An overloaded operator 'delete' is supplied solely to allow the compiler to
// arrange for it to be called in case of an exception.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using a 'bdlma::Pool' for Efficient Memory Allocation
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// A 'bdlma::Pool' can be used by node-based containers (such as lists, trees,
// and hash tables that hold multiple elements of uniform size) for efficient
// memory allocation of new elements.  The following container template class,
// 'my_PooledArray', stores values of (template parameter) 'TYPE'
// "out-of-place" as nodes in a 'vector' of pointers.  Since the size of each
// node is fixed and known *a priori*, the class uses a 'bdlma::Pool' to
// allocate memory for the nodes to improve memory allocation efficiency.  Note
// that for simplicity, we assume that 'TYPE' does not require an allocator,
// and that calls to the destructor of 'TYPE' can be elided.
//
// First, we define the interface of our 'my_PooledArray' template class:
//..
//  // my_poolarray.h
//
//  template <class TYPE>
//  class my_PooledArray {
//      // This class implements a container that stores values of (template
//      // parameter) 'TYPE' out-of-place.  It is assumed that 'TYPE' does not
//      // require an allocator, and that calls to the destructor of 'TYPE' can
//      // be elided.
//
//      // DATA
//      bsl::vector<TYPE *> d_array_p;  // array of pooled elements
//      bdlma::Pool         d_pool;     // memory manager for array elements
//
//    private:
//      // Not implemented:
//      my_PooledArray(const my_PooledArray&);
//
//    public:
//      // CREATORS
//      explicit my_PooledArray(bslma::Allocator *basicAllocator = 0);
//          // Create a pooled array that stores the 'TYPE' element values
//          // "out-of-place".  Optionally specify a 'basicAllocator' used to
//          // supply memory.  If 'basicAllocator' is 0, the currently
//          // installed default allocator is used.
//
//      ~my_PooledArray();
//          // Destroy this array and all elements held by it.
//
//      // MANIPULATORS
//      void append(const TYPE& value);
//          // Append the specified 'value' to this array.
//
//      void removeAll();
//          // Remove all elements from this array.
//
//      // ACCESSORS
//      bsl::size_t length() const;
//          // Return the number of elements in this array.
//
//      const TYPE& operator[](int index) const;
//          // Return a reference providing non-modifiable access to the value
//          // at the specified 'index' in this array.  The behavior is
//          // undefined unless '0 <= index < length()'.
//  };
//..
// Next, we provide the implementation of the 'my_PooledArray' methods that are
// defined 'inline'.
//
// Note that in the 'removeAll' method, all elements are deallocated by simply
// invoking the pool's 'release' method.  This technique implies significant
// performance gain when the array contains many elements:
//..
//  // MANIPULATORS
//  template <class TYPE>
//  inline
//  void my_PooledArray<TYPE>::removeAll()
//  {
//      d_array_p.clear();
//      d_pool.release();
//  }
//
//  // ACCESSORS
//  template <class TYPE>
//  inline
//  bsl::size_t my_PooledArray<TYPE>::length() const
//  {
//      return d_array_p.size();
//  }
//
//  template <class TYPE>
//  inline
//  const TYPE& my_PooledArray<TYPE>::operator[](int index) const
//  {
//      assert(0     <= index);
//      assert(index <  static_cast<int>(length()));
//
//      return *d_array_p[index];
//  }
//..
// Next, we provide the implementation of the 'my_PooledArray' methods that are
// defined in the '.cpp' file.
//
// Note that the growth strategy and maximum chunk size of the pool defaults to
// those provided by 'bdlma::Pool':
//..
//  // my_poolarray.cpp
//
//  // CREATORS
//  template <class TYPE>
//  my_PooledArray<TYPE>::my_PooledArray(bslma::Allocator *basicAllocator)
//  : d_array_p(basicAllocator)
//  , d_pool(sizeof(TYPE), basicAllocator)
//  {
//  }
//..
// Since all memory is managed by 'd_pool', we do not have to explicitly invoke
// 'deleteObject' to reclaim outstanding memory.  The destructor of the pool
// will automatically deallocate all array elements:
//..
//  template <class TYPE>
//  my_PooledArray<TYPE>::~my_PooledArray()
//  {
//      // Elements are automatically deallocated when 'd_pool' is destroyed.
//  }
//..
// Finally, note that the overloaded "placement" 'new' is used to allocate new
// nodes in the 'append' method:
//..
//  // MANIPULATORS
//  template <class TYPE>
//  void my_PooledArray<TYPE>::append(const TYPE& value)
//  {
//      TYPE *tmp = new (d_pool) TYPE(value);
//      d_array_p.push_back(tmp);
//  }
//..

#include <bdlscm_version.h>

#include <bdlma_infrequentdeleteblocklist.h>

#include <bslma_allocator.h>
#include <bslma_deleterhelper.h>

#include <bsls_alignmentutil.h>
#include <bsls_assert.h>
#include <bsls_blockgrowth.h>
#include <bsls_types.h>

#include <bsl_cstddef.h>

namespace BloombergLP {
namespace bdlma {

                                // ==========
                                // class Pool
                                // ==========

class Pool {
    // This class implements a memory pool that allocates and manages memory
    // blocks of some uniform size specified at construction.  This memory pool
    // maintains an internal linked list of free memory blocks, and dispenses
    // one block for each 'allocate' method invocation.  When a memory block is
    // deallocated, it is returned to the free list for potential reuse.

    // PRIVATE TYPES
    struct Link {
        // This 'struct' implements a link data structure that stores the
        // address of the next link, and is used to implement the internal
        // linked list of free memory blocks.  Note that this type is
        // replicated in 'bdlma_pool.cpp' to provide access to a compatible
        // type from static methods defined in 'bdlma_pool.cpp'.

        Link *d_next_p;  // pointer to next link
    };

    // DATA
    bsls::Types::size_type  d_blockSize;          // size (in bytes) of each
                                                  // allocated memory block
                                                  // returned to client

    bsls::Types::size_type  d_internalBlockSize;  // actual size of each block
                                                  // maintained on free list
                                                  // (contains overhead for
                                                  // 'Link')

    int                     d_chunkSize;          // current chunk size (in
                                                  // blocks-per-chunk)

    int                     d_maxBlocksPerChunk;  // maximum chunk size (in
                                                  // blocks-per-chunk)

    bsls::BlockGrowth::Strategy
                            d_growthStrategy;     // growth strategy of the
                                                  // chunk size

    Link                   *d_freeList_p;         // linked list of free memory
                                                  // blocks

    InfrequentDeleteBlockList
                            d_blockList;          // memory manager for
                                                  // allocated memory

    char                   *d_begin_p;            // start of a contiguous
                                                  // group of memory blocks

    char                   *d_end_p;              // end of a contiguous group
                                                  // of memory blocks

  private:
    // PRIVATE MANIPULATORS
    void replenish();
        // Dynamically allocate a new chunk using this pool's underlying growth
        // strategy.

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

  public:
    // CREATORS
    explicit
    Pool(bsls::Types::size_type  blockSize,
         bslma::Allocator       *basicAllocator = 0);
    Pool(bsls::Types::size_type       blockSize,
         bsls::BlockGrowth::Strategy  growthStrategy,
         bslma::Allocator            *basicAllocator = 0);
    Pool(bsls::Types::size_type       blockSize,
         bsls::BlockGrowth::Strategy  growthStrategy,
         int                          maxBlocksPerChunk,
         bslma::Allocator            *basicAllocator = 0);
        // Create a memory pool that returns blocks of contiguous memory of the
        // specified 'blockSize' (in bytes) for each 'allocate' method
        // invocation.  Optionally specify a 'growthStrategy' used to control
        // the growth of internal memory chunks (from which memory blocks are
        // dispensed).  If 'growthStrategy' is not specified, geometric growth
        // is used.  Optionally specify 'maxBlocksPerChunk' as the maximum
        // chunk size if 'growthStrategy' is specified.  If geometric growth is
        // used, the chunk size grows starting at 'blockSize', doubling in size
        // until the size is exactly 'blockSize * maxBlocksPerChunk'.  If
        // constant growth is used, the chunk size is always
        // 'blockSize * maxBlocksPerChunk'.  If 'maxBlocksPerChunk' is not
        // specified, an implementation-defined value is used.  Optionally
        // specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.  The behavior is undefined unless '1 <= blockSize' and
        // '1 <= maxBlocksPerChunk'.

    ~Pool();
        // Destroy this pool, releasing all associated memory back to the
        // underlying allocator.

    // MANIPULATORS
    void *allocate();
        // Return the address of a contiguous block of maximally-aligned memory
        // having the fixed block size specified at construction.

    void deallocate(void *address);
        // Relinquish the memory block at the specified 'address' back to this
        // pool object for reuse.  The behavior is undefined unless 'address'
        // is non-zero, was allocated by this pool, and has not already been
        // deallocated.

    template <class TYPE>
    void deleteObject(const TYPE *object);
        // Destroy the specified 'object' based on its dynamic type and then
        // use this pool to deallocate its memory footprint.  This method has
        // no effect if 'object' is 0.  The behavior is undefined unless
        // 'object', when cast appropriately to 'void *', was allocated using
        // this pool and has not already been deallocated.  Note that
        // 'dynamic_cast<void *>(object)' is applied if 'TYPE' is polymorphic,
        // and 'static_cast<void *>(object)' is applied otherwise.

    template <class TYPE>
    void deleteObjectRaw(const TYPE *object);
        // Destroy the specified 'object' and then use this pool to deallocate
        // its memory footprint.  This method has no effect if 'object' is 0.
        // The behavior is undefined unless 'object' is !not! a secondary base
        // class pointer (i.e., the address is (numerically) the same as when
        // it was originally dispensed by this pool), was allocated using this
        // pool, and has not already been deallocated.

    void release();
        // Relinquish all memory currently allocated via this pool object.

    void reserveCapacity(int numBlocks);
        // Reserve memory from this pool to satisfy memory requests for at
        // least the specified 'numBlocks' before the pool replenishes.  The
        // behavior is undefined unless '0 <= numBlocks'.

    // ACCESSORS
    bsls::Types::size_type blockSize() const;
        // Return the size (in bytes) of the memory blocks allocated from this
        // pool object.  Note that all blocks dispensed by this pool have the
        // same size.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to allocate memory.  Note
        // that this allocator can not be used to deallocate memory
        // allocated through this pool.
};

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

// Note that the 'new' and 'delete' operators are declared outside the
// 'BloombergLP' namespace so that they do not hide the standard placement
// 'new' and 'delete' operators (i.e.,
// 'void *operator new(bsl::size_t, void *)' and
// 'void operator delete(void *)').
//
// Also note that only the scalar versions of operators 'new' and 'delete' are
// provided, because overloading 'new' (and 'delete') with their array versions
// would cause dangerous ambiguity.  Consider what would have happened had we
// overloaded the array version of 'operator new':
//..
//  void *operator new[](bsl::size_t size, BloombergLP::bdlma::Pool& pool);
//..
// A user of 'bdlma::Pool' may expect to be able to use array 'operator new' as
// follows:
//..
//   new (*pool) my_Type[...];
//..
// The problem is that this expression returns an array that cannot be safely
// deallocated.  On the one hand, there is no syntax in C++ to invoke an
// overloaded 'operator delete'; on the other hand, the pointer returned by
// 'operator new' cannot be passed to the 'deallocate' method directly because
// the pointer is different from the one returned by the 'allocate' method.
// The compiler offsets the value of this pointer by a header, which is used to
// maintain the number of objects in the array (so that 'operator delete' can
// destroy the right number of objects).

// FREE OPERATORS
void *operator new(bsl::size_t size, BloombergLP::bdlma::Pool& pool);
    // Return a block of memory of the specified 'size' (in bytes) allocated
    // from the specified 'pool'.  The behavior is undefined unless 'size' is
    // the same or smaller than the 'blockSize' with which 'pool' was
    // constructed.  Note that an object may allocate additional memory
    // internally, requiring the allocator to be passed in as a constructor
    // argument:
    //..
    //  my_Type *newMyType(bdlma::Pool *pool, bslma::Allocator *basicAllocator)
    //  {
    //      return new (*pool) my_Type(..., basicAllocator);
    //  }
    //..
    // Also note 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':
    //..
    //  void deleteMyType(my_Type *t, bdlma::Pool *pool)
    //  {
    //      pool->deleteObject(t);
    //  }
    //..
    // 'deleteObject' performs the following:
    //..
    //  t->~my_Type();
    //  pool->deallocate(t);
    //..

void operator delete(void *address, BloombergLP::bdlma::Pool& pool);
    // Use the specified 'pool' to deallocate the memory at the specified
    // 'address'.  The behavior is undefined unless 'address' is non-zero, was
    // allocated using 'pool', and has not already been deallocated.  Note that
    // this operator is supplied solely to allow the compiler to arrange for it
    // to be called in the case of an exception.

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

namespace BloombergLP {
namespace bdlma {

                                // ----------
                                // class Pool
                                // ----------

// MANIPULATORS
inline
void *Pool::allocate()
{
    if (d_begin_p == d_end_p) {
        if (d_freeList_p) {
            Link *p      = d_freeList_p;
            d_freeList_p = p->d_next_p;
            return p;                                                 // RETURN
        }

        replenish();
    }

    char *p = d_begin_p;
    d_begin_p += d_internalBlockSize;
    return p;
}

inline
void Pool::deallocate(void *address)
{
    BSLS_ASSERT_SAFE(address);

    static_cast<Link *>(address)->d_next_p = d_freeList_p;
    d_freeList_p = static_cast<Link *>(address);
}

template <class TYPE>
inline
void Pool::deleteObject(const TYPE *object)
{
    bslma::DeleterHelper::deleteObject(object, this);
}

template <class TYPE>
inline
void Pool::deleteObjectRaw(const TYPE *object)
{
    bslma::DeleterHelper::deleteObjectRaw(object, this);
}

inline
void Pool::release()
{
    d_blockList.release();
    d_freeList_p = 0;
    d_begin_p = 0;
    d_end_p = 0;
}

// ACCESSORS
inline
bsls::Types::size_type Pool::blockSize() const
{
    return d_blockSize;
}

// Aspects

inline
bslma::Allocator *Pool::allocator() const
{
    return d_blockList.allocator();
}

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

// FREE OPERATORS
inline
void *operator new(bsl::size_t size, BloombergLP::bdlma::Pool& pool)
{
    using namespace BloombergLP;

    BSLS_ASSERT_SAFE(size <= pool.blockSize() &&
        bsls::AlignmentUtil::calculateAlignmentFromSize(size)
         <= bsls::AlignmentUtil::calculateAlignmentFromSize(pool.blockSize()));

    static_cast<void>(size);  // suppress "unused parameter" warnings
    return pool.allocate();
}

inline
void operator delete(void *address, BloombergLP::bdlma::Pool& pool)
{
    BSLS_ASSERT_SAFE(address);

    pool.deallocate(address);
}

#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 ----------------------------------