// bslstl_simplepool.h                                                -*-C++-*-
#ifndef INCLUDED_BSLSTL_SIMPLEPOOL
#define INCLUDED_BSLSTL_SIMPLEPOOL

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

//@PURPOSE: Provide efficient allocation of memory blocks for a specific type.
//
//@CLASSES:
//  bslstl::SimplePool: memory manager that allocates memory blocks for a type
//
//@SEE_ALSO: bslstl_treenodepool, bdlma_pool
//
//@DESCRIPTION: This component implements a memory pool, 'bslstl::SimplePool',
// that allocates and manages memory blocks of for a parameterized type.  A
// 'bslstl::SimplePool' 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,
// 'bslstl::SimplePool' replenishes the list by first allocating a large,
// contiguous "chunk" of memory, then splitting the chunk into multiple memory
// blocks each having the 'sizeof' the simple pool's parameterized type.  A
// chunk and its constituent memory blocks can be depicted visually:
//..
//     +-----+--- memory blocks of uniform size for parameterized type
//     |     |
//   ----- ----- ------------
//  |     |     |     ...    |
//   =====^=====^============
//
//   \___________ __________/
//               V
//           a "chunk"
//..
// This pool implementation is simple because its allocation strategy is not
// configurable.  The size of a chunk starts from 1 memory block, and doubles
// each time a chunk is allocated up to an implementation defined maximum
// number of blocks.
//
///Comparison with 'bdema_Pool'
///----------------------------
// There are a few differences between 'bslstl::SimplePool' and 'bdema_Pool':
//: 1 'bslstl::SimplePool' is parameterized on both allocator and type, which
//:   improve performance and memory usage in exchange for increase in code
//:   size.
//:
//: 2 'bslstl::SimplePool' uses the allocator through the use of
//:   'bsl::allocator_traits' (which is generally not relevant to non-container
//:   type.
//:
//: 3 'bslstl::SimplePool' is less configurable in order to achieve abstraction
//:   of allocation and improvement in performance.
//
// Clients are encouraged to use 'bdema_Pool' as 'bslstl::SimplePool' is
// designed for node-based STL containers, and its pooling behavior may change
// according to the needs of those containers.
//
///Usage
///-----
// This section illustrates intended use for this component.
//
///Example 1: Creating a Node-Based Stack
/// - - - - - - - - - - - - - - - - - - -
// Suppose that we want to implement a stack with a linked list.  It is
// expensive to allocate memory every time a node is inserted.  Therefore, we
// can use 'SimplePool' to efficiently manage the memory for the list.
//
// First, we define the class that implements the stack:
//..
//  template <class ALLOCATOR = bsl::allocator<int> >
//  class my_Stack {
//      // This class defines a node-based stack of integers.
//
//      // PRIVATE TYPES
//      struct Node {
//          // This 'struct' implements a link data structure containing a
//          // value and a pointer to the next node.
//
//          int   d_value;   // payload value
//          Node *d_next_p;  // pointer to the next node
//      };
//
//      typedef bslstl::SimplePool<Node, ALLOCATOR> Pool;
//          // Alias for memory pool.
//
//    private:
//      // DATA
//      Node *d_head_p;  // pointer to the first node
//      int   d_size;    // size of the stack
//      Pool  d_pool;    // memory manager for the stack
//
//    public:
//      // CREATORS
//      my_Stack(const ALLOCATOR& allocator = ALLOCATOR());
//          // Create an empty 'my_Stack' object.  Optionally specify a
//          // 'basicAllocator' used to supply memory.  If 'basicAllocator' is
//          // 0, the currently installed default allocator is used.
//
//      // MANIPULATORS
//      void push(int value);
//          // Insert an element with the specified value to the top of this
//          // stack.
//
//      void pop();
//          // Remove the top element from this stack.  The behavior is
//          // undefined unless '1 <= size()'.
//
//      // ACCESSORS
//      int top();
//          // Return the value of the element on the top of this stack.  The
//          // behavior is undefined unless '1 <= size()'.
//
//      std::size_t size();
//          // Return the number of elements in this stack.
//  };
//..
// Now, we define the implementation of the stack.  Notice how
// 'bslstl::SimplePool' is used to allocate memory in 'push' and deallocate
// memory in 'pop':
//..
//  // CREATORS
//  template <class ALLOCATOR>
//  my_Stack<ALLOCATOR>::my_Stack(const ALLOCATOR& allocator)
//  : d_head_p(0)
//  , d_size(0)
//  , d_pool(allocator)
//  {
//  }
//
//  // MANIPULATORS
//  template <class ALLOCATOR>
//  void my_Stack<ALLOCATOR>::push(int value)
//  {
//      Node *newNode = d_pool.allocate();
//
//      newNode->d_value = value;
//      newNode->d_next_p = d_head_p;
//      d_head_p = newNode;
//
//      ++d_size;
//  }
//
//  template <class ALLOCATOR>
//  void my_Stack<ALLOCATOR>::pop()
//  {
//      BSLS_ASSERT(0 != size());
//
//      Node *n = d_head_p;
//      d_head_p = d_head_p->d_next_p;
//      d_pool.deallocate(n);
//      --d_size;
//  }
//
//  // ACCESSORS
//  template <class ALLOCATOR>
//  int my_Stack<ALLOCATOR>::top()
//  {
//      BSLS_ASSERT(0 != size());
//
//      return d_head_p->d_value;
//  }
//
//  template <class ALLOCATOR>
//  std::size_t my_Stack<ALLOCATOR>::size()
//  {
//      return d_size;
//  }
//..
// Finally, we test our stack by pushing and popping some elements:
//..
//  my_Stack stack;
//  stack.push(1);
//  stack.push(2);
//  stack.push(3);
//  stack.push(4);
//  stack.push(5);
//  assert(5 == stack.size());
//
//  assert(5 == stack.top());
//  stack.pop();
//  assert(4 == stack.top());
//  stack.pop();
//  assert(3 == stack.top());
//  stack.pop();
//  assert(2 == stack.top());
//  stack.pop();
//  assert(1 == stack.top());
//  stack.pop();
//  assert(0 == stack.size());
//..

