File indexing completed on 2025-06-01 03:50:40

0001 /*
0002 * Copyright (c) 2006-2009 Erin Catto http://www.gphysics.com
0003 *
0004 * This software is provided 'as-is', without any express or implied
0005 * warranty.  In no event will the authors be held liable for any damages
0006 * arising from the use of this software.
0007 * Permission is granted to anyone to use this software for any purpose,
0008 * including commercial applications, and to alter it and redistribute it
0009 * freely, subject to the following restrictions:
0010 * 1. The origin of this software must not be misrepresented; you must not
0011 * claim that you wrote the original software. If you use this software
0012 * in a product, an acknowledgment in the product documentation would be
0013 * appreciated but is not required.
0014 * 2. Altered source versions must be plainly marked as such, and must not be
0015 * misrepresented as being the original software.
0016 * 3. This notice may not be removed or altered from any source distribution.
0017 */
0018 
0019 #include <Box2D/Common/b2BlockAllocator.h>
0020 #include <cstdlib>
0021 #include <climits>
0022 #include <cstring>
0023 #include <memory>
0024 using namespace std;
0025 
0026 int32 b2BlockAllocator::s_blockSizes[b2_blockSizes] = 
0027 {
0028     16,     // 0
0029     32,     // 1
0030     64,     // 2
0031     96,     // 3
0032     128,    // 4
0033     160,    // 5
0034     192,    // 6
0035     224,    // 7
0036     256,    // 8
0037     320,    // 9
0038     384,    // 10
0039     448,    // 11
0040     512,    // 12
0041     640,    // 13
0042 };
0043 uint8 b2BlockAllocator::s_blockSizeLookup[b2_maxBlockSize + 1];
0044 bool b2BlockAllocator::s_blockSizeLookupInitialized;
0045 
0046 struct b2Chunk
0047 {
0048     int32 blockSize;
0049     b2Block* blocks;
0050 };
0051 
0052 struct b2Block
0053 {
0054     b2Block* next;
0055 };
0056 
0057 b2BlockAllocator::b2BlockAllocator()
0058 {
0059     b2Assert(b2_blockSizes < UCHAR_MAX);
0060 
0061     m_chunkSpace = b2_chunkArrayIncrement;
0062     m_chunkCount = 0;
0063     m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
0064     
0065     memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
0066     memset(m_freeLists, 0, sizeof(m_freeLists));
0067 
0068     if (s_blockSizeLookupInitialized == false)
0069     {
0070         int32 j = 0;
0071         for (int32 i = 1; i <= b2_maxBlockSize; ++i)
0072         {
0073             b2Assert(j < b2_blockSizes);
0074             if (i <= s_blockSizes[j])
0075             {
0076                 s_blockSizeLookup[i] = (uint8)j;
0077             }
0078             else
0079             {
0080                 ++j;
0081                 s_blockSizeLookup[i] = (uint8)j;
0082             }
0083         }
0084 
0085         s_blockSizeLookupInitialized = true;
0086     }
0087 }
0088 
0089 b2BlockAllocator::~b2BlockAllocator()
0090 {
0091     for (int32 i = 0; i < m_chunkCount; ++i)
0092     {
0093         b2Free(m_chunks[i].blocks);
0094     }
0095 
0096     b2Free(m_chunks);
0097 }
0098 
0099 void* b2BlockAllocator::Allocate(int32 size)
0100 {
0101     if (size == 0)
0102         return NULL;
0103 
0104     b2Assert(0 < size);
0105 
0106     if (size > b2_maxBlockSize)
0107     {
0108         return b2Alloc(size);
0109     }
0110 
0111     int32 index = s_blockSizeLookup[size];
0112     b2Assert(0 <= index && index < b2_blockSizes);
0113 
0114     if (m_freeLists[index])
0115     {
0116         b2Block* block = m_freeLists[index];
0117         m_freeLists[index] = block->next;
0118         return block;
0119     }
0120     else
0121     {
0122         if (m_chunkCount == m_chunkSpace)
0123         {
0124             b2Chunk* oldChunks = m_chunks;
0125             m_chunkSpace += b2_chunkArrayIncrement;
0126             m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
0127             memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk));
0128             memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk));
0129             b2Free(oldChunks);
0130         }
0131 
0132         b2Chunk* chunk = m_chunks + m_chunkCount;
0133         chunk->blocks = (b2Block*)b2Alloc(b2_chunkSize);
0134 #if defined(_DEBUG)
0135         memset(chunk->blocks, 0xcd, b2_chunkSize);
0136 #endif
0137         int32 blockSize = s_blockSizes[index];
0138         chunk->blockSize = blockSize;
0139         int32 blockCount = b2_chunkSize / blockSize;
0140         b2Assert(blockCount * blockSize <= b2_chunkSize);
0141         for (int32 i = 0; i < blockCount - 1; ++i)
0142         {
0143             b2Block* block = (b2Block*)((int8*)chunk->blocks + blockSize * i);
0144             b2Block* next = (b2Block*)((int8*)chunk->blocks + blockSize * (i + 1));
0145             block->next = next;
0146         }
0147         b2Block* last = (b2Block*)((int8*)chunk->blocks + blockSize * (blockCount - 1));
0148         last->next = NULL;
0149 
0150         m_freeLists[index] = chunk->blocks->next;
0151         ++m_chunkCount;
0152 
0153         return chunk->blocks;
0154     }
0155 }
0156 
0157 void b2BlockAllocator::Free(void* p, int32 size)
0158 {
0159     if (size == 0)
0160     {
0161         return;
0162     }
0163 
0164     b2Assert(0 < size);
0165 
0166     if (size > b2_maxBlockSize)
0167     {
0168         b2Free(p);
0169         return;
0170     }
0171 
0172     int32 index = s_blockSizeLookup[size];
0173     b2Assert(0 <= index && index < b2_blockSizes);
0174 
0175 #ifdef _DEBUG
0176     // Verify the memory address and size is valid.
0177     int32 blockSize = s_blockSizes[index];
0178     bool found = false;
0179     for (int32 i = 0; i < m_chunkCount; ++i)
0180     {
0181         b2Chunk* chunk = m_chunks + i;
0182         if (chunk->blockSize != blockSize)
0183         {
0184             b2Assert(   (int8*)p + blockSize <= (int8*)chunk->blocks ||
0185                         (int8*)chunk->blocks + b2_chunkSize <= (int8*)p);
0186         }
0187         else
0188         {
0189             if ((int8*)chunk->blocks <= (int8*)p && (int8*)p + blockSize <= (int8*)chunk->blocks + b2_chunkSize)
0190             {
0191                 found = true;
0192             }
0193         }
0194     }
0195 
0196     b2Assert(found);
0197 
0198     memset(p, 0xfd, blockSize);
0199 #endif
0200 
0201     b2Block* block = (b2Block*)p;
0202     block->next = m_freeLists[index];
0203     m_freeLists[index] = block;
0204 }
0205 
0206 void b2BlockAllocator::Clear()
0207 {
0208     for (int32 i = 0; i < m_chunkCount; ++i)
0209     {
0210         b2Free(m_chunks[i].blocks);
0211     }
0212 
0213     m_chunkCount = 0;
0214     memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
0215 
0216     memset(m_freeLists, 0, sizeof(m_freeLists));
0217 }