b2_block_allocator.cpp 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. // MIT License
  2. // Copyright (c) 2019 Erin Catto
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. // in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. // copies of the Software, and to permit persons to whom the Software is
  8. // furnished to do so, subject to the following conditions:
  9. // The above copyright notice and this permission notice shall be included in all
  10. // copies or substantial portions of the Software.
  11. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  17. // SOFTWARE.
  18. #include "box2d/b2_block_allocator.h"
  19. #include <limits.h>
  20. #include <string.h>
  21. #include <stddef.h>
  22. static const int32 b2_chunkSize = 16 * 1024;
  23. static const int32 b2_maxBlockSize = 640;
  24. static const int32 b2_chunkArrayIncrement = 128;
  25. // These are the supported object sizes. Actual allocations are rounded up the next size.
  26. static const int32 b2_blockSizes[b2_blockSizeCount] =
  27. {
  28. 16, // 0
  29. 32, // 1
  30. 64, // 2
  31. 96, // 3
  32. 128, // 4
  33. 160, // 5
  34. 192, // 6
  35. 224, // 7
  36. 256, // 8
  37. 320, // 9
  38. 384, // 10
  39. 448, // 11
  40. 512, // 12
  41. 640, // 13
  42. };
  43. // This maps an arbitrary allocation size to a suitable slot in b2_blockSizes.
  44. struct b2SizeMap
  45. {
  46. b2SizeMap()
  47. {
  48. int32 j = 0;
  49. values[0] = 0;
  50. for (int32 i = 1; i <= b2_maxBlockSize; ++i)
  51. {
  52. b2Assert(j < b2_blockSizeCount);
  53. if (i <= b2_blockSizes[j])
  54. {
  55. values[i] = (uint8)j;
  56. }
  57. else
  58. {
  59. ++j;
  60. values[i] = (uint8)j;
  61. }
  62. }
  63. }
  64. uint8 values[b2_maxBlockSize + 1];
  65. };
  66. static const b2SizeMap b2_sizeMap;
  67. struct b2Chunk
  68. {
  69. int32 blockSize;
  70. b2Block* blocks;
  71. };
  72. struct b2Block
  73. {
  74. b2Block* next;
  75. };
  76. b2BlockAllocator::b2BlockAllocator()
  77. {
  78. b2Assert(b2_blockSizeCount < UCHAR_MAX);
  79. m_chunkSpace = b2_chunkArrayIncrement;
  80. m_chunkCount = 0;
  81. m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
  82. memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
  83. memset(m_freeLists, 0, sizeof(m_freeLists));
  84. }
  85. b2BlockAllocator::~b2BlockAllocator()
  86. {
  87. for (int32 i = 0; i < m_chunkCount; ++i)
  88. {
  89. b2Free(m_chunks[i].blocks);
  90. }
  91. b2Free(m_chunks);
  92. }
  93. void* b2BlockAllocator::Allocate(int32 size)
  94. {
  95. if (size == 0)
  96. {
  97. return nullptr;
  98. }
  99. b2Assert(0 < size);
  100. if (size > b2_maxBlockSize)
  101. {
  102. return b2Alloc(size);
  103. }
  104. int32 index = b2_sizeMap.values[size];
  105. b2Assert(0 <= index && index < b2_blockSizeCount);
  106. if (m_freeLists[index])
  107. {
  108. b2Block* block = m_freeLists[index];
  109. m_freeLists[index] = block->next;
  110. return block;
  111. }
  112. else
  113. {
  114. if (m_chunkCount == m_chunkSpace)
  115. {
  116. b2Chunk* oldChunks = m_chunks;
  117. m_chunkSpace += b2_chunkArrayIncrement;
  118. m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
  119. memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk));
  120. memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk));
  121. b2Free(oldChunks);
  122. }
  123. b2Chunk* chunk = m_chunks + m_chunkCount;
  124. chunk->blocks = (b2Block*)b2Alloc(b2_chunkSize);
  125. #if defined(_DEBUG)
  126. memset(chunk->blocks, 0xcd, b2_chunkSize);
  127. #endif
  128. int32 blockSize = b2_blockSizes[index];
  129. chunk->blockSize = blockSize;
  130. int32 blockCount = b2_chunkSize / blockSize;
  131. b2Assert(blockCount * blockSize <= b2_chunkSize);
  132. for (int32 i = 0; i < blockCount - 1; ++i)
  133. {
  134. b2Block* block = (b2Block*)((int8*)chunk->blocks + blockSize * i);
  135. b2Block* next = (b2Block*)((int8*)chunk->blocks + blockSize * (i + 1));
  136. block->next = next;
  137. }
  138. b2Block* last = (b2Block*)((int8*)chunk->blocks + blockSize * (blockCount - 1));
  139. last->next = nullptr;
  140. m_freeLists[index] = chunk->blocks->next;
  141. ++m_chunkCount;
  142. return chunk->blocks;
  143. }
  144. }
  145. void b2BlockAllocator::Free(void* p, int32 size)
  146. {
  147. if (size == 0)
  148. {
  149. return;
  150. }
  151. b2Assert(0 < size);
  152. if (size > b2_maxBlockSize)
  153. {
  154. b2Free(p);
  155. return;
  156. }
  157. int32 index = b2_sizeMap.values[size];
  158. b2Assert(0 <= index && index < b2_blockSizeCount);
  159. #if defined(_DEBUG)
  160. // Verify the memory address and size is valid.
  161. int32 blockSize = b2_blockSizes[index];
  162. bool found = false;
  163. for (int32 i = 0; i < m_chunkCount; ++i)
  164. {
  165. b2Chunk* chunk = m_chunks + i;
  166. if (chunk->blockSize != blockSize)
  167. {
  168. b2Assert( (int8*)p + blockSize <= (int8*)chunk->blocks ||
  169. (int8*)chunk->blocks + b2_chunkSize <= (int8*)p);
  170. }
  171. else
  172. {
  173. if ((int8*)chunk->blocks <= (int8*)p && (int8*)p + blockSize <= (int8*)chunk->blocks + b2_chunkSize)
  174. {
  175. found = true;
  176. }
  177. }
  178. }
  179. b2Assert(found);
  180. memset(p, 0xfd, blockSize);
  181. #endif
  182. b2Block* block = (b2Block*)p;
  183. block->next = m_freeLists[index];
  184. m_freeLists[index] = block;
  185. }
  186. void b2BlockAllocator::Clear()
  187. {
  188. for (int32 i = 0; i < m_chunkCount; ++i)
  189. {
  190. b2Free(m_chunks[i].blocks);
  191. }
  192. m_chunkCount = 0;
  193. memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
  194. memset(m_freeLists, 0, sizeof(m_freeLists));
  195. }