#include <bslscm_version.h>

#include <bslma_allocatortraits.h>

#include <bslmf_movableref.h>

#include <bsls_alignmentfromtype.h>
#include <bsls_alignmentutil.h>
#include <bsls_assert.h>
#include <bsls_platform.h>

#include <algorithm>  // swap (C++03)
#include <utility>    // swap (C++17)

namespace BloombergLP {
namespace bslstl {

                       // ======================
                       // struct SimplePool_Type
                       // ======================

template <class ALLOCATOR>
struct SimplePool_Type {
    // For use only by 'bslstl::SimplePool'.  This 'struct' provides a
    // namespace for a set of types used to define the base-class of a
    // 'SimplePool'.  The parameterized 'ALLOCATOR' is bound to
    // 'MaxAlignedType' to ensure the allocated memory is maximally aligned.

    typedef typename bsl::allocator_traits<ALLOCATOR>::template
            rebind_traits<bsls::AlignmentUtil::MaxAlignedType> AllocatorTraits;
        // Alias for the allocator traits rebound to allocate
        // 'bsls::AlignmentUtil::MaxAlignedType'.

    typedef typename AllocatorTraits::allocator_type AllocatorType;
        // Alias for the allocator type for
        // 'bsls::AlignmentUtil::MaxAlignedType'.
};

                       // ================
                       // class SimplePool
                       // ================

template <class VALUE, class ALLOCATOR>
class SimplePool : public SimplePool_Type<ALLOCATOR>::AllocatorType {
    // This class provides methods for creating and deleting nodes using the
    // appropriate allocator-traits of the parameterized 'ALLOCATOR'.
    // This type is intended to be used as a private base-class for a
    // node-based container, in order to take advantage of the
    // empty-base-class optimization in the case where the base-class has 0
    // size (as may the case if the parameterized 'ALLOCATOR' is not a
    // 'bslma::Allocator').

    // PRIVATE TYPES
    typedef SimplePool_Type<ALLOCATOR> Types;

    union Block {
        // This 'union' implements a link data structure with the size no
        // smaller than 'VALUE' that stores the address of the next link.
        // It is used to implement the internal linked list of free memory
        // blocks.

        Block *d_next_p;                    // pointer to the next block

