#include #include #include #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; }