simple_segregated_storage.hpp 13 KB

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