        char   d_size[sizeof(VALUE)];       // make a block has the size of at
                                            // least 'VALUE'

        typename bsls::AlignmentFromType<VALUE>::Type d_alignment;
                                            // ensure proper alignment
    };

    union Chunk {
        // This 'union' prepends to the beginning of each managed block of
        // allocated memory, implementing a singly-linked list of managed
        // chunks, and thereby enabling constant-time additions to the list of
        // chunks.

        Chunk *d_next_p;  // pointer to next Chunk

        typename bsls::AlignmentFromType<Block>::Type d_alignment;
                          // ensure each block is correctly aligned
    };

  public:
    // TYPES
    typedef VALUE ValueType;
        // Alias for the parameterized type 'VALUE'.

    typedef typename Types::AllocatorType AllocatorType;
        // Alias for the allocator type for a
        // 'bsls::AlignmentUtil::MaxAlignedType'.

    typedef typename Types::AllocatorTraits AllocatorTraits;
        // Alias for the allocator traits for the parameterized
        // 'ALLOCATOR'.

    typedef typename AllocatorTraits::size_type size_type;

  private:
    // DATA
    Chunk *d_chunkList_p;     // linked list of "chunks" of memory

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

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

  private:
    // NOT IMPLEMENTED
    SimplePool& operator=(bslmf::MovableRef<SimplePool>);
    SimplePool& operator=(const SimplePool&);
    SimplePool(const SimplePool&);

  private:
    // PRIVATE MANIPULATORS
    Block *allocateChunk(size_type size);
        // Allocate a chunk of memory with at least the specified 'size' number
        // of usable bytes and add the chunk to the chunk list.  Return the
        // address of the usable portion of the memory.

    void replenish();
        // Dynamically allocate a new chunk using the pool's underlying growth
        // strategy, and use the chunk to replenish the free memory list of
        // this pool.

  public:
    // CREATORS
    explicit SimplePool(const ALLOCATOR& allocator);
        // Create a memory pool that returns blocks of contiguous memory of the
        // size of the parameterized 'VALUE' using the specified 'allocator' to
        // supply memory.  The chunk size grows starting with at least
        // 'sizeof(VALUE)', doubling in size up to an implementation defined
        // maximum number of blocks per chunk.

    SimplePool(bslmf::MovableRef<SimplePool> original);
        // Create a memory pool, adopting all outstanding memory allocations
        // associated with the specified 'original' pool, that returns blocks
        // of contiguous memory of the sizeof the paramterized 'VALUE' using
        // the allocator associated with 'original'.  The chunk size is set to
        // that of 'original' and continues to double in size up to an
        // implementation defined maximum number of blocks per chunk.  Note
        // that 'original' is left in a valid but unspecified state.

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

    // MANIPULATORS
    void adopt(bslmf::MovableRef<SimplePool> pool);
        // Adopt all outstanding memory allocations associated with the
        // specfied memory 'pool'.  The behavior is undefined unless this pool
        // uses the same allocator as that associated with 'pool'.  The
        // behavior is undefined unless this pool is in the default-constructed
        // state.

    AllocatorType& allocator();
        // Return a reference providing modifiable access to the rebound
        // allocator traits for the node-type.  Note that this operation
        // returns a base-class ('AllocatorType') reference to this object.

    VALUE *allocate();
        // Return the address of a block of memory of at least the size of
        // 'VALUE'.  Note that the memory is *not* initialized.

    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.

    void reserve(size_type numBlocks);
        // Dynamically allocate a new chunk containing the specified
        // 'numBlocks' number of blocks, and add the chunk to the free memory
        // list of this pool.  The additional memory is added irrespective of
        // the amount of free memory when called.  The behavior is undefined
        // unless '0 < numBlocks'.

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

    void swap(SimplePool& other);
        // Efficiently exchange the memory blocks of this object with those of
        // the specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless
        // 'allocator() == other.allocator()'.

    void quickSwapRetainAllocators(SimplePool& other);
        // Efficiently exchange the memory blocks of this object with those of
        // the specified 'other' object.  This method provides the no-throw
        // exception-safety guarantee.  The behavior is undefined unless
        // 'allocator() == other.allocator()'.

