diff DEPENDENCIES/generic/include/boost/pool/simple_segregated_storage.hpp @ 16:2665513ce2d3

Add boost headers
author Chris Cannam
date Tue, 05 Aug 2014 11:11:38 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DEPENDENCIES/generic/include/boost/pool/simple_segregated_storage.hpp	Tue Aug 05 11:11:38 2014 +0100
@@ -0,0 +1,377 @@
+// 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_SIMPLE_SEGREGATED_STORAGE_HPP
+#define BOOST_SIMPLE_SEGREGATED_STORAGE_HPP
+
+/*!
+  \file
+  \brief Simple Segregated Storage.
+  \details A simple segregated storage implementation:
+  simple segregated storage is the basic idea behind the Boost Pool library.
+  Simple segregated storage is the simplest, and probably the fastest,
+  memory allocation/deallocation algorithm.
+  It begins by partitioning a memory block into fixed-size chunks.
+  Where the block comes from is not important until implementation time.
+  A Pool is some object that uses Simple Segregated Storage in this fashion.
+*/
+
+// std::greater
+#include <functional>
+
+#include <boost/pool/poolfwd.hpp>
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable:4127)  // Conditional expression is constant
+#endif
+
+#ifdef BOOST_POOL_VALIDATE
+#  define BOOST_POOL_VALIDATE_INTERNALS validate();
+#else
+#  define BOOST_POOL_VALIDATE_INTERNALS
+#endif
+
+namespace boost {
+
+/*! 
+
+\brief Simple Segregated Storage is the simplest, and probably the fastest,
+memory allocation/deallocation algorithm.  It is responsible for
+partitioning a memory block into fixed-size chunks: where the block comes from 
+is determined by the client of the class.
+
+\details Template class simple_segregated_storage controls access to a free list of memory chunks. 
+Please note that this is a very simple class, with preconditions on almost all its functions. It is intended to 
+be the fastest and smallest possible quick memory allocator - e.g., something to use in embedded systems. 
+This class delegates many difficult preconditions to the user (i.e., alignment issues).
+
+An object of type simple_segregated_storage<SizeType>  is empty  if its free list is empty. 
+If it is not empty, then it is ordered  if its free list is ordered. A free list is ordered if repeated calls 
+to <tt>malloc()</tt> will result in a constantly-increasing sequence of values, as determined by <tt>std::less<void *></tt>. 
+A member function is <i>order-preserving</i> if the free list maintains its order orientation (that is, an 
+ordered free list is still ordered after the member function call).
+
+*/
+template <typename SizeType>
+class simple_segregated_storage
+{
+  public:
+    typedef SizeType size_type;
+
+  private:
+    simple_segregated_storage(const simple_segregated_storage &);
+    void operator=(const simple_segregated_storage &);
+
+    static void * try_malloc_n(void * & start, size_type n,
+        size_type partition_size);
+
+  protected:
+    void * first; /*!< This data member is the free list.
+      It points to the first chunk in the free list,
+      or is equal to 0 if the free list is empty.
+    */
+
+    void * find_prev(void * ptr);
+
+    // for the sake of code readability :)
+    static void * & nextof(void * const ptr)
+    { //! The return value is just *ptr cast to the appropriate type. ptr must not be 0. (For the sake of code readability :)
+    //! As an example, let us assume that we want to truncate the free list after the first chunk.
+    //! That is, we want to set *first to 0; this will result in a free list with only one entry.
+    //! The normal way to do this is to first cast first to a pointer to a pointer to void,
+    //! and then dereference and assign (*static_cast<void **>(first) = 0;).
+    //! This can be done more easily through the use of this convenience function (nextof(first) = 0;).
+    //! \returns dereferenced pointer.
+      return *(static_cast<void **>(ptr));
+    }
+
+  public:
+    // Post: empty()
+    simple_segregated_storage()
+    :first(0)
+    { //! Construct empty storage area.
+      //! \post empty()
+    }
+
+    static void * segregate(void * block,
+        size_type nsz, size_type npartition_sz,
+        void * end = 0);
+
+    // Same preconditions as 'segregate'
+    // Post: !empty()
+    void add_block(void * const block,
+        const size_type nsz, const size_type npartition_sz)
+    { //! Add block
+      //! Segregate this block and merge its free list into the
+      //!  free list referred to by "first".
+      //! \pre Same as segregate.
+      //!  \post !empty()
+      BOOST_POOL_VALIDATE_INTERNALS
+      first = segregate(block, nsz, npartition_sz, first);
+      BOOST_POOL_VALIDATE_INTERNALS
+    }
+
+    // Same preconditions as 'segregate'
+    // Post: !empty()
+    void add_ordered_block(void * const block,
+        const size_type nsz, const size_type npartition_sz)
+    { //! add block (ordered into list)
+      //! This (slower) version of add_block segregates the
+      //!  block and merges its free list into our free list
+      //!  in the proper order.
+       BOOST_POOL_VALIDATE_INTERNALS
+      // Find where "block" would go in the free list
+      void * const loc = find_prev(block);
+
+      // Place either at beginning or in middle/end
+      if (loc == 0)
+        add_block(block, nsz, npartition_sz);
+      else
+        nextof(loc) = segregate(block, nsz, npartition_sz, nextof(loc));
+      BOOST_POOL_VALIDATE_INTERNALS
+    }
+
+    // default destructor.
+
+    bool empty() const
+    { //! \returns true only if simple_segregated_storage is empty.
+      return (first == 0);
+    }
+
+    void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
+    { //! Create a chunk.
+      //!  \pre !empty()
+      //! Increment the "first" pointer to point to the next chunk.
+       BOOST_POOL_VALIDATE_INTERNALS
+      void * const ret = first;
+
+      // Increment the "first" pointer to point to the next chunk.
+      first = nextof(first);
+      BOOST_POOL_VALIDATE_INTERNALS
+      return ret;
+    }
+
+    void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk)
+    { //! Free a chunk.
+      //! \pre chunk was previously returned from a malloc() referring to the same free list.
+      //! \post !empty()
+       BOOST_POOL_VALIDATE_INTERNALS
+      nextof(chunk) = first;
+      first = chunk;
+      BOOST_POOL_VALIDATE_INTERNALS
+    }
+
+    void ordered_free(void * const chunk)
+    { //! This (slower) implementation of 'free' places the memory
+      //!  back in the list in its proper order.
+      //! \pre chunk was previously returned from a malloc() referring to the same free list
+      //! \post !empty().
+
+      // Find where "chunk" goes in the free list
+       BOOST_POOL_VALIDATE_INTERNALS
+      void * const loc = find_prev(chunk);
+
+      // Place either at beginning or in middle/end.
+      if (loc == 0)
+        (free)(chunk);
+      else
+      {
+        nextof(chunk) = nextof(loc);
+        nextof(loc) = chunk;
+      }
+      BOOST_POOL_VALIDATE_INTERNALS
+    }
+
+   void * malloc_n(size_type n, size_type partition_size);
+    
+    //! \pre chunks was previously allocated from *this with the same
+    //!   values for n and partition_size.
+    //! \post !empty()
+    //! \note If you're allocating/deallocating n a lot, you should
+    //!  be using an ordered pool.
+    void free_n(void * const chunks, const size_type n,
+        const size_type partition_size)
+    { 
+       BOOST_POOL_VALIDATE_INTERNALS
+      if(n != 0)
+        add_block(chunks, n * partition_size, partition_size);
+       BOOST_POOL_VALIDATE_INTERNALS
+    }
+
+    // pre: chunks was previously allocated from *this with the same
+    //   values for n and partition_size.
+    // post: !empty()
+    void ordered_free_n(void * const chunks, const size_type n,
+        const size_type partition_size)
+    { //! Free n chunks from order list.
+      //! \pre chunks was previously allocated from *this with the same
+      //!   values for n and partition_size.
+
+      //! \pre n should not be zero (n == 0 has no effect).
+       BOOST_POOL_VALIDATE_INTERNALS
+      if(n != 0)
+        add_ordered_block(chunks, n * partition_size, partition_size);
+       BOOST_POOL_VALIDATE_INTERNALS
+    }
+#ifdef BOOST_POOL_VALIDATE
+    void validate()
+    {
+       int index = 0;
+       void* old = 0;
+       void* ptr = first;
+       while(ptr)
+       {
+          void* pt = nextof(ptr); // trigger possible segfault *before* we update variables
+          ++index;
+          old = ptr;
+          ptr = nextof(ptr);
+       }
+    }
+#endif
+};
+
+//! Traverses the free list referred to by "first",
+//!  and returns the iterator previous to where
+//!  "ptr" would go if it was in the free list.
+//!  Returns 0 if "ptr" would go at the beginning
+//!  of the free list (i.e., before "first").
+
+//! \note Note that this function finds the location previous to where ptr would go
+//! if it was in the free list.
+//! It does not find the entry in the free list before ptr
+//! (unless ptr is already in the free list).
+//! Specifically, find_prev(0) will return 0,
+//! not the last entry in the free list.
+//! \returns location previous to where ptr would go if it was in the free list.
+template <typename SizeType>
+void * simple_segregated_storage<SizeType>::find_prev(void * const ptr)
+{ 
+  // Handle border case.
+  if (first == 0 || std::greater<void *>()(first, ptr))
+    return 0;
+
+  void * iter = first;
+  while (true)
+  {
+    // if we're about to hit the end, or if we've found where "ptr" goes.
+    if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr))
+      return iter;
+
+    iter = nextof(iter);
+  }
+}
+
+//! Segregate block into chunks.
+//! \pre npartition_sz >= sizeof(void *)
+//! \pre    npartition_sz = sizeof(void *) * i, for some integer i
+//! \pre    nsz >= npartition_sz
+//! \pre Block is properly aligned for an array of object of
+//!        size npartition_sz and array of void *.
+//! The requirements above guarantee that any pointer to a chunk
+//! (which is a pointer to an element in an array of npartition_sz)
+//! may be cast to void **.
+template <typename SizeType>
+void * simple_segregated_storage<SizeType>::segregate(
+    void * const block,
+    const size_type sz,
+    const size_type partition_sz,
+    void * const end)
+{
+  // Get pointer to last valid chunk, preventing overflow on size calculations
+  //  The division followed by the multiplication just makes sure that
+  //    old == block + partition_sz * i, for some integer i, even if the
+  //    block size (sz) is not a multiple of the partition size.
+  char * old = static_cast<char *>(block)
+      + ((sz - partition_sz) / partition_sz) * partition_sz;
+
+  // Set it to point to the end
+  nextof(old) = end;
+
+  // Handle border case where sz == partition_sz (i.e., we're handling an array
+  //  of 1 element)
+  if (old == block)
+    return block;
+
+  // Iterate backwards, building a singly-linked list of pointers
+  for (char * iter = old - partition_sz; iter != block;
+      old = iter, iter -= partition_sz)
+    nextof(iter) = old;
+
+  // Point the first pointer, too
+  nextof(block) = old;
+
+  return block;
+}
+
+//! \pre (n > 0), (start != 0), (nextof(start) != 0)
+//! \post (start != 0)
+//! The function attempts to find n contiguous chunks
+//!  of size partition_size in the free list, starting at start.
+//! If it succeds, it returns the last chunk in that contiguous
+//!  sequence, so that the sequence is known by [start, {retval}]
+//! If it fails, it does do either because it's at the end of the
+//!  free list or hits a non-contiguous chunk.  In either case,
+//!  it will return 0, and set start to the last considered
+//!  chunk.  You are at the end of the free list if
+//!  nextof(start) == 0.  Otherwise, start points to the last
+//!  chunk in the contiguous sequence, and nextof(start) points
+//!  to the first chunk in the next contiguous sequence (assuming
+//!  an ordered free list).
+template <typename SizeType>
+void * simple_segregated_storage<SizeType>::try_malloc_n(
+    void * & start, size_type n, const size_type partition_size)
+{
+  void * iter = nextof(start);
+  while (--n != 0)
+  {
+    void * next = nextof(iter);
+    if (next != static_cast<char *>(iter) + partition_size)
+    {
+      // next == 0 (end-of-list) or non-contiguous chunk found
+      start = iter;
+      return 0;
+    }
+    iter = next;
+  }
+  return iter;
+}
+
+//! Attempts to find a contiguous sequence of n partition_sz-sized chunks. If found, removes them 
+//! all from the free list and returns a pointer to the first. If not found, returns 0. It is strongly 
+//! recommended (but not required) that the free list be ordered, as this algorithm will fail to find 
+//! a contiguous sequence unless it is contiguous in the free list as well. Order-preserving. 
+//! O(N) with respect to the size of the free list.
+template <typename SizeType>
+void * simple_segregated_storage<SizeType>::malloc_n(const size_type n,
+    const size_type partition_size)
+{
+   BOOST_POOL_VALIDATE_INTERNALS
+  if(n == 0)
+    return 0;
+  void * start = &first;
+  void * iter;
+  do
+  {
+    if (nextof(start) == 0)
+      return 0;
+    iter = try_malloc_n(start, n, partition_size);
+  } while (iter == 0);
+  void * const ret = nextof(start);
+  nextof(start) = nextof(iter);
+  BOOST_POOL_VALIDATE_INTERNALS
+  return ret;
+}
+
+} // namespace boost
+
+#ifdef BOOST_MSVC
+#pragma warning(pop)
+#endif
+
+#endif