Files
libsst/ZUtil/ZAllocWindow.cpp
2026-04-03 00:22:39 -05:00

231 lines
5.5 KiB
C++

#include <ZUtil/ZAllocWindow.hpp>
#include <ZUtil/ZAssert.hpp>
#include <SST/SST_OS.h>
#define ALLOC_BITS (0xF00D) // Used to flag a section of memory as 'allocated'
#define DEALLOC_BITS (0xDEAD) // Used to flag a section of memory as 'deallocated'
#define LOOP_BITS (0xCAB0) // Used to flag that the next allocated section is back at the beginning of the buffer
/*************************************************************************/
ZAllocWindow::ZAllocWindow()
: Buffer(NULL), Begin(NULL), End(NULL),
BufferSize(0), bOwnsBuffer(true),
NumAllocs(0)
{
}
/*************************************************************************/
ZAllocWindow::ZAllocWindow( size_t _buffersize )
: Buffer(NULL), Begin(NULL), End(NULL),
BufferSize(_buffersize), bOwnsBuffer(true),
NumAllocs(0)
{
Resize(_buffersize);
}
/*************************************************************************/
ZAllocWindow::~ZAllocWindow()
{
if (bOwnsBuffer)
free(Buffer);
}
/*************************************************************************/
void* ZAllocWindow::Allocate( size_t _size )
{
// boundary is the location in memory that we cannot write past
uintptr_t boundary;
// determine where our boundary is
uintptr_t pool_begin = (uintptr_t)Buffer;
uintptr_t pool_end = pool_begin + BufferSize;
uintptr_t ptr_begin = (uintptr_t)Begin;
uintptr_t ptr_end = (uintptr_t)End;
boundary = (ptr_begin > ptr_end ? ptr_begin :
ptr_begin < ptr_end ? pool_end :
( NumAllocs > 0 ? ptr_end :
pool_end ) );
// align segment to word boundary
uintptr_t seg_begin = (uintptr_t)End;
uintptr_t seg_end = (uintptr_t)SST_OS_AlignPtr((void*)(seg_begin + 2*sizeof(uint32_t) + _size), sizeof(void*));
// check segment against boundary
if (seg_end > boundary) {
// if the segment has not just run up against the pool end, we are done
if (boundary != pool_end) {
return NULL;
}
// signal a loop (if less than 'end') and check again
if (seg_begin < pool_end) {
uint32_t* ptr_32 = (uint32_t*)seg_begin;
ptr_32[0] = (uint32_t)LOOP_BITS;
}
boundary = ptr_begin;
seg_begin = pool_begin;
seg_end = (uintptr_t)SST_OS_AlignPtr((void*)(seg_begin + 2 * sizeof(uint32_t) + _size), sizeof(void*));
}
// check segment against boundary again
if (seg_end <= boundary) {
NumAllocs++;
// set size as first element in block
uint32_t* ptr_32 = (uint32_t*)seg_begin;
ptr_32[0] = (uint32_t)ALLOC_BITS;
ptr_32[1] = (uint32_t)(seg_end - seg_begin);
// set new End pointer to ptr_end
End = (void*)seg_end;
// if End == BufferEnd, End == 0
if (End == (void*)((uintptr_t)Buffer + (uintptr_t)BufferSize))
End = Buffer;
// return the address of the first non-reserved element of the segment
return &ptr_32[2];
}
return NULL;
}
/*************************************************************************/
void ZAllocWindow::Deallocate( void* _data )
{
// check size before data
uint32_t* ptr_32 = (uint32_t*)((uintptr_t)_data - (2*sizeof(uint32_t)));
uint32_t flag = ptr_32[0];
uint32_t size = ptr_32[1];
// quick sanity check
ZASSERT(flag == ALLOC_BITS, "ZAllocWindow passed invalid pointer to deallocate!");
ZASSERT(size < BufferSize, "ZAllocWindow passed corrupted pointer to deallocate!");
// mark size slot as 'freed'
ptr_32[0] = (uint32_t)DEALLOC_BITS;
NumAllocs--;
// see if this was the oldest allocated block, and start rolling up
if (ptr_32 == Begin) {
while (WHILE_TRUE) {
ptr_32 = (uint32_t*)Begin;
// if begin is at or past end, loop to the beginning
if ((uintptr_t)ptr_32 >= ((uintptr_t)Begin + BufferSize)) {
Begin = Buffer;
continue;
}
// determine flag (size if needed)
flag = ptr_32[0];
// if this is a loop flag, start again at the beginning
if (flag == LOOP_BITS) {
Begin = Buffer;
continue;
}
// if this is a dealloc flag, dealloc the segment, increment Begin
if (flag == DEALLOC_BITS) {
size = ptr_32[1];
uintptr_t seg_start = (uintptr_t)ptr_32;
uintptr_t seg_end = seg_start + size;
Begin = (void*)seg_end;
#ifdef _DEBUG
ptr_32[1] = DEALLOC_BITS;
#endif
continue;
}
break;
}
}
}
/*************************************************************************/
void ZAllocWindow::Reset()
{
NumAllocs = 0;
Begin = Buffer;
End = Buffer;
}
/*************************************************************************/
bool ZAllocWindow::Resize( size_t _newsize )
{
// don't resize if we have allocations or don't own the buffer
if (NumAllocs > 0 || !bOwnsBuffer) {
return false;
}
// check newsize is zero to free everything
if (_newsize == 0) {
if (Buffer != NULL) {
free(Buffer);
Buffer = NULL;
}
return true;
}
// free current buffer if not NULL
if (Buffer != NULL) {
free(Buffer);
}
// allocate new buffer and return success / failure
Buffer = malloc(_newsize);
Begin = Buffer;
End = Buffer;
BufferSize = _newsize;
return Buffer != NULL;
}
/*************************************************************************/
bool ZAllocWindow::SetBuffer( void* _buffer, size_t _buffersize )
{
// don't accept buffer if we have allocations
if (NumAllocs > 0) {
return 0;
}
// if the new buffer is null, just reallocate to buffersize
if (_buffer == NULL) {
bOwnsBuffer = true;
return Resize(_buffersize);
}
// Get rid of the current buffer if we need to
if (Buffer != NULL) {
free(Buffer);
}
// lose ownership and set parameters, return success
bOwnsBuffer = false;
Buffer = _buffer;
Begin = Buffer;
End = Buffer;
BufferSize = _buffersize;
return true;
}