b2SlabAllocator.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /*
  2. * Copyright (c) 2014 Google, Inc.
  3. *
  4. * This software is provided 'as-is', without any express or implied
  5. * warranty. In no event will the authors be held liable for any damages
  6. * arising from the use of this software.
  7. * Permission is granted to anyone to use this software for any purpose,
  8. * including commercial applications, and to alter it and redistribute it
  9. * freely, subject to the following restrictions:
  10. * 1. The origin of this software must not be misrepresented; you must not
  11. * claim that you wrote the original software. If you use this software
  12. * in a product, an acknowledgment in the product documentation would be
  13. * appreciated but is not required.
  14. * 2. Altered source versions must be plainly marked as such, and must not be
  15. * misrepresented as being the original software.
  16. * 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #ifndef B2_SLAB_ALLOCATOR_H
  19. #define B2_SLAB_ALLOCATOR_H
  20. #include <stddef.h>
  21. #include <stdint.h>
  22. #include <new>
  23. #include <Box2D/Common/b2IntrusiveList.h>
  24. #include <Box2D/Common/b2FreeList.h>
  25. #include <Box2D/Common/b2Settings.h>
  26. #include <Box2D/Common/b2TrackedBlock.h>
  27. /// Freelist based allocator for fixed sized items from slabs (memory
  28. /// preallocated from the heap).
  29. /// T should be a class which has a default constructor and implements the
  30. /// member function "b2IntrusiveList* GetListNode()".
  31. /// All objects in a slab are constructed when a slab is created and destructed
  32. /// when a slab is freed.
  33. template<typename T>
  34. class b2SlabAllocator
  35. {
  36. private:
  37. // Information about a slab.
  38. class Slab
  39. {
  40. public:
  41. /// Initialize a slab with the number of items it contains.
  42. Slab(uint32 numberOfItems) :
  43. m_numberOfItems(numberOfItems)
  44. {
  45. B2_NOT_USED(m_padding);
  46. // This assumes that this class is packed on at least a 4-byte
  47. // boundary with no padding. Verify the assumption.
  48. b2Assert(sizeof(*this) == b2_mallocAlignment);
  49. }
  50. /// Empty destructor.
  51. ~Slab() { }
  52. /// Get the number of items in this slab.
  53. uint32 GetNumberOfItems() const { return m_numberOfItems; }
  54. /// Get a pointer to the first item in the slab.
  55. T* GetFirstItem() const
  56. {
  57. return (T*)((uint8*)(this + 1));
  58. }
  59. /// Get a pointer to the end of the slab.
  60. /// NOTE: This is a pointer after the last byte of the slab not the
  61. /// last item in the slab.
  62. T* GetItemEnd() const { return GetFirstItem() + GetNumberOfItems(); }
  63. private:
  64. /// Number of items in the slab.
  65. uint32 m_numberOfItems;
  66. /// Padding to align the first item in the slab to b2_mallocAlignment.
  67. uint8 m_padding[b2_mallocAlignment - sizeof(uint32)];
  68. };
  69. public:
  70. /// Initialize the allocator to allocate itemsPerSlab of type T for each
  71. /// slab that is allocated.
  72. b2SlabAllocator(const uint32 itemsPerSlab) :
  73. m_itemsPerSlab(itemsPerSlab)
  74. {
  75. }
  76. /// Free all allocated slabs.
  77. ~b2SlabAllocator()
  78. {
  79. FreeAllSlabs();
  80. }
  81. /// Set size of the next allocated slab using the number of items per
  82. /// slab. Setting this value to zero disables further slab allocation.
  83. void SetItemsPerSlab(uint32 itemsPerSlab)
  84. {
  85. m_itemsPerSlab = itemsPerSlab;
  86. }
  87. // Get the size of the next allocated slab.
  88. uint32 GetItemsPerSlab() const
  89. {
  90. return m_itemsPerSlab;
  91. }
  92. /// Allocate a item from the slab.
  93. T* Allocate()
  94. {
  95. // Allocate a slab if needed here.
  96. if (m_freeList.GetFreeList()->GetFreeList().IsEmpty() &&
  97. !AllocateSlab())
  98. return NULL;
  99. return m_freeList.Allocate();
  100. }
  101. /// Free an item from the slab.
  102. void Free(T *object)
  103. {
  104. m_freeList.Free(object);
  105. }
  106. /// Allocate a slab, construct instances of T and add them to the free
  107. /// pool.
  108. bool AllocateSlab()
  109. {
  110. if (!m_itemsPerSlab) return false;
  111. const uint32 slabSize = sizeof(Slab) + (sizeof(T) * m_itemsPerSlab);
  112. void* const memory = m_slabs.Allocate(slabSize);
  113. if (!memory) return false;
  114. Slab* const slab = new (BlockGetSlab(memory)) Slab(m_itemsPerSlab);
  115. T* item = slab->GetFirstItem();
  116. for (uint32 i = 0; i < m_itemsPerSlab; ++i, ++item)
  117. {
  118. m_freeList.AddToFreeList(new (item) T);
  119. }
  120. return true;
  121. }
  122. /// Free all slabs.
  123. void FreeAllSlabs()
  124. {
  125. const b2TypedIntrusiveListNode<b2TrackedBlock>& slabList =
  126. m_slabs.GetList();
  127. while (!slabList.IsEmpty())
  128. {
  129. FreeSlab(BlockGetSlab(slabList.GetNext()->GetMemory()));
  130. }
  131. }
  132. /// Free all empty slabs.
  133. /// This method is slow - O(M^N) - since this class doesn't track
  134. /// the association between each item and slab.
  135. void FreeEmptySlabs()
  136. {
  137. const b2IntrusiveListNode& freeItemList =
  138. m_freeList.GetFreeList()->GetFreeList();
  139. const b2IntrusiveListNode* freeItemListTerminator =
  140. freeItemList.GetTerminator();
  141. const b2TypedIntrusiveListNode<b2TrackedBlock>& slabList =
  142. m_slabs.GetList();
  143. const b2TypedIntrusiveListNode<b2TrackedBlock>* slabListTerminator =
  144. slabList.GetTerminator();
  145. b2TrackedBlock* block = slabList.GetNext();
  146. while (block != slabListTerminator)
  147. {
  148. // Get the Slab from the memory associated with the block.
  149. Slab* const slab = BlockGetSlab(block->GetMemory());
  150. block = block->GetNext();
  151. // Determine the range of memory the Slab owns.
  152. const uint8* const slabItemStart = (uint8*)slab->GetFirstItem();
  153. const uint8* const slabItemEnd = (uint8*)slab->GetItemEnd();
  154. // Count all free items that are owned by the current slab.
  155. uint8 freeItems = 0;
  156. bool empty = false;
  157. for (b2IntrusiveListNode* itemNode = freeItemList.GetNext();
  158. itemNode != freeItemListTerminator;
  159. itemNode = itemNode->GetNext())
  160. {
  161. const uint8* itemNodeAddress = (uint8*)itemNode;
  162. if (itemNodeAddress >= slabItemStart &&
  163. itemNodeAddress <= slabItemEnd)
  164. {
  165. ++freeItems;
  166. if (slab->GetNumberOfItems() == freeItems)
  167. {
  168. empty = true;
  169. break;
  170. }
  171. }
  172. }
  173. // If a slab is empty, free it.
  174. if (empty)
  175. {
  176. FreeSlab(slab);
  177. }
  178. }
  179. }
  180. /// Get the item allocator freelist.
  181. const b2TypedFreeList<T>& GetFreeList() const
  182. {
  183. return m_freeList;
  184. }
  185. private:
  186. /// Destroy all objects in a slab and free the slab.
  187. void FreeSlab(Slab * const slab)
  188. {
  189. b2Assert(slab);
  190. const uint32 numberOfItems = slab->GetNumberOfItems();
  191. T* item = slab->GetFirstItem();
  192. for (uint32 i = 0; i < numberOfItems; ++i, ++item)
  193. {
  194. item->~T();
  195. }
  196. slab->~Slab();
  197. m_slabs.Free(slab);
  198. }
  199. /// Get a pointer to a Slab from a block of memory in m_slabs.
  200. Slab* BlockGetSlab(void *memory)
  201. {
  202. return (Slab*)memory;
  203. }
  204. /// Get a pointer to the first item in the array of items referenced by a
  205. /// Slab.
  206. T* SlabGetFirstItem(Slab* slab)
  207. {
  208. return (T*)(slab + 1);
  209. }
  210. private:
  211. /// Contains a list of b2TrackedBlock instances where each b2TrackedBlock's
  212. /// associated user memory contains a Slab followed by instances of T.
  213. b2TrackedBlockAllocator m_slabs;
  214. /// Number of items to allocate in the next allocated slab.
  215. uint32 m_itemsPerSlab;
  216. /// Freelist which contains instances of T.
  217. b2TypedFreeList<T> m_freeList;
  218. };
  219. #endif // B2_SLAB_ALLOCATOR_H