diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2018-09-12 16:27:42 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-09-12 16:27:42 -0400 |
| commit | f60135cec62c91a9d7923397fe8796d2b3eaa5cb (patch) | |
| tree | 777646cb3611bf5809dc18e120e506117e143e11 /source/core/slang-memory-arena.cpp | |
| parent | 9a9733091cc7c9628e445313785d561deb229072 (diff) | |
Feature/memory arena (#631)
* First pass at MemoryArena.
* First pass at RandomGenerator.
* Extract TestContext into external source file.
* Fix warning on printf.
* Use enum classes for Test enums.
OutputMode -> TestOutputMode.
* First pass at FreeList unit test.
* Auto registering tests.
Improvements to RandomGenerator.
* Remove the need for unitTest headers - cos can use registering.
* Added unitTest for MemoryArena.
* Do unit tests.
* Fix typo.
Diffstat (limited to 'source/core/slang-memory-arena.cpp')
| -rw-r--r-- | source/core/slang-memory-arena.cpp | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/source/core/slang-memory-arena.cpp b/source/core/slang-memory-arena.cpp new file mode 100644 index 000000000..bdfd87602 --- /dev/null +++ b/source/core/slang-memory-arena.cpp @@ -0,0 +1,244 @@ + +#include "slang-memory-arena.h" + +namespace Slang { + +MemoryArena::MemoryArena() +{ + // Mark as invalid so any alloc call will fail + m_blockAlignment = 0; + m_blockSize = 0; + + // Set up as empty + m_blocks = nullptr; + _setCurrentBlock(nullptr); + m_blockFreeList.init(sizeof(Block), sizeof(void*), 16); +} + +MemoryArena::~MemoryArena() +{ + _deallocateBlocks(); +} + +MemoryArena::MemoryArena(size_t blockSize, size_t blockAlignment) +{ + _initialize(blockSize, blockAlignment); +} + +void MemoryArena::init(size_t blockSize, size_t blockAlignment) +{ + _deallocateBlocks(); + m_blockFreeList.reset(); + _initialize(blockSize, blockAlignment); +} + +void MemoryArena::_initialize(size_t blockSize, size_t alignment) +{ + // Alignment must be a power of 2 + assert(((alignment - 1) & alignment) == 0); + + // Must be at least sizeof(void*) in size, as that is the minimum the backing allocator will be + alignment = (alignment < kMinAlignment) ? kMinAlignment : alignment; + + // If alignment required is larger then the backing allocators then + // make larger to ensure when alignment correction takes place it will be aligned + if (alignment > kMinAlignment) + { + blockSize += alignment; + } + + m_blockSize = blockSize; + m_blockAlignment = alignment; + m_blocks = nullptr; + _setCurrentBlock(nullptr); + m_blockFreeList.init(sizeof(Block), sizeof(void*), 16); +} + +void* MemoryArena::_allocateAligned(size_t size, size_t alignment) +{ + assert(size); + // Can't be space in the current block -> so we can either place in next, or in a new block + _newCurrentBlock(size, alignment); + uint8_t* const current = m_current; + // If everything has gone to plan, must be space here... + assert(current + size <= m_end); + m_current = current + size; + return current; +} + +void MemoryArena::_deallocateBlocks() +{ + Block* currentBlock = m_blocks; + while (currentBlock) + { + // Deallocate the block + ::free(currentBlock->m_alloc); + // next block + currentBlock = currentBlock->m_next; + } + // Can deallocate all blocks to + m_blockFreeList.deallocateAll(); +} + +void MemoryArena::_setCurrentBlock(Block* block) +{ + if (block) + { + m_end = block->m_end; + m_start = block->m_start; + m_current = m_start; + } + else + { + m_start = nullptr; + m_end = nullptr; + m_current = nullptr; + } + m_currentBlock = block; +} + +MemoryArena::Block* MemoryArena::_newCurrentBlock(size_t size, size_t alignment) +{ + // Make sure init has been called (or has been set up in parameterized constructor) + assert(m_blockSize > 0); + // Alignment must be a power of 2 + assert(((alignment - 1) & alignment) == 0); + + // Alignment must at a minimum be block alignment (such if reused the constraints hold) + alignment = (alignment < m_blockAlignment) ? m_blockAlignment : alignment; + + const size_t alignMask = alignment - 1; + + // First try the next block (if there is one) + { + Block* next = m_currentBlock ? m_currentBlock->m_next : m_blocks; + if (next) + { + // Align could be done from the actual allocation start, but doing so would mean a pointer which + // didn't hit the constraint of being between start/end + // So have to align conservatively using start + uint8_t* memory = (uint8_t*)((size_t(next->m_start) + alignMask) & ~alignMask); + + // Check if can fit block in + if (memory + size <= next->m_end) + { + _setCurrentBlock(next); + return next; + } + } + } + + // The size of the block must be at least large enough to take into account alignment + size_t allocSize = (alignment <= kMinAlignment) ? size : (size + alignment); + + // The minimum block size should be at least m_blockSize + allocSize = (allocSize < m_blockSize) ? m_blockSize : allocSize; + + // Allocate block + Block* block = (Block*)m_blockFreeList.allocate(); + if (!block) + { + return nullptr; + } + // Allocate the memory + uint8_t* alloc = (uint8_t*)::malloc(allocSize); + if (!alloc) + { + m_blockFreeList.deallocate(block); + return nullptr; + } + // Do the alignment on the allocation + uint8_t* const start = (uint8_t*)((size_t(alloc) + alignMask) & ~alignMask); + + // Setup the block + block->m_alloc = alloc; + block->m_start = start; + block->m_end = alloc + allocSize; + block->m_next = nullptr; + + // Insert block into list + if (m_currentBlock) + { + // Insert after current block + block->m_next = m_currentBlock->m_next; + m_currentBlock->m_next = block; + } + else + { + // Add to start of the list of the blocks + block->m_next = m_blocks; + m_blocks = block; + } + _setCurrentBlock(block); + return block; +} + +MemoryArena::Block* MemoryArena::_findBlock(const void* alloc, Block* endBlock) const +{ + const uint8_t* ptr = (const uint8_t*)alloc; + + Block* block = m_blocks; + while (block != endBlock) + { + if (ptr >= block->m_start && ptr < block->m_end) + { + return block; + } + block = block->m_next; + } + return nullptr; +} + +MemoryArena::Block* MemoryArena::_findPreviousBlock(Block* block) +{ + Block* currentBlock = m_blocks; + while (currentBlock) + { + if (currentBlock->m_next == block) + { + return currentBlock; + } + currentBlock = currentBlock->m_next; + } + return nullptr; +} + +void MemoryArena::deallocateAll() +{ + Block** prev = &m_blocks; + Block* block = m_blocks; + + while (block) + { + if (size_t(block->m_end - block->m_alloc) > m_blockSize) + { + // Oversized block so we need to free it and remove from the list + Block* nextBlock = block->m_next; + *prev = nextBlock; + // Free the backing memory + ::free(block->m_alloc); + // Free the block + m_blockFreeList.deallocate(block); + // prev stays the same, now working on next tho + block = nextBlock; + } + else + { + // Onto next + prev = &block->m_next; + block = block->m_next; + } + } + + // Make the first current (if any) + _setCurrentBlock(m_blocks); +} + +void MemoryArena::reset() +{ + _deallocateBlocks(); + m_blocks = nullptr; + _setCurrentBlock(nullptr); +} + +} // namespace Slang |
