LCOV - code coverage report
Current view: top level - opt/homebrew/include/boost/pool - pool.hpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 108 130 83.1 %
Date: 2026-06-25 07:23:51 Functions: 27 27 100.0 %

          Line data    Source code
       1             : // Copyright (C) 2000, 2001 Stephen Cleary
       2             : //
       3             : // Distributed under the Boost Software License, Version 1.0. (See
       4             : // accompanying file LICENSE_1_0.txt or copy at
       5             : // http://www.boost.org/LICENSE_1_0.txt)
       6             : //
       7             : // See http://www.boost.org for updates, documentation, and revision history.
       8             : 
       9             : #ifndef BOOST_POOL_HPP
      10             : #define BOOST_POOL_HPP
      11             : 
      12             : #include <boost/config.hpp>  // for workarounds
      13             : 
      14             : // std::less, std::less_equal, std::greater
      15             : #include <functional>
      16             : // new[], delete[], std::nothrow
      17             : #include <new>
      18             : // std::size_t, std::ptrdiff_t
      19             : #include <cstddef>
      20             : // std::malloc, std::free
      21             : #include <cstdlib>
      22             : // std::invalid_argument
      23             : #include <exception>
      24             : // std::max
      25             : #include <algorithm>
      26             : 
      27             : #include <boost/pool/poolfwd.hpp>
      28             : 
      29             : // std::numeric_limits
      30             : #include <boost/limits.hpp>
      31             : // boost::integer::static_lcm
      32             : #include <boost/integer/common_factor_ct.hpp>
      33             : // boost::simple_segregated_storage
      34             : #include <boost/pool/simple_segregated_storage.hpp>
      35             : // boost::alignment_of
      36             : #include <boost/type_traits/alignment_of.hpp>
      37             : // BOOST_ASSERT
      38             : #include <boost/assert.hpp>
      39             : 
      40             : #ifdef BOOST_POOL_INSTRUMENT
      41             : #include <iostream>
      42             : #include <iomanip>
      43             : #endif
      44             : #ifdef BOOST_POOL_VALGRIND
      45             : #include <set>
      46             : #include <valgrind/memcheck.h>
      47             : #endif
      48             : 
      49             : #ifdef BOOST_NO_STDC_NAMESPACE
      50             :  namespace std { using ::malloc; using ::free; }
      51             : #endif
      52             : 
      53             : // There are a few places in this file where the expression "this->m" is used.
      54             : // This expression is used to force instantiation-time name lookup, which I am
      55             : //   informed is required for strict Standard compliance.  It's only necessary
      56             : //   if "m" is a member of a base class that is dependent on a template
      57             : //   parameter.
      58             : // Thanks to Jens Maurer for pointing this out!
      59             : 
      60             : /*!
      61             :   \file
      62             :   \brief Provides class \ref pool: a fast memory allocator that guarantees proper alignment of all allocated chunks,
      63             :   and which extends and generalizes the framework provided by the simple segregated storage solution.
      64             :   Also provides two UserAllocator classes which can be used in conjuction with \ref pool.
      65             : */
      66             : 
      67             : /*!
      68             :   \mainpage Boost.Pool Memory Allocation Scheme
      69             : 
      70             :   \section intro_sec Introduction
      71             : 
      72             :    Pool allocation is a memory allocation scheme that is very fast, but limited in its usage.
      73             : 
      74             :    This Doxygen-style documentation is complementary to the
      75             :    full Quickbook-generated html and pdf documentation at www.boost.org.
      76             : 
      77             :   This page generated from file pool.hpp.
      78             : 
      79             : */
      80             : 
      81             : #ifdef BOOST_MSVC
      82             : #pragma warning(push)
      83             : #pragma warning(disable:4127)  // Conditional expression is constant
      84             : #endif
      85             : 
      86             :  namespace boost
      87             : {
      88             : 
      89             : //! \brief Allocator used as the default template parameter for 
      90             : //! a <a href="boost_pool/pool/pooling.html#boost_pool.pool.pooling.user_allocator">UserAllocator</a>
      91             : //! template parameter.  Uses new and delete.
      92             : struct default_user_allocator_new_delete
      93             : {
      94             :   typedef std::size_t size_type; //!< An unsigned integral type that can represent the size of the largest object to be allocated.
      95             :   typedef std::ptrdiff_t difference_type; //!< A signed integral type that can represent the difference of any two pointers.
      96             : 
      97             :   static char * malloc BOOST_PREVENT_MACRO_SUBSTITUTION(const size_type bytes)
      98             :   { //! Attempts to allocate n bytes from the system. Returns 0 if out-of-memory
      99             :     return new (std::nothrow) char[bytes];
     100             :   }
     101             :   static void free BOOST_PREVENT_MACRO_SUBSTITUTION(char * const block)
     102             :   { //! Attempts to de-allocate block.
     103             :     //! \pre Block must have been previously returned from a call to UserAllocator::malloc.
     104             :     delete [] block;
     105             :   }
     106             : };
     107             : 
     108             : //! \brief <a href="boost_pool/pool/pooling.html#boost_pool.pool.pooling.user_allocator">UserAllocator</a>
     109             : //! used as template parameter for \ref pool and \ref object_pool.
     110             : //! Uses malloc and free internally.
     111             : struct default_user_allocator_malloc_free
     112             : {
     113             :   typedef std::size_t size_type; //!< An unsigned integral type that can represent the size of the largest object to be allocated.
     114             :   typedef std::ptrdiff_t difference_type; //!< A signed integral type that can represent the difference of any two pointers.
     115             : 
     116             :   static char * malloc BOOST_PREVENT_MACRO_SUBSTITUTION(const size_type bytes)
     117             :   { return static_cast<char *>((std::malloc)(bytes)); }
     118             :   static void free BOOST_PREVENT_MACRO_SUBSTITUTION(char * const block)
     119             :   { (std::free)(block); }
     120             : };
     121             : 
     122             : namespace details
     123             : {  //! Implemention only.
     124             : 
     125             : template <typename SizeType>
     126             : class PODptr
     127             : { //! PODptr is a class that pretends to be a "pointer" to different class types
     128             :   //!  that don't really exist.  It provides member functions to access the "data"
     129             :   //!  of the "object" it points to.  Since these "class" types are of variable
     130             :   //!  size, and contains some information at the *end* of its memory
     131             :   //!  (for alignment reasons),
     132             :   //! PODptr must contain the size of this "class" as well as the pointer to this "object".
     133             : 
     134             :   /*! \details A PODptr holds the location and size of a memory block allocated from the system. 
     135             :   Each memory block is split logically into three sections:
     136             : 
     137             :   <b>Chunk area</b>. This section may be different sizes. PODptr does not care what the size of the chunks is, 
     138             :   but it does care (and keep track of) the total size of the chunk area.
     139             : 
     140             :   <b>Next pointer</b>. This section is always the same size for a given SizeType. It holds a pointer 
     141             :   to the location of the next memory block in the memory block list, or 0 if there is no such block.
     142             : 
     143             :   <b>Next size</b>. This section is always the same size for a given SizeType. It holds the size of the 
     144             :   next memory block in the memory block list.
     145             : 
     146             : The PODptr class just provides cleaner ways of dealing with raw memory blocks.
     147             : 
     148             : A PODptr object is either valid or invalid. An invalid PODptr is analogous to a null pointer.
     149             : The default constructor for PODptr will result in an invalid object.
     150             : Calling the member function invalidate will result in that object becoming invalid.
     151             : The member function valid can be used to test for validity.
     152             : */
     153             :   public:
     154             :     typedef SizeType size_type;
     155             : 
     156             :   private:
     157             :     char * ptr;
     158             :     size_type sz;
     159             : 
     160          40 :     char * ptr_next_size() const
     161             :     {
     162          40 :       return (ptr + sz - sizeof(size_type));
     163             :     }
     164          20 :     char * ptr_next_ptr() const
     165             :     {
     166          20 :       return (ptr_next_size() -
     167             :           integer::static_lcm<sizeof(size_type), sizeof(void *)>::value);
     168             :     }
     169             : 
     170             :   public:
     171         330 :     PODptr(char * const nptr, const size_type nsize)
     172         165 :     :ptr(nptr), sz(nsize)
     173         165 :     {
     174             :       //! A PODptr may be created to point to a memory block by passing
     175             :       //! the address and size of that memory block into the constructor.
     176             :       //! A PODptr constructed in this way is valid.
     177         330 :     }
     178             :     PODptr()
     179             :     :  ptr(0), sz(0)
     180             :     { //! default constructor for PODptr will result in an invalid object.
     181             :     }
     182             : 
     183         165 :     bool valid() const
     184             :     { //! A PODptr object is either valid or invalid.
     185             :       //! An invalid PODptr is analogous to a null pointer.
     186             :       //! \returns true if PODptr is valid, false if invalid.
     187         165 :       return (begin() != 0);
     188             :     }
     189          10 :     void invalidate()
     190             :     { //! Make object invalid.
     191          10 :       begin() = 0;
     192          10 :     }
     193          20 :     char * & begin()
     194             :     { //! Each PODptr keeps the address and size of its memory block.
     195             :       //! \returns The address of its memory block.
     196          20 :       return ptr;
     197             :   }
     198         195 :     char * begin() const
     199             :     { //! Each PODptr keeps the address and size of its memory block.
     200             :       //! \return The address of its memory block.
     201         195 :       return ptr;
     202             :     }
     203             :     char * end() const
     204             :     { //! \returns begin() plus element_size (a 'past the end' value).
     205             :       return ptr_next_ptr();
     206             :     }
     207          10 :     size_type total_size() const
     208             :     { //! Each PODptr keeps the address and size of its memory block.
     209             :       //! The address may be read or written by the member functions begin.
     210             :       //! The size of the memory block may only be read,
     211             :       //! \returns size of the memory block.
     212          10 :       return sz;
     213             :     }
     214          10 :     size_type element_size() const
     215             :     { //! \returns size of element pointer area.
     216          10 :       return static_cast<size_type>(sz - sizeof(size_type) -
     217             :           integer::static_lcm<sizeof(size_type), sizeof(void *)>::value);
     218             :     }
     219             : 
     220          20 :     size_type & next_size() const
     221             :     { //!
     222             :       //! \returns next_size.
     223          20 :       return *(static_cast<size_type *>(static_cast<void*>((ptr_next_size()))));
     224             :     }
     225          20 :     char * & next_ptr() const
     226             :     {  //! \returns pointer to next pointer area.
     227          20 :       return *(static_cast<char **>(static_cast<void*>(ptr_next_ptr())));
     228             :     }
     229             : 
     230          10 :     PODptr next() const
     231             :     { //! \returns next PODptr.
     232          10 :       return PODptr<size_type>(next_ptr(), next_size());
     233             :     }
     234          10 :     void next(const PODptr & arg) const
     235             :     { //! Sets next PODptr.
     236          10 :       next_ptr() = arg.begin();
     237          10 :       next_size() = arg.total_size();
     238          10 :     }
     239             : }; // class PODptr
     240             : } // namespace details
     241             : 
     242             : #ifndef BOOST_POOL_VALGRIND
     243             : /*!
     244             :   \brief A fast memory allocator that guarantees proper alignment of all allocated chunks.
     245             : 
     246             :   \details Whenever an object of type pool needs memory from the system,
     247             :   it will request it from its UserAllocator template parameter.
     248             :   The amount requested is determined using a doubling algorithm;
     249             :   that is, each time more system memory is allocated,
     250             :   the amount of system memory requested is doubled.
     251             : 
     252             :   Users may control the doubling algorithm by using the following extensions:
     253             : 
     254             :   Users may pass an additional constructor parameter to pool.
     255             :   This parameter is of type size_type,
     256             :   and is the number of chunks to request from the system
     257             :   the first time that object needs to allocate system memory.
     258             :   The default is 32. This parameter may not be 0.
     259             : 
     260             :   Users may also pass an optional third parameter to pool's
     261             :   constructor.  This parameter is of type size_type,
     262             :   and sets a maximum size for allocated chunks.  When this
     263             :   parameter takes the default value of 0, then there is no upper
     264             :   limit on chunk size.
     265             : 
     266             :   Finally, if the doubling algorithm results in no memory
     267             :   being allocated, the pool will backtrack just once, halving
     268             :   the chunk size and trying again.
     269             : 
     270             :   <b>UserAllocator type</b> - the method that the Pool will use to allocate memory from the system.
     271             : 
     272             :   There are essentially two ways to use class pool: the client can call \ref malloc() and \ref free() to allocate
     273             :   and free single chunks of memory, this is the most efficient way to use a pool, but does not allow for
     274             :   the efficient allocation of arrays of chunks.  Alternatively, the client may call \ref ordered_malloc() and \ref
     275             :   ordered_free(), in which case the free list is maintained in an ordered state, and efficient allocation of arrays
     276             :   of chunks are possible.  However, this latter option can suffer from poor performance when large numbers of
     277             :   allocations are performed.
     278             : 
     279             : */
     280             : template <typename UserAllocator>
     281             : class pool: protected simple_segregated_storage < typename UserAllocator::size_type >
     282             : {
     283             :   public:
     284             :     typedef UserAllocator user_allocator; //!< User allocator.
     285             :     typedef typename UserAllocator::size_type size_type;  //!< An unsigned integral type that can represent the size of the largest object to be allocated.
     286             :     typedef typename UserAllocator::difference_type difference_type;  //!< A signed integral type that can represent the difference of any two pointers.
     287             : 
     288             :   private:
     289             :     BOOST_STATIC_CONSTANT(size_type, min_alloc_size =
     290             :         (::boost::integer::static_lcm<sizeof(void *), sizeof(size_type)>::value) );
     291             :     BOOST_STATIC_CONSTANT(size_type, min_align =
     292             :         (::boost::integer::static_lcm< ::boost::alignment_of<void *>::value, ::boost::alignment_of<size_type>::value>::value) );
     293             : 
     294             :     //! \returns 0 if out-of-memory.
     295             :     //! Called if malloc/ordered_malloc needs to resize the free list.
     296             :     void * malloc_need_resize(); //! Called if malloc needs to resize the free list.
     297             :     void * ordered_malloc_need_resize();  //! Called if ordered_malloc needs to resize the free list.
     298             : 
     299             :   protected:
     300             :     details::PODptr<size_type> list; //!< List structure holding ordered blocks.
     301             : 
     302        1278 :     simple_segregated_storage<size_type> & store()
     303             :     { //! \returns pointer to store.
     304        1278 :       return *this;
     305             :     }
     306             :     const simple_segregated_storage<size_type> & store() const
     307             :     { //! \returns pointer to store.
     308             :       return *this;
     309             :     }
     310             :     const size_type requested_size;
     311             :     size_type next_size;
     312             :     size_type start_size;
     313             :     size_type max_size;
     314             : 
     315             :     //! finds which POD in the list 'chunk' was allocated from.
     316             :     details::PODptr<size_type> find_POD(void * const chunk) const;
     317             : 
     318             :     // is_from() tests a chunk to determine if it belongs in a block.
     319             :     static bool is_from(void * const chunk, char * const i,
     320             :         const size_type sizeof_i)
     321             :     { //! \param chunk chunk to check if is from this pool.
     322             :       //! \param i memory chunk at i with element sizeof_i.
     323             :       //! \param sizeof_i element size (size of the chunk area of that block, not the total size of that block).
     324             :       //! \returns true if chunk was allocated or may be returned.
     325             :       //! as the result of a future allocation.
     326             :       //!
     327             :       //! Returns false if chunk was allocated from some other pool,
     328             :       //! or may be returned as the result of a future allocation from some other pool.
     329             :       //! Otherwise, the return value is meaningless.
     330             :       //!
     331             :       //! Note that this function may not be used to reliably test random pointer values.
     332             : 
     333             :       // We use std::less_equal and std::less to test 'chunk'
     334             :       //  against the array bounds because standard operators
     335             :       //  may return unspecified results.
     336             :       // This is to ensure portability.  The operators < <= > >= are only
     337             :       //  defined for pointers to objects that are 1) in the same array, or
     338             :       //  2) subobjects of the same object [5.9/2].
     339             :       // The functor objects guarantee a total order for any pointer [20.3.3/8]
     340             :       std::less_equal<void *> lt_eq;
     341             :       std::less<void *> lt;
     342             :       return (lt_eq(i, chunk) && lt(chunk, i + sizeof_i));
     343             :     }
     344             : 
     345        2202 :     size_type alloc_size() const
     346             :     { //!  Calculated size of the memory chunks that will be allocated by this Pool.
     347             :       //! \returns allocated size.
     348             :       // For alignment reasons, this used to be defined to be lcm(requested_size, sizeof(void *), sizeof(size_type)),
     349             :       // but is now more parsimonious: just rounding up to the minimum required alignment of our housekeeping data
     350             :       // when required.  This works provided all alignments are powers of two.
     351        2202 :       size_type s = (std::max)(requested_size, min_alloc_size);
     352        2202 :       size_type rem = s % min_align;
     353        2202 :       if(rem)
     354           0 :          s += min_align - rem;
     355        2202 :       BOOST_ASSERT(s >= min_alloc_size);
     356        2202 :       BOOST_ASSERT(s % min_align == 0);
     357        2202 :       return s;
     358             :     }
     359             : 
     360         934 :     size_type max_chunks() const
     361             :     { //! Calculated maximum number of memory chunks that can be allocated in a single call by this Pool.
     362         934 :       size_type POD_size = integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type);
     363         934 :       return ((std::numeric_limits<size_type>::max)() - POD_size) / alloc_size();
     364             :     }
     365             : 
     366             :     static void * & nextof(void * const ptr)
     367             :     { //! \returns Pointer dereferenced.
     368             :       //! (Provided and used for the sake of code readability :)
     369             :       return *(static_cast<void **>(ptr));
     370             :     }
     371             : 
     372             :   public:
     373             :     // pre: npartition_size != 0 && nnext_size != 0
     374         290 :     explicit pool(const size_type nrequested_size,
     375             :         const size_type nnext_size = 32,
     376             :         const size_type nmax_size = 0)
     377             :     :
     378         145 :         list(0, 0), requested_size(nrequested_size), next_size(nnext_size), start_size(nnext_size),max_size(nmax_size)
     379         145 :     { //!   Constructs a new empty Pool that can be used to allocate chunks of size RequestedSize.
     380             :       //! \param nrequested_size  Requested chunk size
     381             :       //! \param  nnext_size parameter is of type size_type,
     382             :       //!   is the number of chunks to request from the system
     383             :       //!   the first time that object needs to allocate system memory.
     384             :       //!   The default is 32. This parameter may not be 0.
     385             :       //! \param nmax_size is the maximum number of chunks to allocate in one block.
     386         145 :       set_next_size(nnext_size);
     387         145 :       set_max_size(nmax_size);
     388         290 :     }
     389             : 
     390         290 :     ~pool()
     391         145 :     { //!   Destructs the Pool, freeing its list of memory blocks.
     392         145 :       purge_memory();
     393         290 :     }
     394             : 
     395             :     // Releases memory blocks that don't have chunks allocated
     396             :     // pre: lists are ordered
     397             :     //  Returns true if memory was actually deallocated
     398             :     bool release_memory();
     399             : 
     400             :     // Releases *all* memory blocks, even if chunks are still allocated
     401             :     //  Returns true if memory was actually deallocated
     402             :     bool purge_memory();
     403             : 
     404             :     size_type get_next_size() const
     405             :     { //! Number of chunks to request from the system the next time that object needs to allocate system memory. This value should never be 0.
     406             :       //! \returns next_size;
     407             :       return next_size;
     408             :     }
     409         155 :     void set_next_size(const size_type nnext_size)
     410             :     { //! Set number of chunks to request from the system the next time that object needs to allocate system memory. This value should never be set to 0.
     411             :       BOOST_USING_STD_MIN();
     412         155 :       next_size = start_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(nnext_size, max_chunks());
     413         155 :     }
     414             :     size_type get_max_size() const
     415             :     { //! \returns max_size.
     416             :       return max_size;
     417             :     }
     418         145 :     void set_max_size(const size_type nmax_size)
     419             :     { //! Set max_size.
     420             :       BOOST_USING_STD_MIN();
     421         145 :       max_size = min BOOST_PREVENT_MACRO_SUBSTITUTION(nmax_size, max_chunks());
     422         145 :     }
     423        3170 :     size_type get_requested_size() const
     424             :     { //!   \returns the requested size passed into the constructor.
     425             :       //! (This value will not change during the lifetime of a Pool object).
     426        3170 :       return requested_size;
     427             :     }
     428             : 
     429             :     // Both malloc and ordered_malloc do a quick inlined check first for any
     430             :     //  free chunks.  Only if we need to get another memory block do we call
     431             :     //  the non-inlined *_need_resize() functions.
     432             :     // Returns 0 if out-of-memory
     433             :     void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
     434             :     { //! Allocates a chunk of memory. Searches in the list of memory blocks
     435             :       //! for a block that has a free chunk, and returns that free chunk if found.
     436             :       //! Otherwise, creates a new memory block, adds its free list to pool's free list,
     437             :       //! \returns a free chunk from that block.
     438             :       //! If a new memory block cannot be allocated, returns 0. Amortized O(1).
     439             :       // Look for a non-empty storage
     440             :       if (!store().empty())
     441             :         return (store().malloc)();
     442             :       return malloc_need_resize();
     443             :     }
     444             : 
     445             :     void * ordered_malloc()
     446             :     { //! Same as malloc, only merges the free lists, to preserve order. Amortized O(1).
     447             :       //! \returns a free chunk from that block.
     448             :       //! If a new memory block cannot be allocated, returns 0. Amortized O(1).
     449             :       // Look for a non-empty storage
     450             :       if (!store().empty())
     451             :         return (store().malloc)();
     452             :       return ordered_malloc_need_resize();
     453             :     }
     454             : 
     455             :     // Returns 0 if out-of-memory
     456             :     // Allocate a contiguous section of n chunks
     457             :     void * ordered_malloc(size_type n);
     458             :       //! Same as malloc, only allocates enough contiguous chunks to cover n * requested_size bytes. Amortized O(n).
     459             :       //! \returns a free chunk from that block.
     460             :       //! If a new memory block cannot be allocated, returns 0. Amortized O(1).
     461             : 
     462             :     // pre: 'chunk' must have been previously
     463             :     //        returned by *this.malloc().
     464             :     void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk)
     465             :     { //!   Deallocates a chunk of memory. Note that chunk may not be 0. O(1).
     466             :       //!
     467             :       //! Chunk must have been previously returned by t.malloc() or t.ordered_malloc().
     468             :       //! Assumes that chunk actually refers to a block of chunks
     469             :       //! spanning n * partition_sz bytes.
     470             :       //! deallocates each chunk in that block.
     471             :       //! Note that chunk may not be 0. O(n).
     472             :       (store().free)(chunk);
     473             :     }
     474             : 
     475             :     // pre: 'chunk' must have been previously
     476             :     //        returned by *this.malloc().
     477             :     void ordered_free(void * const chunk)
     478             :     { //! Same as above, but is order-preserving.
     479             :       //!
     480             :       //! Note that chunk may not be 0. O(N) with respect to the size of the free list.
     481             :       //! chunk must have been previously returned by t.malloc() or t.ordered_malloc().
     482             :       store().ordered_free(chunk);
     483             :     }
     484             : 
     485             :     // pre: 'chunk' must have been previously
     486             :     //        returned by *this.malloc(n).
     487             :     void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunks, const size_type n)
     488             :     { //! Assumes that chunk actually refers to a block of chunks.
     489             :       //!
     490             :       //! chunk must have been previously returned by t.ordered_malloc(n)
     491             :       //! spanning n * partition_sz bytes.
     492             :       //! Deallocates each chunk in that block.
     493             :       //! Note that chunk may not be 0. O(n).
     494             :       const size_type partition_size = alloc_size();
     495             :       const size_type total_req_size = n * requested_size;
     496             :       const size_type num_chunks = total_req_size / partition_size +
     497             :           ((total_req_size % partition_size) ? true : false);
     498             : 
     499             :       store().free_n(chunks, num_chunks, partition_size);
     500             :     }
     501             : 
     502             :     // pre: 'chunk' must have been previously
     503             :     //        returned by *this.malloc(n).
     504         634 :     void ordered_free(void * const chunks, const size_type n)
     505             :     { //! Assumes that chunk actually refers to a block of chunks spanning n * partition_sz bytes;
     506             :       //! deallocates each chunk in that block.
     507             :       //!
     508             :       //! Note that chunk may not be 0. Order-preserving. O(N + n) where N is the size of the free list.
     509             :       //! chunk must have been previously returned by t.malloc() or t.ordered_malloc().
     510             : 
     511         634 :       const size_type partition_size = alloc_size();
     512         634 :       const size_type total_req_size = n * requested_size;
     513        1268 :       const size_type num_chunks = total_req_size / partition_size +
     514         634 :           ((total_req_size % partition_size) ? true : false);
     515             : 
     516         634 :       store().ordered_free_n(chunks, num_chunks, partition_size);
     517         634 :     }
     518             : 
     519             :     // is_from() tests a chunk to determine if it was allocated from *this
     520             :     bool is_from(void * const chunk) const
     521             :     { //! \returns Returns true if chunk was allocated from u or
     522             :       //! may be returned as the result of a future allocation from u.
     523             :       //! Returns false if chunk was allocated from some other pool or
     524             :       //! may be returned as the result of a future allocation from some other pool.
     525             :       //! Otherwise, the return value is meaningless.
     526             :       //! Note that this function may not be used to reliably test random pointer values.
     527             :       return (find_POD(chunk).valid());
     528             :     }
     529             : };
     530             : 
     531             : #ifndef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
     532             : template <typename UserAllocator>
     533             : typename pool<UserAllocator>::size_type const pool<UserAllocator>::min_alloc_size;
     534             : template <typename UserAllocator>
     535             : typename pool<UserAllocator>::size_type const pool<UserAllocator>::min_align;
     536             : #endif
     537             : 
     538             : template <typename UserAllocator>
     539             : bool pool<UserAllocator>::release_memory()
     540             : { //! pool must be ordered. Frees every memory block that doesn't have any allocated chunks.
     541             :   //! \returns true if at least one memory block was freed.
     542             : 
     543             :   // ret is the return value: it will be set to true when we actually call
     544             :   //  UserAllocator::free(..)
     545             :   bool ret = false;
     546             : 
     547             :   // This is a current & previous iterator pair over the memory block list
     548             :   details::PODptr<size_type> ptr = list;
     549             :   details::PODptr<size_type> prev;
     550             : 
     551             :   // This is a current & previous iterator pair over the free memory chunk list
     552             :   //  Note that "prev_free" in this case does NOT point to the previous memory
     553             :   //  chunk in the free list, but rather the last free memory chunk before the
     554             :   //  current block.
     555             :   void * free_p = this->first;
     556             :   void * prev_free_p = 0;
     557             : 
     558             :   const size_type partition_size = alloc_size();
     559             : 
     560             :   // Search through all the all the allocated memory blocks
     561             :   while (ptr.valid())
     562             :   {
     563             :     // At this point:
     564             :     //  ptr points to a valid memory block
     565             :     //  free_p points to either:
     566             :     //    0 if there are no more free chunks
     567             :     //    the first free chunk in this or some next memory block
     568             :     //  prev_free_p points to either:
     569             :     //    the last free chunk in some previous memory block
     570             :     //    0 if there is no such free chunk
     571             :     //  prev is either:
     572             :     //    the PODptr whose next() is ptr
     573             :     //    !valid() if there is no such PODptr
     574             : 
     575             :     // If there are no more free memory chunks, then every remaining
     576             :     //  block is allocated out to its fullest capacity, and we can't
     577             :     //  release any more memory
     578             :     if (free_p == 0)
     579             :       break;
     580             : 
     581             :     // We have to check all the chunks.  If they are *all* free (i.e., present
     582             :     //  in the free list), then we can free the block.
     583             :     bool all_chunks_free = true;
     584             : 
     585             :     // Iterate 'i' through all chunks in the memory block
     586             :     // if free starts in the memory block, be careful to keep it there
     587             :     void * saved_free = free_p;
     588             :     for (char * i = ptr.begin(); i != ptr.end(); i += partition_size)
     589             :     {
     590             :       // If this chunk is not free
     591             :       if (i != free_p)
     592             :       {
     593             :         // We won't be able to free this block
     594             :         all_chunks_free = false;
     595             : 
     596             :         // free_p might have travelled outside ptr
     597             :         free_p = saved_free;
     598             :         // Abort searching the chunks; we won't be able to free this
     599             :         //  block because a chunk is not free.
     600             :         break;
     601             :       }
     602             : 
     603             :       // We do not increment prev_free_p because we are in the same block
     604             :       free_p = nextof(free_p);
     605             :     }
     606             : 
     607             :     // post: if the memory block has any chunks, free_p points to one of them
     608             :     // otherwise, our assertions above are still valid
     609             : 
     610             :     const details::PODptr<size_type> next = ptr.next();
     611             : 
     612             :     if (!all_chunks_free)
     613             :     {
     614             :       if (is_from(free_p, ptr.begin(), ptr.element_size()))
     615             :       {
     616             :         std::less<void *> lt;
     617             :         void * const end = ptr.end();
     618             :         do
     619             :         {
     620             :           prev_free_p = free_p;
     621             :           free_p = nextof(free_p);
     622             :         } while (free_p && lt(free_p, end));
     623             :       }
     624             :       // This invariant is now restored:
     625             :       //     free_p points to the first free chunk in some next memory block, or
     626             :       //       0 if there is no such chunk.
     627             :       //     prev_free_p points to the last free chunk in this memory block.
     628             : 
     629             :       // We are just about to advance ptr.  Maintain the invariant:
     630             :       // prev is the PODptr whose next() is ptr, or !valid()
     631             :       // if there is no such PODptr
     632             :       prev = ptr;
     633             :     }
     634             :     else
     635             :     {
     636             :       // All chunks from this block are free
     637             : 
     638             :       // Remove block from list
     639             :       if (prev.valid())
     640             :         prev.next(next);
     641             :       else
     642             :         list = next;
     643             : 
     644             :       // Remove all entries in the free list from this block
     645             :       if (prev_free_p != 0)
     646             :         nextof(prev_free_p) = free_p;
     647             :       else
     648             :         this->first = free_p;
     649             : 
     650             :       // And release memory
     651             :       (UserAllocator::free)(ptr.begin());
     652             :       ret = true;
     653             :     }
     654             : 
     655             :     // Increment ptr
     656             :     ptr = next;
     657             :   }
     658             : 
     659             :   next_size = start_size;
     660             :   return ret;
     661             : }
     662             : 
     663             : template <typename UserAllocator>
     664         145 : bool pool<UserAllocator>::purge_memory()
     665             : { //! pool must be ordered.
     666             :   //! Frees every memory block.
     667             :   //!
     668             :   //! This function invalidates any pointers previously returned
     669             :   //! by allocation functions of t.
     670             :   //! \returns true if at least one memory block was freed.
     671             : 
     672         145 :   details::PODptr<size_type> iter = list;
     673             : 
     674         145 :   if (!iter.valid())
     675         135 :     return false;
     676             : 
     677          10 :   do
     678             :   {
     679             :     // hold "next" pointer
     680          10 :     const details::PODptr<size_type> next = iter.next();
     681             : 
     682             :     // delete the storage
     683          10 :     (UserAllocator::free)(iter.begin());
     684             : 
     685             :     // increment iter
     686          10 :     iter = next;
     687          10 :   } while (iter.valid());
     688             : 
     689          10 :   list.invalidate();
     690          10 :   this->first = 0;
     691          10 :   next_size = start_size;
     692             : 
     693          10 :   return true;
     694         145 : }
     695             : 
     696             : template <typename UserAllocator>
     697             : void * pool<UserAllocator>::malloc_need_resize()
     698             : { //! No memory in any of our storages; make a new storage,
     699             :   //!  Allocates chunk in newly malloc aftert resize.
     700             :   //! \returns pointer to chunk.
     701             :   size_type partition_size = alloc_size();
     702             :   size_type POD_size = static_cast<size_type>(next_size * partition_size +
     703             :       integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
     704             :   char * ptr = (UserAllocator::malloc)(POD_size);
     705             :   if (ptr == 0)
     706             :   {
     707             :      if(next_size > 4)
     708             :      {
     709             :         next_size >>= 1;
     710             :         partition_size = alloc_size();
     711             :         POD_size = static_cast<size_type>(next_size * partition_size +
     712             :             integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
     713             :         ptr = (UserAllocator::malloc)(POD_size);
     714             :      }
     715             :      if(ptr == 0)
     716             :         return 0;
     717             :   }
     718             :   const details::PODptr<size_type> node(ptr, POD_size);
     719             : 
     720             :   BOOST_USING_STD_MIN();
     721             :   if(!max_size)
     722             :     set_next_size(next_size << 1);
     723             :   else if( next_size*partition_size/requested_size < max_size)
     724             :     set_next_size(min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size * requested_size / partition_size));
     725             : 
     726             :   //  initialize it,
     727             :   store().add_block(node.begin(), node.element_size(), partition_size);
     728             : 
     729             :   //  insert it into the list,
     730             :   node.next(list);
     731             :   list = node;
     732             : 
     733             :   //  and return a chunk from it.
     734             :   return (store().malloc)();
     735             : }
     736             : 
     737             : template <typename UserAllocator>
     738             : void * pool<UserAllocator>::ordered_malloc_need_resize()
     739             : { //! No memory in any of our storages; make a new storage,
     740             :   //! \returns pointer to new chunk.
     741             :   size_type partition_size = alloc_size();
     742             :   size_type POD_size = static_cast<size_type>(next_size * partition_size +
     743             :       integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
     744             :   char * ptr = (UserAllocator::malloc)(POD_size);
     745             :   if (ptr == 0)
     746             :   {
     747             :      if(next_size > 4)
     748             :      {
     749             :        next_size >>= 1;
     750             :        partition_size = alloc_size();
     751             :        POD_size = static_cast<size_type>(next_size * partition_size +
     752             :                     integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
     753             :        ptr = (UserAllocator::malloc)(POD_size);
     754             :      }
     755             :      if(ptr == 0)
     756             :        return 0;
     757             :   }
     758             :   const details::PODptr<size_type> node(ptr, POD_size);
     759             : 
     760             :   BOOST_USING_STD_MIN();
     761             :   if(!max_size)
     762             :     set_next_size(next_size << 1);
     763             :   else if( next_size*partition_size/requested_size < max_size)
     764             :     set_next_size(min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size * requested_size / partition_size));
     765             : 
     766             :   //  initialize it,
     767             :   //  (we can use "add_block" here because we know that
     768             :   //  the free list is empty, so we don't have to use
     769             :   //  the slower ordered version)
     770             :   store().add_ordered_block(node.begin(), node.element_size(), partition_size);
     771             : 
     772             :   //  insert it into the list,
     773             :   //   handle border case
     774             :   if (!list.valid() || std::greater<void *>()(list.begin(), node.begin()))
     775             :   {
     776             :     node.next(list);
     777             :     list = node;
     778             :   }
     779             :   else
     780             :   {
     781             :     details::PODptr<size_type> prev = list;
     782             : 
     783             :     while (true)
     784             :     {
     785             :       // if we're about to hit the end or
     786             :       //  if we've found where "node" goes
     787             :       if (prev.next_ptr() == 0
     788             :           || std::greater<void *>()(prev.next_ptr(), node.begin()))
     789             :         break;
     790             : 
     791             :       prev = prev.next();
     792             :     }
     793             : 
     794             :     node.next(prev.next());
     795             :     prev.next(node);
     796             :   }
     797             :   //  and return a chunk from it.
     798             :   return (store().malloc)();
     799             : }
     800             : 
     801             : template <typename UserAllocator>
     802         634 : void * pool<UserAllocator>::ordered_malloc(const size_type n)
     803             : { //! Gets address of a chunk n, allocating new memory if not already available.
     804             :   //! \returns Address of chunk n if allocated ok.
     805             :   //! \returns 0 if not enough memory for n chunks.
     806         634 :   if (n > max_chunks())
     807           0 :     return 0;
     808             : 
     809         634 :   const size_type partition_size = alloc_size();
     810         634 :   const size_type total_req_size = n * requested_size;
     811        1268 :   const size_type num_chunks = total_req_size / partition_size +
     812         634 :       ((total_req_size % partition_size) ? true : false);
     813             : 
     814         634 :   void * ret = store().malloc_n(num_chunks, partition_size);
     815             : 
     816             : #ifdef BOOST_POOL_INSTRUMENT
     817             :   std::cout << "Allocating " << n << " chunks from pool of size " << partition_size << std::endl;
     818             : #endif
     819         634 :   if ((ret != 0) || (n == 0))
     820         624 :     return ret;
     821             : 
     822             : #ifdef BOOST_POOL_INSTRUMENT
     823             :   std::cout << "Cache miss, allocating another chunk...\n";
     824             : #endif
     825             : 
     826             :   // Not enough memory in our storages; make a new storage,
     827             :   BOOST_USING_STD_MAX();
     828          10 :   next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks);
     829          20 :   size_type POD_size = static_cast<size_type>(next_size * partition_size +
     830          10 :       integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
     831          10 :   char * ptr = (UserAllocator::malloc)(POD_size);
     832          10 :   if (ptr == 0)
     833             :   {
     834           0 :      if(num_chunks < next_size)
     835             :      {
     836             :         // Try again with just enough memory to do the job, or at least whatever we
     837             :         // allocated last time:
     838           0 :         next_size >>= 1;
     839           0 :         next_size = max BOOST_PREVENT_MACRO_SUBSTITUTION(next_size, num_chunks);
     840           0 :         POD_size = static_cast<size_type>(next_size * partition_size +
     841           0 :             integer::static_lcm<sizeof(size_type), sizeof(void *)>::value + sizeof(size_type));
     842           0 :         ptr = (UserAllocator::malloc)(POD_size);
     843           0 :      }
     844           0 :      if(ptr == 0)
     845           0 :        return 0;
     846           0 :   }
     847          10 :   const details::PODptr<size_type> node(ptr, POD_size);
     848             : 
     849             :   // Split up block so we can use what wasn't requested.
     850          10 :   if (next_size > num_chunks)
     851          20 :     store().add_ordered_block(node.begin() + num_chunks * partition_size,
     852          10 :         node.element_size() - num_chunks * partition_size, partition_size);
     853             : 
     854             :   BOOST_USING_STD_MIN();
     855          10 :   if(!max_size)
     856          10 :     set_next_size(next_size << 1);
     857           0 :   else if( next_size*partition_size/requested_size < max_size)
     858           0 :     set_next_size(min BOOST_PREVENT_MACRO_SUBSTITUTION(next_size << 1, max_size * requested_size / partition_size));
     859             : 
     860             :   //  insert it into the list,
     861             :   //   handle border case.
     862          10 :   if (!list.valid() || std::greater<void *>()(list.begin(), node.begin()))
     863             :   {
     864          10 :     node.next(list);
     865          10 :     list = node;
     866          10 :   }
     867             :   else
     868             :   {
     869           0 :     details::PODptr<size_type> prev = list;
     870             : 
     871           0 :     while (true)
     872             :     {
     873             :       // if we're about to hit the end, or if we've found where "node" goes.
     874           0 :       if (prev.next_ptr() == 0
     875           0 :           || std::greater<void *>()(prev.next_ptr(), node.begin()))
     876           0 :         break;
     877             : 
     878           0 :       prev = prev.next();
     879             :     }
     880             : 
     881           0 :     node.next(prev.next());
     882           0 :     prev.next(node);
     883             :   }
     884             : 
     885             :   //  and return it.
     886          10 :   return node.begin();
     887         634 : }
     888             : 
     889             : template <typename UserAllocator>
     890             : details::PODptr<typename pool<UserAllocator>::size_type>
     891             : pool<UserAllocator>::find_POD(void * const chunk) const
     892             : { //! find which PODptr storage memory that this chunk is from.
     893             :   //! \returns the PODptr that holds this chunk.
     894             :   // Iterate down list to find which storage this chunk is from.
     895             :   details::PODptr<size_type> iter = list;
     896             :   while (iter.valid())
     897             :   {
     898             :     if (is_from(chunk, iter.begin(), iter.element_size()))
     899             :       return iter;
     900             :     iter = iter.next();
     901             :   }
     902             : 
     903             :   return iter;
     904             : }
     905             : 
     906             : #else // BOOST_POOL_VALGRIND
     907             : 
     908             : template<typename UserAllocator> 
     909             : class pool 
     910             : {
     911             : public:
     912             :   // types
     913             :   typedef UserAllocator                  user_allocator;   // User allocator. 
     914             :   typedef typename UserAllocator::size_type       size_type;        // An unsigned integral type that can represent the size of the largest object to be allocated. 
     915             :   typedef typename UserAllocator::difference_type difference_type;  // A signed integral type that can represent the difference of any two pointers. 
     916             : 
     917             :   // construct/copy/destruct
     918             :   explicit pool(const size_type s, const size_type = 32, const size_type m = 0) : chunk_size(s), max_alloc_size(m) {}
     919             :   ~pool()
     920             :   {
     921             :      purge_memory();
     922             :   }
     923             : 
     924             :   bool release_memory()
     925             :   {
     926             :      bool ret = free_list.empty() ? false : true;
     927             :      for(std::set<void*>::iterator pos = free_list.begin(); pos != free_list.end(); ++pos)
     928             :      {
     929             :         (user_allocator::free)(static_cast<char*>(*pos));
     930             :      }
     931             :      free_list.clear();
     932             :      return ret;
     933             :   }
     934             :   bool purge_memory()
     935             :   {
     936             :      bool ret = free_list.empty() && used_list.empty() ? false : true;
     937             :      for(std::set<void*>::iterator pos = free_list.begin(); pos != free_list.end(); ++pos)
     938             :      {
     939             :         (user_allocator::free)(static_cast<char*>(*pos));
     940             :      }
     941             :      free_list.clear();
     942             :      for(std::set<void*>::iterator pos = used_list.begin(); pos != used_list.end(); ++pos)
     943             :      {
     944             :         (user_allocator::free)(static_cast<char*>(*pos));
     945             :      }
     946             :      used_list.clear();
     947             :      return ret;
     948             :   }
     949             :   size_type get_next_size() const
     950             :   {
     951             :      return 1;
     952             :   }
     953             :   void set_next_size(const size_type){}
     954             :   size_type get_max_size() const
     955             :   {
     956             :      return max_alloc_size;
     957             :   }
     958             :   void set_max_size(const size_type s)
     959             :   {
     960             :      max_alloc_size = s;
     961             :   }
     962             :   size_type get_requested_size() const
     963             :   {
     964             :      return chunk_size;
     965             :   }
     966             :   void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
     967             :   {
     968             :      void* ret;
     969             :      if(free_list.empty())
     970             :      {
     971             :         ret = (user_allocator::malloc)(chunk_size);
     972             :         VALGRIND_MAKE_MEM_UNDEFINED(ret, chunk_size);
     973             :      }
     974             :      else
     975             :      {
     976             :         ret = *free_list.begin();
     977             :         free_list.erase(free_list.begin());
     978             :         VALGRIND_MAKE_MEM_UNDEFINED(ret, chunk_size);
     979             :      }
     980             :      used_list.insert(ret);
     981             :      return ret;
     982             :   }
     983             :   void * ordered_malloc()
     984             :   {
     985             :      return (this->malloc)();
     986             :   }
     987             :   void * ordered_malloc(size_type n)
     988             :   {
     989             :      if(max_alloc_size && (n > max_alloc_size))
     990             :         return 0;
     991             :      void* ret = (user_allocator::malloc)(chunk_size * n);
     992             :      used_list.insert(ret);
     993             :      return ret;
     994             :   }
     995             :   void free BOOST_PREVENT_MACRO_SUBSTITUTION(void *const chunk)
     996             :   {
     997             :      BOOST_ASSERT(used_list.count(chunk) == 1);
     998             :      BOOST_ASSERT(free_list.count(chunk) == 0);
     999             :      used_list.erase(chunk);
    1000             :      free_list.insert(chunk);
    1001             :      VALGRIND_MAKE_MEM_NOACCESS(chunk, chunk_size);
    1002             :   }
    1003             :   void ordered_free(void *const chunk)
    1004             :   {
    1005             :      return (this->free)(chunk);
    1006             :   }
    1007             :   void free BOOST_PREVENT_MACRO_SUBSTITUTION(void *const chunk, const size_type)
    1008             :   {
    1009             :      BOOST_ASSERT(used_list.count(chunk) == 1);
    1010             :      BOOST_ASSERT(free_list.count(chunk) == 0);
    1011             :      used_list.erase(chunk);
    1012             :      (user_allocator::free)(static_cast<char*>(chunk));
    1013             :   }
    1014             :   void ordered_free(void *const chunk, const size_type n)
    1015             :   {
    1016             :      (this->free)(chunk, n);
    1017             :   }
    1018             :   bool is_from(void *const chunk) const
    1019             :   {
    1020             :      return used_list.count(chunk) || free_list.count(chunk);
    1021             :   }
    1022             : 
    1023             : protected:
    1024             :    size_type chunk_size, max_alloc_size;
    1025             :    std::set<void*> free_list, used_list;
    1026             : };
    1027             : 
    1028             : #endif
    1029             : 
    1030             : } // namespace boost
    1031             : 
    1032             : #ifdef BOOST_MSVC
    1033             : #pragma warning(pop)
    1034             : #endif
    1035             : 
    1036             : #endif // #ifdef BOOST_POOL_HPP
    1037             : 

Generated by: LCOV version 1.16