#include "munga.h" #pragma hdrstop #include "memblock.h" #include "scalar.h" //#define MEMORY_VERIFY //~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MemoryBlockHeader ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #if defined(USE_SIGNATURE) int Is_Signature_Bad(const volatile MemoryBlockHeader *) { return False; } #endif // //############################################################################# //############################################################################# // Logical MemoryBlockHeader::TestInstance() { return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MemoryBlockBase ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MemoryBlockBase* MemoryBlockBase::firstBlock = NULL; // //############################################################################# // // This function constructs a MemoryBlockBase variable, allocating a block of // blockMemory from heap, preparing it for suballocation of a constant record // blockSize. // // NOTE: Record blockSize must be large enough to contain a void pointer in order // to properly set up the re-use chain! // // Rec_Size - blockSize in bytes of the suballocation unit // Start - number of records to allocate initially // Delta - number of records to allocate when growing the blockMemory block // //############################################################################# // MemoryBlockBase::MemoryBlockBase( size_t rec_size, size_t start, size_t delta, const char* name ) { // //---------------------------------------------------------------------- // Make sure that the requested record blockSize is large enough if debugging // is enabled //---------------------------------------------------------------------- // Verify(rec_size >= sizeof(MemoryBlockHeader)); // //------------------------------------------------------------------------ // Set up the blockSize variables for the blockMemory block, and figure out the byte // sizes of the initial block and delta blocks //------------------------------------------------------------------------ // recordSize = rec_size; blockSize = start * recordSize; deltaSize = delta * recordSize; blockName = name; // //------------------------------------------------------------------------- // Allocate a block big enough for the requested blockMemory plus a link to // the next blockMemory block, initializing this link to NULL //------------------------------------------------------------------------- // #if !defined(MEMORY_VERIFY) blockMemory = (MemoryBlockHeader*)new Byte[sizeof(MemoryBlockHeader) + blockSize]; Verify(blockMemory); blockMemory->nextBlock = NULL; blockMemory->blockSize = blockSize; // //---------------------------------------------------------------------- // Establish the beginning of the firstHeaderRecord record block, and point // the freeRecord record pointer to the beginning. There are no deletedRecord // records yet, so make sure Deleted is NULL //---------------------------------------------------------------------- // firstHeaderRecord = (Byte*)(blockMemory + 1); freeRecord = firstHeaderRecord; deletedRecord = NULL; #endif // //-------------------------------------------------------------------------- // If this is the first memory block, set the first block pointer and // initialize the ring. If not, insert the new block at the end of the ring //-------------------------------------------------------------------------- // if (!firstBlock) { firstBlock = nextBlock = previousBlock = this; } else { MemoryBlockBase *block = firstBlock; Check(block); block = block->previousBlock; Check(block); nextBlock = firstBlock; previousBlock = block; block->nextBlock = this; firstBlock->previousBlock = this; } } // //############################################################################# // // This function destroys the MemoryBlockBase object, deleting any additional // record blocks which were allocated // //############################################################################# // MemoryBlockBase::~MemoryBlockBase() { #if !defined(MEMORY_VERIFY) // //------------------------------------------------------------------------ // Find the address of the first record block, then delete blocks until we // come to the end of the chain //------------------------------------------------------------------------ // Check(this); MemoryBlockHeader *block = blockMemory; while (block) { // //-------------------------------------------------------------------- // Save the address of the next blockMemory block, then delete this one and // get ready to delete the next one //-------------------------------------------------------------------- // Check(block); MemoryBlockHeader *next_block = block->nextBlock; delete block; block = next_block; } #endif // //------------------------------------------------------------------------ // Remove the block from the ring. If the block is the first one, set the // first block pointer correctly. Note that we memoryblocks, as static // objects, should deconstruct in the opposite order they constructed, so // when we reach the first block, it should be the only one in the queue. //------------------------------------------------------------------------ // if (firstBlock == this) { if (nextBlock == this) { firstBlock = NULL; } else { firstBlock = nextBlock; goto Unlink; } } else { Unlink: MemoryBlockBase *next = nextBlock; Check(next); MemoryBlockBase *prev = previousBlock; Check(prev); next->previousBlock = prev; prev->nextBlock = next; } } // //############################################################################# // // This function allocates a fixed blockSize record from the record blocks // //############################################################################# // void* MemoryBlockBase::Grow() { #if defined(MEMORY_VERIFY) Fail("MemoryBlockBase::Grow() not available!\n"); #endif // //--------------------------------------------------------------------- // If we have freeRecord space left in the firstHeaderRecord record block, allocate the // new record from here, updating the next freeRecord record pointer //--------------------------------------------------------------------- // Check(this); if (freeRecord - firstHeaderRecord <= blockSize - recordSize) { void *result = freeRecord; freeRecord += recordSize; return result; } // //----------------------------------------------------------------------- // Allocate a new block of records using the growth blockSize, and make the // link field of the firstHeaderRecord block point to the new block. Then make the // new block the firstHeaderRecord block, and make its next link NULL, as it is the // end of the chain //----------------------------------------------------------------------- // blockSize = deltaSize; firstHeaderRecord -= sizeof(MemoryBlockHeader); Byte *new_block = new Byte[blockSize + sizeof(MemoryBlockHeader)]; Verify(new_block); MemoryBlockHeader *header = (MemoryBlockHeader*)firstHeaderRecord; header->nextBlock = (MemoryBlockHeader*)new_block; firstHeaderRecord = new_block; header = (MemoryBlockHeader*)firstHeaderRecord; header->nextBlock = NULL; header->blockSize = deltaSize; // //--------------------------------------------------------------------- // Make firstHeaderRecord point to the first available address in the new block // (having skipped the first field allocated to the link pointer), and // allocate this first block to the caller. Update the freeRecord pointer to // reflect this allocation //--------------------------------------------------------------------- // firstHeaderRecord += sizeof(MemoryBlockHeader); freeRecord = firstHeaderRecord + recordSize; return (void*)firstHeaderRecord; } // //############################################################################# //############################################################################# // void MemoryBlockBase::UsageReport() { #if !defined(MEMORY_VERIFY) DEBUG_STREAM << "#Bytes #Rcds #Dels #Free Use% Name\n" << std::flush; DEBUG_STREAM << "------ ----- ----- ----- ---- -------------------------------\n" << std::flush; MemoryBlockBase *block = firstBlock; do { Check(block); MemoryBlockHeader *header = block->blockMemory; size_t byte_count = 0; size_t record_count = 0; size_t deletion_count = 0; size_t unused_count = block->blockSize - (block->freeRecord - block->firstHeaderRecord); unused_count /= block->recordSize; while (header) { Check(header); record_count += header->blockSize / block->recordSize; byte_count += header->blockSize + sizeof(MemoryBlockHeader); header = header->nextBlock; } Byte *deletion = block->deletedRecord; while (deletion) { ++deletion_count; deletion = *(Byte**)deletion; } Scalar usage = 1.0f - (deletion_count+unused_count)/(float)record_count; record_count -= deletion_count + unused_count; DEBUG_STREAM << std::setw(6) << byte_count << std::setw(6) << record_count << std::setw(6) << deletion_count << std::setw(6) << unused_count << std::setw(4) << ((int)(usage*100.0f)) << '%'; if (block->blockName) { DEBUG_STREAM << ' ' << block->blockName << std::flush; } DEBUG_STREAM << std::endl << std::flush; block = block->nextBlock; } while (block != firstBlock); DEBUG_STREAM << std::endl << std::flush; #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MemoryBlock ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //############################################################################# // // This function allocates a fixed blockSize record from the record blocks // //############################################################################# // void* MemoryBlock::New() { void *result; #if defined(MEMORY_VERIFY) result = new char[recordSize]; #else // //---------------------------------------------------------------------- // If we have a deletedRecord record, go ahead and reuse it, updating the next // deletedRecord record value from the firstHeaderRecord one. This chain is independant // of the firstHeaderRecord record block. If not, grow the blockMemory block by one // record //---------------------------------------------------------------------- // Check(this); if (deletedRecord) { result = (void*)deletedRecord; deletedRecord = *(Byte**)deletedRecord; } else { result = Grow(); } #endif // //---------------------------------------------------------------------- // If we are checking for unassigned variables, initialize the allocated // memory with NANs //---------------------------------------------------------------------- // #if defined(DEBUG_NEW_ON) #define SNAN_POSITIVE_LONG 0x7fb07fb0 long *filler = (long *)result; for (int i = recordSize >> 2; i; --i) { *filler++ = SNAN_POSITIVE_LONG; } #endif // //------------------------------------- // Return the address of the new record //------------------------------------- // return result; } // //############################################################################# // // This function deallocates a record, making it available for re-use // //############################################################################# // void MemoryBlock::Delete( void* where ) { #if defined(MEMORY_VERIFY) delete where; #else // //-------------------------------------------------------------------- // If we are in debug2 mode, check to see if the deletedRecord region really // belongs to us //-------------------------------------------------------------------- // Check(this); #if DEBUG_LEVEL>1 // //-------------------------------------------------------------------- // Make sure the address of this record is not already in the deletion // chain //-------------------------------------------------------------------- // void *record; for ( record = (void*)deletedRecord; record; record = *(Byte**)record ) { if (record == where) { break; } } Verify(!record); // //------------------------------------------------------------------ // Find the address of the first record block, then check each block // until we come to the end of the chain //------------------------------------------------------------------ // unsigned offset; MemoryBlockHeader* block = blockMemory; while (block) { // //--------------------------------------------------------------- // If the record is in this block and is positioned correctly, go // ahead and break as we have found a legal place within a block //--------------------------------------------------------------- // Check(block); offset = (unsigned)((Byte*)where - (Byte*)(block + 1)); if (offset < block->blockSize) { Verify(!(offset % recordSize)); break; } // //--------------------- // Go to the next block //--------------------- // block = block->nextBlock; } Verify(block); // //----------------------------------------------------------- // Now make sure that the address is not in our future region //----------------------------------------------------------- // Verify(block->nextBlock || offset < freeRecord-firstHeaderRecord); #endif // //---------------------------------------------------------------------- // Make the first few bytes of the record act as the link pointer to the // beginning of the firstHeaderRecord deletedRecord chain, then make the first record // available for reuse this one //---------------------------------------------------------------------- // *(Byte**)where = deletedRecord; deletedRecord = (Byte*)where; #endif } // //############################################################################# //############################################################################# // void* MemoryBlock::operator[](size_t index) { #if defined(MEMORY_VERIFY) Fail("MemoryBlock::operator[] not available!\n"); #endif // //------------------------------------------------------------------ // Find the address of the first record block, then check each block // until we come to the end of the chain //------------------------------------------------------------------ // Check(this); MemoryBlockHeader *block = blockMemory; while (block) { // //--------------------------------------------------------------- // If the index is in this block, go ahead and return its address //--------------------------------------------------------------- // Check(block); Verify(recordSize); int records = block->blockSize / recordSize; if (index < records) { return (Byte*)(block + 1) + index * recordSize; } // //-------------------------------------------------------------------- // Save the address of the next blockMemory block, then delete this one and // get ready to delete the next one //-------------------------------------------------------------------- // index -= records; block = block->nextBlock; } // //----------------------------------------- // The record doesn't exist, so return NULL //----------------------------------------- // return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ MemoryStack ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //############################################################################# //############################################################################# // void* MemoryStack::Push(const void* what) { Check(this); Check_Pointer(what); Byte *block = firstHeaderRecord; topOfStack = freeRecord; Mem_Copy(Grow(), what, recordSize, blockSize - (freeRecord - firstHeaderRecord)); if (firstHeaderRecord != block) { topOfStack = firstHeaderRecord; } return topOfStack; } // //############################################################################# //############################################################################# // void* MemoryStack::Push() { Check(this); Byte *block = firstHeaderRecord; topOfStack = freeRecord; Grow(); if (firstHeaderRecord != block) { topOfStack = firstHeaderRecord; } return topOfStack; } // //########################################################################### //########################################################################### // void MemoryStack::Pop() { MemoryBlockHeader *block, *new_block; // //----------------------------------------- // Make sure that something is in the stack //----------------------------------------- // Check(this); if (topOfStack) { // //-------------------------------------------------------------------- // If the topOfStack of the stack is not at the bottom of a blockMemory block, the // freeRecord and topOfStack pointers can move normally //-------------------------------------------------------------------- // if (topOfStack != firstHeaderRecord) { // //------------------------------------------------------------------- // If the freeRecord pointer is at the bottom of a block, we have to delete // the block and update the variables to the previous block //------------------------------------------------------------------- // if (freeRecord == firstHeaderRecord) { new_block = (MemoryBlockHeader*)firstHeaderRecord - 1; Check(new_block); for ( block = blockMemory; block->nextBlock != new_block; block = block->nextBlock ) { Check(block); } delete new_block; block->nextBlock = NULL; firstHeaderRecord = (Byte*)(block + 1); blockSize = block->blockSize; } // //----------------------------------------------- // Move the topOfStack and freeRecord pointers back one record //----------------------------------------------- // freeRecord = topOfStack; topOfStack -= recordSize; } // //-------------------------------------------------------------------- // Otherwise, we have to wrap the top of the stack pointer back to the // previous block. If there is only one allocated block, then the // stack is empty //-------------------------------------------------------------------- // else if (!blockMemory->nextBlock) { topOfStack = NULL; } // //-------------------------------------------------------------------- // Point the top of the stack to the last record of the previous block //-------------------------------------------------------------------- // else { new_block = (MemoryBlockHeader*)firstHeaderRecord - 1; Check(new_block); for ( block = blockMemory; block->nextBlock != new_block; block = block->nextBlock ) { Check(block); } topOfStack = (Byte*)(block + 1) + block->blockSize - recordSize; } } } #if defined(TEST_CLASS) # include "memblock.tcp" #endif