annotate DEPENDENCIES/generic/include/boost/pool/simple_segregated_storage.hpp @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents 2665513ce2d3
children
rev   line source
Chris@16 1 // Copyright (C) 2000, 2001 Stephen Cleary
Chris@16 2 //
Chris@16 3 // Distributed under the Boost Software License, Version 1.0. (See
Chris@16 4 // accompanying file LICENSE_1_0.txt or copy at
Chris@16 5 // http://www.boost.org/LICENSE_1_0.txt)
Chris@16 6 //
Chris@16 7 // See http://www.boost.org for updates, documentation, and revision history.
Chris@16 8
Chris@16 9 #ifndef BOOST_SIMPLE_SEGREGATED_STORAGE_HPP
Chris@16 10 #define BOOST_SIMPLE_SEGREGATED_STORAGE_HPP
Chris@16 11
Chris@16 12 /*!
Chris@16 13 \file
Chris@16 14 \brief Simple Segregated Storage.
Chris@16 15 \details A simple segregated storage implementation:
Chris@16 16 simple segregated storage is the basic idea behind the Boost Pool library.
Chris@16 17 Simple segregated storage is the simplest, and probably the fastest,
Chris@16 18 memory allocation/deallocation algorithm.
Chris@16 19 It begins by partitioning a memory block into fixed-size chunks.
Chris@16 20 Where the block comes from is not important until implementation time.
Chris@16 21 A Pool is some object that uses Simple Segregated Storage in this fashion.
Chris@16 22 */
Chris@16 23
Chris@16 24 // std::greater
Chris@16 25 #include <functional>
Chris@16 26
Chris@16 27 #include <boost/pool/poolfwd.hpp>
Chris@16 28
Chris@16 29 #ifdef BOOST_MSVC
Chris@16 30 #pragma warning(push)
Chris@16 31 #pragma warning(disable:4127) // Conditional expression is constant
Chris@16 32 #endif
Chris@16 33
Chris@16 34 #ifdef BOOST_POOL_VALIDATE
Chris@16 35 # define BOOST_POOL_VALIDATE_INTERNALS validate();
Chris@16 36 #else
Chris@16 37 # define BOOST_POOL_VALIDATE_INTERNALS
Chris@16 38 #endif
Chris@16 39
Chris@16 40 namespace boost {
Chris@16 41
Chris@16 42 /*!
Chris@16 43
Chris@16 44 \brief Simple Segregated Storage is the simplest, and probably the fastest,
Chris@16 45 memory allocation/deallocation algorithm. It is responsible for
Chris@16 46 partitioning a memory block into fixed-size chunks: where the block comes from
Chris@16 47 is determined by the client of the class.
Chris@16 48
Chris@16 49 \details Template class simple_segregated_storage controls access to a free list of memory chunks.
Chris@16 50 Please note that this is a very simple class, with preconditions on almost all its functions. It is intended to
Chris@16 51 be the fastest and smallest possible quick memory allocator - e.g., something to use in embedded systems.
Chris@16 52 This class delegates many difficult preconditions to the user (i.e., alignment issues).
Chris@16 53
Chris@16 54 An object of type simple_segregated_storage<SizeType> is empty if its free list is empty.
Chris@16 55 If it is not empty, then it is ordered if its free list is ordered. A free list is ordered if repeated calls
Chris@16 56 to <tt>malloc()</tt> will result in a constantly-increasing sequence of values, as determined by <tt>std::less<void *></tt>.
Chris@16 57 A member function is <i>order-preserving</i> if the free list maintains its order orientation (that is, an
Chris@16 58 ordered free list is still ordered after the member function call).
Chris@16 59
Chris@16 60 */
Chris@16 61 template <typename SizeType>
Chris@16 62 class simple_segregated_storage
Chris@16 63 {
Chris@16 64 public:
Chris@16 65 typedef SizeType size_type;
Chris@16 66
Chris@16 67 private:
Chris@16 68 simple_segregated_storage(const simple_segregated_storage &);
Chris@16 69 void operator=(const simple_segregated_storage &);
Chris@16 70
Chris@16 71 static void * try_malloc_n(void * & start, size_type n,
Chris@16 72 size_type partition_size);
Chris@16 73
Chris@16 74 protected:
Chris@16 75 void * first; /*!< This data member is the free list.
Chris@16 76 It points to the first chunk in the free list,
Chris@16 77 or is equal to 0 if the free list is empty.
Chris@16 78 */
Chris@16 79
Chris@16 80 void * find_prev(void * ptr);
Chris@16 81
Chris@16 82 // for the sake of code readability :)
Chris@16 83 static void * & nextof(void * const ptr)
Chris@16 84 { //! The return value is just *ptr cast to the appropriate type. ptr must not be 0. (For the sake of code readability :)
Chris@16 85 //! As an example, let us assume that we want to truncate the free list after the first chunk.
Chris@16 86 //! That is, we want to set *first to 0; this will result in a free list with only one entry.
Chris@16 87 //! The normal way to do this is to first cast first to a pointer to a pointer to void,
Chris@16 88 //! and then dereference and assign (*static_cast<void **>(first) = 0;).
Chris@16 89 //! This can be done more easily through the use of this convenience function (nextof(first) = 0;).
Chris@16 90 //! \returns dereferenced pointer.
Chris@16 91 return *(static_cast<void **>(ptr));
Chris@16 92 }
Chris@16 93
Chris@16 94 public:
Chris@16 95 // Post: empty()
Chris@16 96 simple_segregated_storage()
Chris@16 97 :first(0)
Chris@16 98 { //! Construct empty storage area.
Chris@16 99 //! \post empty()
Chris@16 100 }
Chris@16 101
Chris@16 102 static void * segregate(void * block,
Chris@16 103 size_type nsz, size_type npartition_sz,
Chris@16 104 void * end = 0);
Chris@16 105
Chris@16 106 // Same preconditions as 'segregate'
Chris@16 107 // Post: !empty()
Chris@16 108 void add_block(void * const block,
Chris@16 109 const size_type nsz, const size_type npartition_sz)
Chris@16 110 { //! Add block
Chris@16 111 //! Segregate this block and merge its free list into the
Chris@16 112 //! free list referred to by "first".
Chris@16 113 //! \pre Same as segregate.
Chris@16 114 //! \post !empty()
Chris@16 115 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 116 first = segregate(block, nsz, npartition_sz, first);
Chris@16 117 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 118 }
Chris@16 119
Chris@16 120 // Same preconditions as 'segregate'
Chris@16 121 // Post: !empty()
Chris@16 122 void add_ordered_block(void * const block,
Chris@16 123 const size_type nsz, const size_type npartition_sz)
Chris@16 124 { //! add block (ordered into list)
Chris@16 125 //! This (slower) version of add_block segregates the
Chris@16 126 //! block and merges its free list into our free list
Chris@16 127 //! in the proper order.
Chris@16 128 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 129 // Find where "block" would go in the free list
Chris@16 130 void * const loc = find_prev(block);
Chris@16 131
Chris@16 132 // Place either at beginning or in middle/end
Chris@16 133 if (loc == 0)
Chris@16 134 add_block(block, nsz, npartition_sz);
Chris@16 135 else
Chris@16 136 nextof(loc) = segregate(block, nsz, npartition_sz, nextof(loc));
Chris@16 137 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 138 }
Chris@16 139
Chris@16 140 // default destructor.
Chris@16 141
Chris@16 142 bool empty() const
Chris@16 143 { //! \returns true only if simple_segregated_storage is empty.
Chris@16 144 return (first == 0);
Chris@16 145 }
Chris@16 146
Chris@16 147 void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION()
Chris@16 148 { //! Create a chunk.
Chris@16 149 //! \pre !empty()
Chris@16 150 //! Increment the "first" pointer to point to the next chunk.
Chris@16 151 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 152 void * const ret = first;
Chris@16 153
Chris@16 154 // Increment the "first" pointer to point to the next chunk.
Chris@16 155 first = nextof(first);
Chris@16 156 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 157 return ret;
Chris@16 158 }
Chris@16 159
Chris@16 160 void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk)
Chris@16 161 { //! Free a chunk.
Chris@16 162 //! \pre chunk was previously returned from a malloc() referring to the same free list.
Chris@16 163 //! \post !empty()
Chris@16 164 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 165 nextof(chunk) = first;
Chris@16 166 first = chunk;
Chris@16 167 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 168 }
Chris@16 169
Chris@16 170 void ordered_free(void * const chunk)
Chris@16 171 { //! This (slower) implementation of 'free' places the memory
Chris@16 172 //! back in the list in its proper order.
Chris@16 173 //! \pre chunk was previously returned from a malloc() referring to the same free list
Chris@16 174 //! \post !empty().
Chris@16 175
Chris@16 176 // Find where "chunk" goes in the free list
Chris@16 177 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 178 void * const loc = find_prev(chunk);
Chris@16 179
Chris@16 180 // Place either at beginning or in middle/end.
Chris@16 181 if (loc == 0)
Chris@16 182 (free)(chunk);
Chris@16 183 else
Chris@16 184 {
Chris@16 185 nextof(chunk) = nextof(loc);
Chris@16 186 nextof(loc) = chunk;
Chris@16 187 }
Chris@16 188 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 189 }
Chris@16 190
Chris@16 191 void * malloc_n(size_type n, size_type partition_size);
Chris@16 192
Chris@16 193 //! \pre chunks was previously allocated from *this with the same
Chris@16 194 //! values for n and partition_size.
Chris@16 195 //! \post !empty()
Chris@16 196 //! \note If you're allocating/deallocating n a lot, you should
Chris@16 197 //! be using an ordered pool.
Chris@16 198 void free_n(void * const chunks, const size_type n,
Chris@16 199 const size_type partition_size)
Chris@16 200 {
Chris@16 201 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 202 if(n != 0)
Chris@16 203 add_block(chunks, n * partition_size, partition_size);
Chris@16 204 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 205 }
Chris@16 206
Chris@16 207 // pre: chunks was previously allocated from *this with the same
Chris@16 208 // values for n and partition_size.
Chris@16 209 // post: !empty()
Chris@16 210 void ordered_free_n(void * const chunks, const size_type n,
Chris@16 211 const size_type partition_size)
Chris@16 212 { //! Free n chunks from order list.
Chris@16 213 //! \pre chunks was previously allocated from *this with the same
Chris@16 214 //! values for n and partition_size.
Chris@16 215
Chris@16 216 //! \pre n should not be zero (n == 0 has no effect).
Chris@16 217 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 218 if(n != 0)
Chris@16 219 add_ordered_block(chunks, n * partition_size, partition_size);
Chris@16 220 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 221 }
Chris@16 222 #ifdef BOOST_POOL_VALIDATE
Chris@16 223 void validate()
Chris@16 224 {
Chris@16 225 int index = 0;
Chris@16 226 void* old = 0;
Chris@16 227 void* ptr = first;
Chris@16 228 while(ptr)
Chris@16 229 {
Chris@16 230 void* pt = nextof(ptr); // trigger possible segfault *before* we update variables
Chris@16 231 ++index;
Chris@16 232 old = ptr;
Chris@16 233 ptr = nextof(ptr);
Chris@16 234 }
Chris@16 235 }
Chris@16 236 #endif
Chris@16 237 };
Chris@16 238
Chris@16 239 //! Traverses the free list referred to by "first",
Chris@16 240 //! and returns the iterator previous to where
Chris@16 241 //! "ptr" would go if it was in the free list.
Chris@16 242 //! Returns 0 if "ptr" would go at the beginning
Chris@16 243 //! of the free list (i.e., before "first").
Chris@16 244
Chris@16 245 //! \note Note that this function finds the location previous to where ptr would go
Chris@16 246 //! if it was in the free list.
Chris@16 247 //! It does not find the entry in the free list before ptr
Chris@16 248 //! (unless ptr is already in the free list).
Chris@16 249 //! Specifically, find_prev(0) will return 0,
Chris@16 250 //! not the last entry in the free list.
Chris@16 251 //! \returns location previous to where ptr would go if it was in the free list.
Chris@16 252 template <typename SizeType>
Chris@16 253 void * simple_segregated_storage<SizeType>::find_prev(void * const ptr)
Chris@16 254 {
Chris@16 255 // Handle border case.
Chris@16 256 if (first == 0 || std::greater<void *>()(first, ptr))
Chris@16 257 return 0;
Chris@16 258
Chris@16 259 void * iter = first;
Chris@16 260 while (true)
Chris@16 261 {
Chris@16 262 // if we're about to hit the end, or if we've found where "ptr" goes.
Chris@16 263 if (nextof(iter) == 0 || std::greater<void *>()(nextof(iter), ptr))
Chris@16 264 return iter;
Chris@16 265
Chris@16 266 iter = nextof(iter);
Chris@16 267 }
Chris@16 268 }
Chris@16 269
Chris@16 270 //! Segregate block into chunks.
Chris@16 271 //! \pre npartition_sz >= sizeof(void *)
Chris@16 272 //! \pre npartition_sz = sizeof(void *) * i, for some integer i
Chris@16 273 //! \pre nsz >= npartition_sz
Chris@16 274 //! \pre Block is properly aligned for an array of object of
Chris@16 275 //! size npartition_sz and array of void *.
Chris@16 276 //! The requirements above guarantee that any pointer to a chunk
Chris@16 277 //! (which is a pointer to an element in an array of npartition_sz)
Chris@16 278 //! may be cast to void **.
Chris@16 279 template <typename SizeType>
Chris@16 280 void * simple_segregated_storage<SizeType>::segregate(
Chris@16 281 void * const block,
Chris@16 282 const size_type sz,
Chris@16 283 const size_type partition_sz,
Chris@16 284 void * const end)
Chris@16 285 {
Chris@16 286 // Get pointer to last valid chunk, preventing overflow on size calculations
Chris@16 287 // The division followed by the multiplication just makes sure that
Chris@16 288 // old == block + partition_sz * i, for some integer i, even if the
Chris@16 289 // block size (sz) is not a multiple of the partition size.
Chris@16 290 char * old = static_cast<char *>(block)
Chris@16 291 + ((sz - partition_sz) / partition_sz) * partition_sz;
Chris@16 292
Chris@16 293 // Set it to point to the end
Chris@16 294 nextof(old) = end;
Chris@16 295
Chris@16 296 // Handle border case where sz == partition_sz (i.e., we're handling an array
Chris@16 297 // of 1 element)
Chris@16 298 if (old == block)
Chris@16 299 return block;
Chris@16 300
Chris@16 301 // Iterate backwards, building a singly-linked list of pointers
Chris@16 302 for (char * iter = old - partition_sz; iter != block;
Chris@16 303 old = iter, iter -= partition_sz)
Chris@16 304 nextof(iter) = old;
Chris@16 305
Chris@16 306 // Point the first pointer, too
Chris@16 307 nextof(block) = old;
Chris@16 308
Chris@16 309 return block;
Chris@16 310 }
Chris@16 311
Chris@16 312 //! \pre (n > 0), (start != 0), (nextof(start) != 0)
Chris@16 313 //! \post (start != 0)
Chris@16 314 //! The function attempts to find n contiguous chunks
Chris@16 315 //! of size partition_size in the free list, starting at start.
Chris@16 316 //! If it succeds, it returns the last chunk in that contiguous
Chris@16 317 //! sequence, so that the sequence is known by [start, {retval}]
Chris@16 318 //! If it fails, it does do either because it's at the end of the
Chris@16 319 //! free list or hits a non-contiguous chunk. In either case,
Chris@16 320 //! it will return 0, and set start to the last considered
Chris@16 321 //! chunk. You are at the end of the free list if
Chris@16 322 //! nextof(start) == 0. Otherwise, start points to the last
Chris@16 323 //! chunk in the contiguous sequence, and nextof(start) points
Chris@16 324 //! to the first chunk in the next contiguous sequence (assuming
Chris@16 325 //! an ordered free list).
Chris@16 326 template <typename SizeType>
Chris@16 327 void * simple_segregated_storage<SizeType>::try_malloc_n(
Chris@16 328 void * & start, size_type n, const size_type partition_size)
Chris@16 329 {
Chris@16 330 void * iter = nextof(start);
Chris@16 331 while (--n != 0)
Chris@16 332 {
Chris@16 333 void * next = nextof(iter);
Chris@16 334 if (next != static_cast<char *>(iter) + partition_size)
Chris@16 335 {
Chris@16 336 // next == 0 (end-of-list) or non-contiguous chunk found
Chris@16 337 start = iter;
Chris@16 338 return 0;
Chris@16 339 }
Chris@16 340 iter = next;
Chris@16 341 }
Chris@16 342 return iter;
Chris@16 343 }
Chris@16 344
Chris@16 345 //! Attempts to find a contiguous sequence of n partition_sz-sized chunks. If found, removes them
Chris@16 346 //! all from the free list and returns a pointer to the first. If not found, returns 0. It is strongly
Chris@16 347 //! recommended (but not required) that the free list be ordered, as this algorithm will fail to find
Chris@16 348 //! a contiguous sequence unless it is contiguous in the free list as well. Order-preserving.
Chris@16 349 //! O(N) with respect to the size of the free list.
Chris@16 350 template <typename SizeType>
Chris@16 351 void * simple_segregated_storage<SizeType>::malloc_n(const size_type n,
Chris@16 352 const size_type partition_size)
Chris@16 353 {
Chris@16 354 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 355 if(n == 0)
Chris@16 356 return 0;
Chris@16 357 void * start = &first;
Chris@16 358 void * iter;
Chris@16 359 do
Chris@16 360 {
Chris@16 361 if (nextof(start) == 0)
Chris@16 362 return 0;
Chris@16 363 iter = try_malloc_n(start, n, partition_size);
Chris@16 364 } while (iter == 0);
Chris@16 365 void * const ret = nextof(start);
Chris@16 366 nextof(start) = nextof(iter);
Chris@16 367 BOOST_POOL_VALIDATE_INTERNALS
Chris@16 368 return ret;
Chris@16 369 }
Chris@16 370
Chris@16 371 } // namespace boost
Chris@16 372
Chris@16 373 #ifdef BOOST_MSVC
Chris@16 374 #pragma warning(pop)
Chris@16 375 #endif
Chris@16 376
Chris@16 377 #endif