Boost::pool(笔记)

 

// Copyright (C) 2000, 2001 Stephen Cleary
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// See http://www.boost.org for updates, documentation, and revision history.
#ifndef BOOST_POOL_HPP
#define BOOST_POOL_HPP
#include   // for workarounds
// std::less, std::less_equal, std::greater
#include 
// new[], delete[], std::nothrow
#include 
// std::size_t, std::ptrdiff_t
#include 
// std::malloc, std::free
#include 
// std::invalid_argument
#include 
// std::max
#include 
#include 
// boost::details::pool::ct_lcm
#include 
// boost::details::pool::lcm
#include 
// boost::simple_segregated_storage
#include 
#ifdef BOOST_NO_STDC_NAMESPACE
 namespace std { using ::malloc; using ::free; }
#endif
// There are a few places in this file where the expression "this->m" is used.
// This expression is used to force instantiation-time name lookup, which I am
//   informed is required for strict Standard compliance.  It's only necessary
//   if "m" is a member of a base class that is dependent on a template
//   parameter.
// Thanks to Jens Maurer for pointing this out!
namespace boost {
struct default_user_allocator_new_delete
{
  typedef std::size_t size_type;
  typedef std::ptrdiff_t difference_type;
  static char * malloc(const size_type bytes)
  { return new (std::nothrow) char[bytes]; }
  static void free(char * const block)
  { delete [] block; }
};
struct default_user_allocator_malloc_free
{
  typedef std::size_t size_type;
  typedef std::ptrdiff_t difference_type;
  static char * malloc(const size_type bytes)
  { return reinterpret_cast(std::malloc(bytes)); }
  static void free(char * const block)
  { std::free(block); }
};
namespace details {
//这个类实际上不存在,它只是一个间接过程,起一个换算过程。
//也就是使一个block末尾存放指向上一块的指针,和上一块block尺寸的大小。
// PODptr is a class that pretends to be a "pointer" to different class types
//  that don't really exist.  It provides member functions to access the "data"
//  of the "object" it points to.  Since these "class" types are of variable
//  size, and contains some information at the *end* of its memory (for
//  alignment reasons), PODptr must contain the size of this "class" as well as
//  the pointer to this "object".
template 
class PODptr
{
  public:
    typedef SizeType size_type;
  private:
    char * ptr;
    size_type sz;
    char * ptr_next_size() const
    { return (ptr + sz - sizeof(size_type)); }
    char * ptr_next_ptr() const
    {
      return (ptr_next_size() -
          pool::ct_lcm::value);
    }
  public:
    PODptr(char * const nptr, const size_type nsize)
    :ptr(nptr), sz(nsize) { }
    PODptr()
    :ptr(0), sz(0) { }
    bool valid() const { return (begin() != 0); }
    void invalidate() { begin() = 0; }
    char * & begin() { return ptr; }
    char * begin() const { return ptr; }
    char * end() const { return ptr_next_ptr(); }
    size_type total_size() const { return sz; }
    size_type element_size() const
    {
      return (sz - sizeof(size_type) -
          pool::ct_lcm::value);
    }
    size_type & next_size() const
    { return *(reinterpret_cast(ptr_next_size())); }
    char * & next_ptr() const
    { return *(reinterpret_cast(ptr_next_ptr())); }
    PODptr next() const
    { return PODptr(next_ptr(), next_size()); }
    void next(const PODptr & arg) const
    {//这一块的block指向上一块的开头。
      next_ptr() = arg.begin();
      next_size() = arg.total_size();
    }
};
} // namespace details
template 
class pool: protected simple_segregated_storage<
    typename UserAllocator::size_type>
{
  public:
    typedef UserAllocator user_allocator;
    typedef typename UserAllocator::size_type size_type;
    typedef typename UserAllocator::difference_type difference_type;
  private:
    BOOST_STATIC_CONSTANT(unsigned, min_alloc_size =
        (::boost::details::pool::ct_lcm::value) );
    // Returns 0 if out-of-memory
    // Called if malloc/ordered_malloc needs to resize the free list
    void * malloc_need_resize();
    void * ordered_malloc_need_resize();
  protected:
    details::PODptr list;
    simple_segregated_storage & store() { return *this; }
    const simple_segregated_storage & store() const { return *this; }
    const size_type requested_size;
    size_type next_size;
    size_type start_size;
    // finds which POD in the list 'chunk' was allocated from
    details::PODptr find_POD(void * const chunk) const;
    // is_from() tests a chunk to determine if it belongs in a block
    static bool is_from(void * const chunk, char * const i,
        const size_type sizeof_i)
    {
      // We use std::less_equal and std::less to test 'chunk'
      //  against the array bounds because standard operators
      //  may return unspecified results.
      // This is to ensure portability.  The operators < <= > >= are only
      //  defined for pointers to objects that are 1) in the same array, or
      //  2) subobjects of the same object [5.9/2].
      // The functor objects guarantee a total order for any pointer [20.3.3/8]
//WAS:
//      return (std::less_equal()(static_cast(i), chunk)
//          && std::less()(chunk,
//              static_cast(i + sizeof_i)));
      std::less_equal lt_eq;
      std::less lt;
      return (lt_eq(i, chunk) && lt(chunk, i + sizeof_i));
    }
    size_type alloc_size() const
    {//对齐条件要满足:void, requested_size, size_type
      const unsigned min_size = min_alloc_size;
      return details::pool::lcm(requested_size, min_size);
    }
    //先把ptr指针转换成二级指针,然后对这个二级指针解引用,由于解引用后的指针占有四字节的内容。
	//如果是链表,那么将指向下个链表
    // for the sake of code readability :)
    static void * & nextof(void * const ptr)
    { return *(static_cast(ptr)); }
  public:
    // The second parameter here is an extension!
    // pre: npartition_size != 0 && nnext_size != 0
    explicit pool(const size_type nrequested_size,
        const size_type nnext_size = 32)
    :list(0, 0), requested_size(nrequested_size), next_size(nnext_size), start_size(nnext_size)
    { }
    ~pool() { purge_memory(); }
    // Releases memory blocks that don't have chunks allocated
    // pre: lists are ordered
    //  Returns true if memory was actually deallocated
    bool release_memory();
    // Releases *all* memory blocks, even if chunks are still allocated
    //  Returns true if memory was actually deallocated
    bool purge_memory();
    // These functions are extensions!
    size_type get_next_size() const { return next_size; }
    void set_next_size(const size_type nnext_size) { next_size = start_size = nnext_size; }
    size_type get_requested_size() const { return requested_size; }
    // Both malloc and ordered_malloc do a quick inlined check first for any
    //  free chunks.  Only if we need to get another memory block do we call
    //  the non-inlined *_need_resize() functions.
    // Returns 0 if out-of-memory
    void * malloc()
    {
      // Look for a non-empty storage
      if (!store().empty())
        return store().malloc();
      return malloc_need_resize();
    }
    void * ordered_malloc()
    {
      // Look for a non-empty storage
      if (!store().empty())
        return store().malloc();
      return ordered_malloc_need_resize();
    }
    // Returns 0 if out-of-memory
    // Allocate a contiguous section of n chunks
    void * ordered_malloc(size_type n);
    // pre: 'chunk' must have been previously
    //        returned by *this.malloc().
    void free(void * const chunk)
    { store().free(chunk); }
    // pre: 'chunk' must have been previously
    //        returned by *this.malloc().
    void ordered_free(void * const chunk)
    { store().ordered_free(chunk); }
    // pre: 'chunk' must have been previously
    //        returned by *this.malloc(n).
    void free(void * const chunks, const size_type n)
    {
      const size_type partition_size = alloc_size();
      const size_type total_req_size = n * requested_size;
      const size_type num_chunks = total_req_size / partition_size +
          ((total_req_size % partition_size) ? true : false);
      store().free_n(chunks, num_chunks, partition_size);
    }
    // pre: 'chunk' must have been previously
    //        returned by *this.malloc(n).
    void ordered_free(void * const chunks, const size_type n)
    {
      const size_type partition_size = alloc_size();
      const size_type total_req_size = n * requested_size;
      const size_type num_chunks = total_req_size / partition_size +
          ((total_req_size % partition_size) ? true : false);
      store().ordered_free_n(chunks, num_chunks, partition_size);
    }
    // is_from() tests a chunk to determine if it was allocated from *this
    bool is_from(void * const chunk) const
    {
      return (find_POD(chunk).valid());
    }
};
template 
bool pool::release_memory()
{
  // This is the return value: it will be set to true when we actually call
  //  UserAllocator::free(..)
  bool ret = false;
  // This is a current & previous iterator pair over the memory block list
  details::PODptr ptr = list;
  details::PODptr prev;
  // This is a current & previous iterator pair over the free memory chunk list
  //  Note that "prev_free" in this case does NOT point to the previous memory
  //  chunk in the free list, but rather the last free memory chunk before the
  //  current block.
  void * free_p = this->first;
  void * prev_free_p = 0;
  const size_type partition_size = alloc_size();
  // Search through all the all the allocated memory blocks
  while (ptr.valid())
  {
    // At this point:
    //  ptr points to a valid memory block
    //  free_p points to either:
    //    0 if there are no more free chunks
    //    the first free chunk in this or some next memory block
    //  prev_free_p points to either:
    //    the last free chunk in some previous memory block
    //    0 if there is no such free chunk
    //  prev is either:
    //    the PODptr whose next() is ptr
    //    !valid() if there is no such PODptr
    // If there are no more free memory chunks, then every remaining
    //  block is allocated out to its fullest capacity, and we can't
    //  release any more memory
    if (free_p == 0)
      break;
    // We have to check all the chunks.  If they are *all* free (i.e., present
    //  in the free list), then we can free the block.
    bool all_chunks_free = true;
    // Iterate 'i' through all chunks in the memory block
    // if free starts in the memory block, be careful to keep it there
    void * saved_free = free_p;
    for (char * i = ptr.begin(); i != ptr.end(); i += partition_size)
    {
      // If this chunk is not free
      if (i != free_p)
      {
        // We won't be able to free this block
        all_chunks_free = false;
        // free_p might have travelled outside ptr
        free_p = saved_free;
        // Abort searching the chunks; we won't be able to free this
        //  block because a chunk is not free.
        break;
      }
      // We do not increment prev_free_p because we are in the same block
      free_p = nextof(free_p);
    }
    // post: if the memory block has any chunks, free_p points to one of them
    // otherwise, our assertions above are still valid
    const details::PODptr next = ptr.next();
    if (!all_chunks_free)
    {
      if (is_from(free_p, ptr.begin(), ptr.element_size()))
      {
        std::less lt;
        void * const end = ptr.end();
        do
        {
          prev_free_p = free_p;
          free_p = nextof(free_p);
        } while (free_p && lt(free_p, end));
      }
	  //指向下一块的区块的开头。free_P = end
      // This invariant is now restored:
      //     free_p points to the first free chunk in some next memory block, or
      //       0 if there is no such chunk.
      //     prev_free_p points to the last free chunk in this memory block.
      
      // We are just about to advance ptr.  Maintain the invariant:
      // prev is the PODptr whose next() is ptr, or !valid()
      // if there is no such PODptr
      prev = ptr;
    }
    else
    {
      // All chunks from this block are free
      // Remove block from list
      if (prev.valid())
        prev.next(next);
      else
        list = next;
      // Remove all entries in the free list from this block
      if (prev_free_p != 0)
        nextof(prev_free_p) = free_p;
      else
        this->first = free_p;
      // And release memory
      UserAllocator::free(ptr.begin());
      ret = true;
    }
    // Increment ptr
    ptr = next;
  }
  next_size = start_size;
  return ret;
}
template 
bool pool::purge_memory()
{
  details::PODptr iter = list;
  if (!iter.valid())
    return false;
  do
  {
    // hold "next" pointer
    const details::PODptr next = iter.next();
    // delete the storage
    UserAllocator::free(iter.begin());
    // increment iter
    iter = next;
  } while (iter.valid());
  list.invalidate();
  this->first = 0;
  next_size = start_size;
  return true;
}
//Each of these sections may contain padding
//as necessary to guarantee alignment for each of the next sections.
//The size of the first section is number_of_chunks * lcm(requested_size, sizeof(void *), sizeof(size_type));
//the size of the second section is lcm(sizeof(void *), sizeof(size_type);
//and the size of the third section is sizeof(size_type).
template 
void * pool::malloc_need_resize()
{
  // No memory in any of our storages; make a new storage,
	//partition_size 是对齐了的字节数,是要求分配尺寸的对齐。
	//对齐中的一条原则便是(地址)/(类型字节数)的整数倍.而求他的最小公倍数,是必定能成为整数倍的。
  const size_type partition_size = alloc_size();
  //总共对齐了的字节数,boost::pool文档中有详细的讨论。
  const size_type POD_size = next_size * partition_size +
      details::pool::ct_lcm::value + sizeof(size_type);
  char * const ptr = UserAllocator::malloc(POD_size);
  if (ptr == 0)
    return 0;
  const details::PODptr node(ptr, POD_size);
  next_size <<= 1;
  //  initialize it,
  store().add_block(node.begin(), node.element_size(), partition_size);
  
  //这是申请的block尾部(void* , size_type)记录上一块block的大小。以及指向上一块。
  //  insert it into the list,
  node.next(list);
  list = node;
  //  and return a chunk from it.
  return store().malloc();
}
template 
void * pool::ordered_malloc_need_resize()
{
  // No memory in any of our storages; make a new storage,
  const size_type partition_size = alloc_size();
  const size_type POD_size = next_size * partition_size +
      details::pool::ct_lcm::value + sizeof(size_type);
  char * const ptr = UserAllocator::malloc(POD_size);
  if (ptr == 0)
    return 0;
  const details::PODptr node(ptr, POD_size);
  next_size <<= 1;
  //  initialize it,
  //  (we can use "add_block" here because we know that
  //  the free list is empty, so we don't have to use
  //  the slower ordered version)
  store().add_block(node.begin(), node.element_size(), partition_size);
  //  insert it into the list,
  //   handle border case
  if (!list.valid() || std::greater()(list.begin(), node.begin()))
  {
    node.next(list);
    list = node;
  }
  else
  {
    details::PODptr prev = list;
    while (true)
    {
      // if we're about to hit the end or
      //  if we've found where "node" goes
      if (prev.next_ptr() == 0
          || std::greater()(prev.next_ptr(), node.begin()))
        break;
      prev = prev.next();
    }
    node.next(prev.next());
    prev.next(node);
  }
  //  and return a chunk from it.
  return store().malloc();
}
template 
void * pool::ordered_malloc(const size_type n)
{
  const size_type partition_size = alloc_size();
  const size_type total_req_size = n * requested_size;
  const size_type num_chunks = total_req_size / partition_size +
      ((total_req_size % partition_size) ? true : false);
  void * ret = store().malloc_n(num_chunks, partition_size);
  if (ret != 0)
    return ret;
  // Not enougn memory in our storages; make a new storage,
  BOOST_USING_STD_MAX();
  next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks);
  const size_type POD_size = next_size * partition_size +
      details::pool::ct_lcm::value + sizeof(size_type);
  char * const ptr = UserAllocator::malloc(POD_size);
  if (ptr == 0)
    return 0;
  const details::PODptr node(ptr, POD_size);
  // Split up block so we can use what wasn't requested
  //  (we can use "add_block" here because we know that
  //  the free list is empty, so we don't have to use
  //  the slower ordered version)
  if (next_size > num_chunks)
    store().add_block(node.begin() + num_chunks * partition_size,
        node.element_size() - num_chunks * partition_size, partition_size);
  next_size <<= 1;
  //  insert it into the list,
  //   handle border case
  if (!list.valid() || std::greater()(list.begin(), node.begin()))
  {
    node.next(list);
    list = node;
  }
  else
  {
    details::PODptr prev = list;
    while (true)
    {
      // if we're about to hit the end or
      //  if we've found where "node" goes
      if (prev.next_ptr() == 0
          || std::greater()(prev.next_ptr(), node.begin()))
        break;
      prev = prev.next();
    }
    node.next(prev.next());
    prev.next(node);
  }
  //  and return it.
  return node.begin();
}
template 
details::PODptr::size_type>
pool::find_POD(void * const chunk) const
{
  // We have to find which storage this chunk is from.
  details::PODptr iter = list;
  while (iter.valid())
  {
    if (is_from(chunk, iter.begin(), iter.element_size()))
      return iter;
    iter = iter.next();
  }
  return iter;
}
} // namespace boost
#endif 

 

你可能感兴趣的:(list,alignment,less,iterator,delete,insert)