// bdlma_blocklist.h                                                  -*-C++-*-
#ifndef INCLUDED_BDLMA_BLOCKLIST
#define INCLUDED_BDLMA_BLOCKLIST

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

//@PURPOSE: Provide allocation and management of a sequence of memory blocks.
//
//@CLASSES:
//  bdlma::BlockList: memory manager that allocates and manages memory blocks
//
//@SEE_ALSO: 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.

#include <bdlscm_version.h>

#include <bslma_allocator.h>
#include <bslma_default.h>

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

namespace BloombergLP {
namespace bdlma {

                             // ===============
                             // class BlockList
                             // ===============

class BlockList {
    // This class implements a low-level memory manager that allocates and
    // manages a sequence of memory blocks -- each potentially of a different
    // size as specified during the invocation of the 'allocate' method.
    // Allocated blocks may be efficiently deallocated individually, i.e.,
    // potentially in constant time depending on the supplied allocator.  The
    // 'release' method deallocates the entire sequence of memory blocks, as
    // does the destructor.

    // TYPES
    struct Block {
        // This 'struct' overlays the beginning of each managed block of
        // allocated memory, implementing a doubly-linked list of managed
        // blocks, and thereby enabling constant-time deletions from, as well
        // as additions to, the list of blocks.

        Block                                *d_next_p;       // next pointer

        Block                               **d_addrPrevNext; // enable delete

        bsls::AlignmentUtil::MaxAlignedType   d_memory;       // force
                                                              // alignment
    };

    // DATA
    Block            *d_head_p;       // address of first block of memory (or
                                      // 0)

    bslma::Allocator *d_allocator_p;  // memory allocator (held, not owned)

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

  public:
    // CREATORS
    explicit
    BlockList(bslma::Allocator *basicAllocator = 0);
        // Create an empty block list suitable for managing memory blocks of
        // varying sizes.  Optionally specify a 'basicAllocator' used to supply
        // memory.  If 'basicAllocator' is 0, the currently installed default
        // allocator is used.

    ~BlockList();
        // Destroy this object and deallocate all outstanding memory blocks
        // managed by this object.

    // MANIPULATORS
    void *allocate(bsls::Types::size_type size);
        // Return the address of a contiguous block of memory of the specified
        // 'size' (in bytes).  If 'size' is 0, no memory is allocated and 0 is
        // returned.  The returned memory is guaranteed to be maximally
        // aligned.

    void deallocate(void *address);
        // Return the memory at the specified 'address' back to the associated
        // allocator.  If 'address' is 0, this function has no effect.  The
        // behavior is undefined unless 'address' was allocated by this object,
        // and has not already been deallocated.

    void release();
        // Deallocate all memory blocks currently managed by this object,
        // returning it to its default-constructed state.

                                  // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this object to allocate memory.
};

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

                             // ---------------
                             // class BlockList
                             // ---------------

// CREATORS
inline
BlockList::BlockList(bslma::Allocator *basicAllocator)
: d_head_p(0)
, d_allocator_p(bslma::Default::allocator(basicAllocator))
{
}

// Aspects

inline
bslma::Allocator *BlockList::allocator() const
{
    return d_allocator_p;
}

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

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