    void quickSwapExchangeAllocators(SimplePool& other);
        // Efficiently exchange the memory blocks and the allocator of this
        // object with those of the specified 'other' object.  This method
        // provides the no-throw exception-safety guarantee.

    // ACCESSORS
    const AllocatorType& allocator() const;
        // Return a reference providing non-modifiable access to the rebound
        // allocator traits for the node-type.  Note that this operation
        // returns a base-class ('AllocatorType') reference to this object.

    bool hasFreeBlocks() const;
        // Return 'true' if this object holds free (currently unused) blocks,
        // and 'false' otherwise.
};

// ============================================================================
//                  TEMPLATE AND INLINE FUNCTION DEFINITIONS
// ============================================================================

// PRIVATE MANIPULATORS
template <class VALUE, class ALLOCATOR>
typename SimplePool<VALUE, ALLOCATOR>::Block *
SimplePool<VALUE, ALLOCATOR>::allocateChunk(size_type size)
{
    // Determine the number of bytes we want to allocate and compute the number
    // of 'MaxAlignedType' needed to contain those bytes.

    size_type numBytes = static_cast<size_type>(sizeof(Chunk)) + size;
    size_type numMaxAlignedType =
                       (numBytes + bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT - 1)
                     / bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT;

    Chunk *chunkPtr = reinterpret_cast<Chunk *>(
                    AllocatorTraits::allocate(allocator(), numMaxAlignedType));

    BSLS_ASSERT_SAFE(0 ==
             reinterpret_cast<bsls::Types::UintPtr>(chunkPtr) % sizeof(Chunk));

    chunkPtr->d_next_p = d_chunkList_p;
    d_chunkList_p      = chunkPtr;

    return reinterpret_cast<Block *>(chunkPtr + 1);
}

template <class VALUE, class ALLOCATOR>
inline
void SimplePool<VALUE, ALLOCATOR>::replenish()
{
    reserve(d_blocksPerChunk);

    enum { MAX_BLOCKS_PER_CHUNK = 32 };

    if (d_blocksPerChunk < MAX_BLOCKS_PER_CHUNK) {
        d_blocksPerChunk *= 2;
    }
}

// CREATORS
template <class VALUE, class ALLOCATOR>
inline
SimplePool<VALUE, ALLOCATOR>::SimplePool(const ALLOCATOR& allocator)
: AllocatorType(allocator)
, d_chunkList_p(0)
, d_freeList_p(0)
, d_blocksPerChunk(1)
{
}

template <class VALUE, class ALLOCATOR>
inline
SimplePool<VALUE, ALLOCATOR>::SimplePool(
                                        bslmf::MovableRef<SimplePool> original)
: AllocatorType(bslmf::MovableRefUtil::access(original).allocator())
, d_chunkList_p(bslmf::MovableRefUtil::access(original).d_chunkList_p)
, d_freeList_p(bslmf::MovableRefUtil::access(original).d_freeList_p)
, d_blocksPerChunk(bslmf::MovableRefUtil::access(original).d_blocksPerChunk)
{
    SimplePool& lvalue = original;
    lvalue.d_chunkList_p = 0;
    lvalue.d_freeList_p = 0;
    lvalue.d_blocksPerChunk = 1;
}

template <class VALUE, class ALLOCATOR>
inline
SimplePool<VALUE, ALLOCATOR>::~SimplePool()
{
    release();
}

// MANIPULATORS
template <class VALUE, class ALLOCATOR>
inline
void
SimplePool<VALUE, ALLOCATOR>::adopt(bslmf::MovableRef<SimplePool> pool)
{
    BSLS_ASSERT_SAFE(0 == d_chunkList_p);
    BSLS_ASSERT_SAFE(0 == d_freeList_p);
    BSLS_ASSERT_SAFE(allocator()
                           == bslmf::MovableRefUtil::access(pool).allocator());

    SimplePool& lvalue = pool;
    d_chunkList_p = lvalue.d_chunkList_p;
    d_freeList_p = lvalue.d_freeList_p;
    d_blocksPerChunk = lvalue.d_blocksPerChunk;

    lvalue.d_chunkList_p = 0;
    lvalue.d_freeList_p = 0;
    lvalue.d_blocksPerChunk = 1;
}

template <class VALUE, class ALLOCATOR>
inline
typename SimplePool<VALUE, ALLOCATOR>::AllocatorType&
SimplePool<VALUE, ALLOCATOR>::allocator()
{
    return *this;
}

template <class VALUE, class ALLOCATOR>
inline
VALUE *SimplePool<VALUE, ALLOCATOR>::allocate()
{
    if (!d_freeList_p) {
        replenish();
    }
    VALUE *block = reinterpret_cast<VALUE *>(d_freeList_p);
    d_freeList_p = d_freeList_p->d_next_p;
    return block;
}

template <class VALUE, class ALLOCATOR>
inline
void SimplePool<VALUE, ALLOCATOR>::deallocate(void *address)
{
    BSLS_ASSERT_SAFE(address);

    reinterpret_cast<Block *>(address)->d_next_p = d_freeList_p;
    d_freeList_p = reinterpret_cast<Block *>(address);
}

template <class VALUE, class ALLOCATOR>
inline
void SimplePool<VALUE, ALLOCATOR>::swap(SimplePool<VALUE, ALLOCATOR>& other)
{
    BSLS_ASSERT_SAFE(allocator() == other.allocator());

    std::swap(d_blocksPerChunk, other.d_blocksPerChunk);
    std::swap(d_freeList_p, other.d_freeList_p);
    std::swap(d_chunkList_p, other.d_chunkList_p);
}

template <class VALUE, class ALLOCATOR>
inline
void SimplePool<VALUE, ALLOCATOR>::quickSwapRetainAllocators(
                                           SimplePool<VALUE, ALLOCATOR>& other)
{
    swap(other);
}

template <class VALUE, class ALLOCATOR>
inline
void SimplePool<VALUE, ALLOCATOR>::quickSwapExchangeAllocators(
                                           SimplePool<VALUE, ALLOCATOR>& other)
{
    using std::swap;
    swap(this->allocator(), other.allocator());
    swap(d_blocksPerChunk,  other.d_blocksPerChunk);
    swap(d_freeList_p,      other.d_freeList_p);
    swap(d_chunkList_p,     other.d_chunkList_p);
}

template <class VALUE, class ALLOCATOR>
void SimplePool<VALUE, ALLOCATOR>::reserve(size_type numBlocks)
{
    BSLS_ASSERT(0 < numBlocks);

    Block *begin = allocateChunk(
                            numBlocks * static_cast<size_type>(sizeof(Block)));
    Block *end   = begin + numBlocks - 1;

    for (Block *p = begin; p < end; ++p) {
        p->d_next_p = p + 1;
    }
    end->d_next_p = d_freeList_p;
    d_freeList_p  = begin;
}

template <class VALUE, class ALLOCATOR>
void SimplePool<VALUE, ALLOCATOR>::release()
{
    // The values in 'd_chunkList_p' are allocated using
    // 'AllocatorTraits::allocate' for max-aligned type (see
    // 'allocateChunk'). Casting from 'Chunk *' back to that type
    // will not impact alignment, but may generate warnings.

#ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-align"
#endif

    while (d_chunkList_p) {
        typename AllocatorTraits::value_type *lastChunk =
                      reinterpret_cast<typename AllocatorTraits::value_type *>(
                                                                d_chunkList_p);
        d_chunkList_p   = d_chunkList_p->d_next_p;
        AllocatorTraits::deallocate(allocator(), lastChunk, 1);
    }
    d_freeList_p = 0;

#ifdef BSLS_PLATFORM_HAS_PRAGMA_GCC_DIAGNOSTIC
#pragma GCC diagnostic pop
#endif

}

// ACCESSORS
template <class VALUE, class ALLOCATOR>
inline
const typename SimplePool<VALUE, ALLOCATOR>::AllocatorType&
SimplePool<VALUE, ALLOCATOR>::allocator() const
{
    return *this;
}

template <class VALUE, class ALLOCATOR>
inline
bool SimplePool<VALUE, ALLOCATOR>::hasFreeBlocks() const
{
    return d_freeList_p;
}

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

#endif

// ----------------------------------------------------------------------------
// Copyright 2019 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 ----------------------------------