mirror of https://github.com/axmolengine/axmol.git
231 lines
5.2 KiB
C++
231 lines
5.2 KiB
C++
|
// MIT License
|
||
|
|
||
|
// Copyright (c) 2019 Erin Catto
|
||
|
|
||
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
// of this software and associated documentation files (the "Software"), to deal
|
||
|
// in the Software without restriction, including without limitation the rights
|
||
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
// copies of the Software, and to permit persons to whom the Software is
|
||
|
// furnished to do so, subject to the following conditions:
|
||
|
|
||
|
// The above copyright notice and this permission notice shall be included in all
|
||
|
// copies or substantial portions of the Software.
|
||
|
|
||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
// SOFTWARE.
|
||
|
|
||
|
#include "box2d/b2_block_allocator.h"
|
||
|
#include <limits.h>
|
||
|
#include <string.h>
|
||
|
#include <stddef.h>
|
||
|
|
||
|
static const int32 b2_chunkSize = 16 * 1024;
|
||
|
static const int32 b2_maxBlockSize = 640;
|
||
|
static const int32 b2_chunkArrayIncrement = 128;
|
||
|
|
||
|
// These are the supported object sizes. Actual allocations are rounded up the next size.
|
||
|
static const int32 b2_blockSizes[b2_blockSizeCount] =
|
||
|
{
|
||
|
16, // 0
|
||
|
32, // 1
|
||
|
64, // 2
|
||
|
96, // 3
|
||
|
128, // 4
|
||
|
160, // 5
|
||
|
192, // 6
|
||
|
224, // 7
|
||
|
256, // 8
|
||
|
320, // 9
|
||
|
384, // 10
|
||
|
448, // 11
|
||
|
512, // 12
|
||
|
640, // 13
|
||
|
};
|
||
|
|
||
|
// This maps an arbitrary allocation size to a suitable slot in b2_blockSizes.
|
||
|
struct b2SizeMap
|
||
|
{
|
||
|
b2SizeMap()
|
||
|
{
|
||
|
int32 j = 0;
|
||
|
values[0] = 0;
|
||
|
for (int32 i = 1; i <= b2_maxBlockSize; ++i)
|
||
|
{
|
||
|
b2Assert(j < b2_blockSizeCount);
|
||
|
if (i <= b2_blockSizes[j])
|
||
|
{
|
||
|
values[i] = (uint8)j;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
++j;
|
||
|
values[i] = (uint8)j;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
uint8 values[b2_maxBlockSize + 1];
|
||
|
};
|
||
|
|
||
|
static const b2SizeMap b2_sizeMap;
|
||
|
|
||
|
struct b2Chunk
|
||
|
{
|
||
|
int32 blockSize;
|
||
|
b2Block* blocks;
|
||
|
};
|
||
|
|
||
|
struct b2Block
|
||
|
{
|
||
|
b2Block* next;
|
||
|
};
|
||
|
|
||
|
b2BlockAllocator::b2BlockAllocator()
|
||
|
{
|
||
|
b2Assert(b2_blockSizeCount < UCHAR_MAX);
|
||
|
|
||
|
m_chunkSpace = b2_chunkArrayIncrement;
|
||
|
m_chunkCount = 0;
|
||
|
m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
|
||
|
|
||
|
memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
|
||
|
memset(m_freeLists, 0, sizeof(m_freeLists));
|
||
|
}
|
||
|
|
||
|
b2BlockAllocator::~b2BlockAllocator()
|
||
|
{
|
||
|
for (int32 i = 0; i < m_chunkCount; ++i)
|
||
|
{
|
||
|
b2Free(m_chunks[i].blocks);
|
||
|
}
|
||
|
|
||
|
b2Free(m_chunks);
|
||
|
}
|
||
|
|
||
|
void* b2BlockAllocator::Allocate(int32 size)
|
||
|
{
|
||
|
if (size == 0)
|
||
|
{
|
||
|
return nullptr;
|
||
|
}
|
||
|
|
||
|
b2Assert(0 < size);
|
||
|
|
||
|
if (size > b2_maxBlockSize)
|
||
|
{
|
||
|
return b2Alloc(size);
|
||
|
}
|
||
|
|
||
|
int32 index = b2_sizeMap.values[size];
|
||
|
b2Assert(0 <= index && index < b2_blockSizeCount);
|
||
|
|
||
|
if (m_freeLists[index])
|
||
|
{
|
||
|
b2Block* block = m_freeLists[index];
|
||
|
m_freeLists[index] = block->next;
|
||
|
return block;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (m_chunkCount == m_chunkSpace)
|
||
|
{
|
||
|
b2Chunk* oldChunks = m_chunks;
|
||
|
m_chunkSpace += b2_chunkArrayIncrement;
|
||
|
m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
|
||
|
memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk));
|
||
|
memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk));
|
||
|
b2Free(oldChunks);
|
||
|
}
|
||
|
|
||
|
b2Chunk* chunk = m_chunks + m_chunkCount;
|
||
|
chunk->blocks = (b2Block*)b2Alloc(b2_chunkSize);
|
||
|
#if defined(_DEBUG)
|
||
|
memset(chunk->blocks, 0xcd, b2_chunkSize);
|
||
|
#endif
|
||
|
int32 blockSize = b2_blockSizes[index];
|
||
|
chunk->blockSize = blockSize;
|
||
|
int32 blockCount = b2_chunkSize / blockSize;
|
||
|
b2Assert(blockCount * blockSize <= b2_chunkSize);
|
||
|
for (int32 i = 0; i < blockCount - 1; ++i)
|
||
|
{
|
||
|
b2Block* block = (b2Block*)((int8*)chunk->blocks + blockSize * i);
|
||
|
b2Block* next = (b2Block*)((int8*)chunk->blocks + blockSize * (i + 1));
|
||
|
block->next = next;
|
||
|
}
|
||
|
b2Block* last = (b2Block*)((int8*)chunk->blocks + blockSize * (blockCount - 1));
|
||
|
last->next = nullptr;
|
||
|
|
||
|
m_freeLists[index] = chunk->blocks->next;
|
||
|
++m_chunkCount;
|
||
|
|
||
|
return chunk->blocks;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void b2BlockAllocator::Free(void* p, int32 size)
|
||
|
{
|
||
|
if (size == 0)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
b2Assert(0 < size);
|
||
|
|
||
|
if (size > b2_maxBlockSize)
|
||
|
{
|
||
|
b2Free(p);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int32 index = b2_sizeMap.values[size];
|
||
|
b2Assert(0 <= index && index < b2_blockSizeCount);
|
||
|
|
||
|
#if defined(_DEBUG)
|
||
|
// Verify the memory address and size is valid.
|
||
|
int32 blockSize = b2_blockSizes[index];
|
||
|
bool found = false;
|
||
|
for (int32 i = 0; i < m_chunkCount; ++i)
|
||
|
{
|
||
|
b2Chunk* chunk = m_chunks + i;
|
||
|
if (chunk->blockSize != blockSize)
|
||
|
{
|
||
|
b2Assert( (int8*)p + blockSize <= (int8*)chunk->blocks ||
|
||
|
(int8*)chunk->blocks + b2_chunkSize <= (int8*)p);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((int8*)chunk->blocks <= (int8*)p && (int8*)p + blockSize <= (int8*)chunk->blocks + b2_chunkSize)
|
||
|
{
|
||
|
found = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
b2Assert(found);
|
||
|
|
||
|
memset(p, 0xfd, blockSize);
|
||
|
#endif
|
||
|
|
||
|
b2Block* block = (b2Block*)p;
|
||
|
block->next = m_freeLists[index];
|
||
|
m_freeLists[index] = block;
|
||
|
}
|
||
|
|
||
|
void b2BlockAllocator::Clear()
|
||
|
{
|
||
|
for (int32 i = 0; i < m_chunkCount; ++i)
|
||
|
{
|
||
|
b2Free(m_chunks[i].blocks);
|
||
|
}
|
||
|
|
||
|
m_chunkCount = 0;
|
||
|
memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
|
||
|
memset(m_freeLists, 0, sizeof(m_freeLists));
|
||
|
}
|