ObjectAllocator
Data Structures: Memory Management
A basic memory manager that manages the allocation / deallocation of fixed sizes of memory based on user input.
With the interfaced provided to us, this assignment was focused on translating our understanding of how memory is managed in a theoretical sense into C++ syntax. I have included the interface at the bottom of the page for cross-reference.
This was the first major assignment in the Data Structures class that is known to be a degree-killer. In fact, with how the class is structured, most people decided to skip this assignment and do more assignments later on with how difficult it is. Overall, I enjoyed the experience and found that I learned quite a bit about how memory actually functions through this project.
The most interesting thing to me had to be the existance / use of headers. It is such an elegant solution, looking back, that I wish I had known about sooner!
This memory manager was run through multiple unit tests to test the padding, header management, and overall process. It was also put until multiple random stress tests to see if it was acting like a proper middle-man between the user and the memory manager of C++.
/*****************************************************************//** * \file ObjectAllocator.cpp * \author(s) Jennifer Assid * * \date 1/21/2022 *********************************************************************/ #include "ObjectAllocator.h" #include <string.h> void ObjectAllocator::put_on_freelist(void* Object) { /* Check to make sure the object is valid */ if (Object == NULL) { /* If not - throw exception and return out of function */ throw OAException(OAException::E_NO_MEMORY, "Object was NULL"); return; } /* Cast the object to a GenericObject */ GenericObject* object = reinterpret_cast<GenericObject*>(Object); /* Add to the FreeList and correct the FreeList pointer */ object->Next = FreeList_; FreeList_ = object; /* Update the statistics */ Stats_->FreeObjects_++; } void ObjectAllocator::allocate_new_page(void) { /* Check if max pages has been reached */ if (Stats_->PagesInUse_ == Config_->MaxPages_) { /* If not, throw an exception and return out of function */ throw OAException(OAException::E_NO_MEMORY, "Max amount of pages has been reached."); return; } /* Initialize the page variable */ unsigned char* new_page = NULL; /* Allocate a new page given the information in config_ and stats_ */ try { new_page = new unsigned char[Stats_->PageSize_] {0}; } catch (const std::bad_alloc& e) { throw OAException(OAException::E_NO_MEMORY, "Allocation of new page failed\n"); return; } /* Cast the new_page to a GenericObject */ GenericObject* new_page_object = reinterpret_cast<GenericObject*>(new_page); /* Add to the PageList and correct that PageList pointer */ new_page_object->Next = PageList_; PageList_ = new_page_object; /* Update the statistics */ Stats_->PagesInUse_++; /* Set the left alignment bits to their necessary values */ if (Config_->DebugOn_ && Config_->LeftAlignSize_ != 0) { memset(new_page + sizeof(void*), ALIGN_PATTERN, sizeof(unsigned char) * Config_->LeftAlignSize_); } /* Add the objects in the new page to the FreeList_ */ for (unsigned int i = 0; i < (Config_->ObjectsPerPage_); i++) { /* Get the modifiable pointer at the beginning of the current object */ unsigned char* modify = new_page + PtrSize_ + ObjSize_ * i; /* Get the pointers to the surrounding padding */ unsigned char* left_padding = modify - Config_->PadBytes_; unsigned char* right_padding = modify + Stats_->ObjectSize_; /* If Debug is on */ if (Config_->DebugOn_) { /* Set through the object size and set the bytes to AA */ memset(modify, UNALLOCATED_PATTERN, sizeof(unsigned char) * Stats_->ObjectSize_); /* Set the padding to its necessary information */ memset(left_padding, PAD_PATTERN, sizeof(unsigned char) * Config_->PadBytes_); memset(right_padding, PAD_PATTERN, sizeof(unsigned char) * Config_->PadBytes_); /* Set the inter alignment as necessary */ if (i != Config_->ObjectsPerPage_ - 1) { memset(right_padding + Config_->PadBytes_, ALIGN_PATTERN, sizeof(unsigned char) * Config_->InterAlignSize_); } } /* Cast the object so it can be placed on the freelist */ GenericObject* object = reinterpret_cast<GenericObject*>(modify); /* Place the object on the free list */ put_on_freelist(object); } } ObjectAllocator::ObjectAllocator(size_t ObjectSize, const OAConfig& config) : PageList_(NULL), FreeList_(NULL) { /* Set the private configuration to the given configuration */ try { Config_ = new OAConfig(config.UseCPPMemManager_, config.ObjectsPerPage_, config.MaxPages_, config.DebugOn_, config.PadBytes_, config.HBlockInfo_, config.Alignment_); } catch (const std::bad_alloc& e) { throw OAException(OAException::E_NO_MEMORY, "Allocation of Config_ failed\n"); return; } /* Calculate the size of the pointer section (beginning of the page - before the first object) */ PtrSize_ = sizeof(void*) + Config_->PadBytes_ + Config_->HBlockInfo_.size_; /* If there is alignment and the calculated memory size doesn't fall on the specified boundary */ if (Config_->Alignment_ != 0 && PtrSize_ % Config_->Alignment_ != 0) { /* If the calculated memory block is larger than the alignment - subtract the alignment by the remainder and add the result of the size of the block */ /* Else - subtract the memory size from alignment and add the difference to the memory size */ Config_->LeftAlignSize_ = static_cast<unsigned int>((PtrSize_ > Config_->Alignment_) ? Config_->Alignment_ - (PtrSize_ % Config_->Alignment_) : Config_->Alignment_ - PtrSize_); PtrSize_ += Config_->LeftAlignSize_; } /* Calculate the size of the objects (with associated padding and headers) */ ObjSize_ = ObjectSize + (Config_->PadBytes_ * 2) + Config_->HBlockInfo_.size_; /* If there is alignment and the calculated memory size doesn't fall on the specified boundary */ if (Config_->Alignment_ != 0 && ObjSize_ % Config_->Alignment_ != 0) { /* If the memory block is larger than alignment - subtract the alignment by the remainder and add to the memory size */ /* Else - subtract the memory size form the alignment and add the difference to the memory size */ Config_->InterAlignSize_ = static_cast<unsigned int>((ObjSize_ > Config_->Alignment_) ? Config_->Alignment_ - (ObjSize_ % Config_->Alignment_) : Config_->Alignment_ - ObjSize_); ObjSize_ += Config_->InterAlignSize_; } /* Calculate the size of the page based off of the previous calcuations */ size_t page_size = PtrSize_ + ((Config_->ObjectsPerPage_ - 1) * ObjSize_) + ObjectSize + Config_->PadBytes_; /* Attempt to allocate the statistics module */ try { Stats_ = new OAStats(); } catch (const std::bad_alloc& e) { throw OAException(OAException::E_NO_MEMORY, "Allocation of Stats_ failed\n"); return; } /* Set the private statistics to the needed values */ Stats_->ObjectSize_ = ObjectSize; Stats_->PageSize_ = page_size; /* Allocate the page */ allocate_new_page(); } ObjectAllocator::~ObjectAllocator() { /* Go through and make sure that all memory is freed properly */ delete Stats_; delete Config_; /* Free all of the memory associated with the pages */ while (PageList_) { GenericObject* temp = PageList_; PageList_ = PageList_->Next; delete [] temp; } } void* ObjectAllocator::Allocate(const char* label) { /* If the user wants to use the memory manager innate with CPP (not this one) - adjust statics as necessary and use base CPP functions */ if (Config_->UseCPPMemManager_) { /* Adjust the statistics */ Stats_->FreeObjects_--; Stats_->ObjectsInUse_++; if (Stats_->ObjectsInUse_ > Stats_->MostObjects_) Stats_->MostObjects_ = Stats_->ObjectsInUse_; Stats_->Allocations_++; return new GenericObject(); } /* Check if the FreeList_ is empty */ if (Stats_->FreeObjects_ == 0) { /* Try allocating a new page */ try { allocate_new_page(); } catch(const std::exception& e) { throw OAException(OAException::E_NO_MEMORY, "New page allocation failed.\n"); return NULL; } /* If there are no new free objects - throw expection and return out of function */ if (Stats_->FreeObjects_ == 0) { throw OAException(OAException::E_NO_MEMORY, "No available memory was found.\n"); return NULL; } } /* Store the top of the free list in a local variable */ GenericObject* free_block = FreeList_; /* Detach the head of the list and reassemble */ FreeList_ = FreeList_->Next; /* Adjust the statistics */ Stats_->FreeObjects_--; Stats_->ObjectsInUse_++; if (Stats_->ObjectsInUse_ > Stats_->MostObjects_) Stats_->MostObjects_ = Stats_->ObjectsInUse_; Stats_->Allocations_++; /* Change the pointer into something we can modify */ unsigned char* modify = reinterpret_cast<unsigned char*>(free_block); /* If the Debug is set to true, set the values of the object to 0xBB */ if (Config_->DebugOn_) { memset(modify, ALLOCATED_PATTERN, sizeof(unsigned char) * Stats_->ObjectSize_); } /* Move the pointer to the start of the header */ modify -= Config_->PadBytes_ + Config_->HBlockInfo_.size_; /* Adjust logic based off the type of header being used */ if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbBasic) { /* Convert the header pointer to its data */ unsigned char* basic = (modify + sizeof(unsigned)); /* Notate that the object is in use */ *basic |= (1 << 0); /* Update the allocation number on the object */ unsigned int* alloc = reinterpret_cast<unsigned int*>(modify); *alloc = Stats_->Allocations_; } else if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbExtended) { /* Update the number of times the object has been allocated */ unsigned short* use_counter = reinterpret_cast<unsigned short*>(modify + Config_->HBlockInfo_.additional_); unsigned short temp = *use_counter; temp = static_cast<unsigned short>(temp + 1); *use_counter = temp; /* Update the allocation number on the object*/ unsigned int* alloc_counter = reinterpret_cast<unsigned int*>(modify + Config_->HBlockInfo_.additional_ + sizeof(unsigned short)); *alloc_counter = Stats_->Allocations_; /* Notate that the block of memory is in use */ unsigned char* flag = (modify + Config_->HBlockInfo_.additional_ + sizeof(unsigned short) + sizeof(unsigned int)); *flag = 1; } else if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbExternal) { /* Covert pointer to form of data that can be modified*/ *reinterpret_cast<MemBlockInfo**>(modify) = new MemBlockInfo(); /* Notate that the block of memory is in use */ (*reinterpret_cast<MemBlockInfo**>(modify))->in_use = true; /* Update the allocation number of the block of memory */ (*reinterpret_cast<MemBlockInfo**>(modify))->alloc_num = Stats_->Allocations_; /* Adjust the label of the header */ (*reinterpret_cast<MemBlockInfo**>(modify))->label = new char[128]; if (label) { memcpy((*reinterpret_cast<MemBlockInfo**>(modify))->label, label, sizeof(char) * 128); } } /* Return the result */ return free_block; } void ObjectAllocator::Free(void* Object) { /* Check to see if the object is valid */ if (Object == NULL) { /* If not, throw an exception and return out of function */ throw OAException(OAException::E_NO_MEMORY, "Invalid object; cannot be freed.\n"); return; } /* If the user wants to use the innate memory manager for CPP (not this one), update the statistics and use CPP functions */ if (Config_->UseCPPMemManager_) { /* Update the statistics */ Stats_->ObjectsInUse_--; Stats_->Deallocations_++; delete reinterpret_cast<GenericObject*>(Object); return; } /* Get the head of FreeList into a modifable pointer */ GenericObject* free_list = FreeList_; /* Iterate thorugh the free list to see if the object has been freed */ for (unsigned int i = 0; i < Stats_->FreeObjects_; i++) { /* If the obejct has been free - throw an exception and return out of function */ if (Object == free_list) { throw OAException(OAException::E_MULTIPLE_FREE, "Memory has already been freed.\n"); return; } /* Continue iteration */ free_list = free_list->Next; } /* Cast the pointer to something modifiable */ unsigned char* modify = reinterpret_cast<unsigned char*>(Object); /* Initatie the check to see if the memory given is on a known boundary line */ bool onBoundary = false; /* Get a stepper for the PageList */ GenericObject* page_list = PageList_; /* Iterate through the pages */ while (page_list) { /* Get the modifable page pointer */ unsigned char* page = reinterpret_cast<unsigned char*>(page_list) + PtrSize_; /* Check to see if the object is on the page */ if (modify >= page && modify <= reinterpret_cast<unsigned char*>(page_list) + Stats_->PageSize_) { /* Determine the distance between the current object and the beginning of the page */ long distance = modify - page; /* If the object is on a boundary - update bool and break out of iteration */ if (distance % ObjSize_ == 0) { onBoundary = true; break; } } /* Go to the next page */ page_list = page_list->Next; } /* If the object is not on a boundary - throw an exception and return out of function */ if (!onBoundary) { throw OAException(OAException::E_BAD_BOUNDARY, ""); return; } /* Check to see if the memory is corrupted */ /* Get the pointers to the surrounding padding */ unsigned char* left_padding = modify - Config_->PadBytes_; unsigned char* right_padding = modify + Stats_->ObjectSize_; /* Iterate through the paddings */ for (unsigned int i = 0; i < Config_->PadBytes_; i++) { /* If either padding has been overwritten with data - throw an exception and return out of the function */ if (*(left_padding + i) != PAD_PATTERN || *(right_padding + i) != PAD_PATTERN) { throw OAException(OAException::E_CORRUPTED_BLOCK, "The memory has overrun the padding.\n"); return; } } /* "Free" the memory */ if (Config_->DebugOn_) { memset(modify, FREED_PATTERN, sizeof(unsigned char) * Stats_->ObjectSize_); } else { memset(modify, 0x00, sizeof(unsigned char) * Stats_->ObjectSize_); } /* Move the pointer to the start of the header */ modify -= Config_->PadBytes_ + Config_->HBlockInfo_.size_; /* Adjust the logic based off of the time of header being used */ if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbBasic) { /* Reset the number of allocations and flag */ unsigned int* alloc = reinterpret_cast<unsigned int*>(modify); *alloc = 0; unsigned char* basic = (modify + sizeof(unsigned)); *basic = 0; } else if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbExtended) { /* Reset the number of allocations and flag */ unsigned int* alloc_counter = reinterpret_cast<unsigned int*>(modify + Config_->HBlockInfo_.additional_ + sizeof(unsigned short)); *alloc_counter = 0; unsigned char* flag = (modify + Config_->HBlockInfo_.additional_ + sizeof(unsigned short) + sizeof(unsigned int)); *flag = 0; } else if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbExternal) { /* Cast the pointer to a data type that can be modified */ (*reinterpret_cast<MemBlockInfo**>(modify))->in_use = 0; /* Reset the allocation data */ (*reinterpret_cast<MemBlockInfo**>(modify))->alloc_num = 0; /* Free the memory associated with the header label */ delete [] (*reinterpret_cast<MemBlockInfo**>(modify))->label; (*reinterpret_cast<MemBlockInfo**>(modify))->label = NULL; /* Delete the header */ delete *reinterpret_cast<MemBlockInfo**>(modify); /* Reset the memory block */ memset(modify, 0, sizeof(void*)); } /* Update the statistics */ Stats_->ObjectsInUse_--; Stats_->Deallocations_++; /* Cast pointer to something we can compare */ GenericObject* object = reinterpret_cast<GenericObject*>(Object); /* Add the object to the FreeList_ */ put_on_freelist(object); } unsigned ObjectAllocator::DumpMemoryInUse(DUMPCALLBACK fn) const { /* If no header is being used - return the default value and return */ if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbNone) { /* Return the number of blocks in use by the client */ return static_cast<unsigned>(Stats_->ObjectSize_); } /* Get the head of the page list */ GenericObject* page = reinterpret_cast<GenericObject*>(PageList_); /* Iterate through the pages to determine if the block of data is being used */ for (unsigned int i = 0; i < Stats_->PagesInUse_; i++) { /* Get the modifiable pointer of the beginning of the page - after the pointer */ unsigned char* page_char = reinterpret_cast<unsigned char*>(page) + PtrSize_; /* Iterate through the objects on the page */ for (unsigned int j = 0; j < Config_->ObjectsPerPage_; j++) { /* Determine the pointer of the current memory block and current header */ unsigned char* block = page_char + ObjSize_ * j; unsigned char* header = block - (Config_->PadBytes_ + Config_->HBlockInfo_.size_); /* Adjust the logic based off of the type of header */ if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbBasic) { /* Get the pointer to the flag */ unsigned char* flag = header + sizeof(unsigned); /* If the flag is triggered - the block is in use - dump the information */ if (*flag == 0x1) { fn(block, ObjSize_ - Config_->HBlockInfo_.size_); } } else if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbExtended) { /* Get the pointer to the flag */ unsigned char* flag = header + sizeof(unsigned int) + sizeof(unsigned short) + Config_->HBlockInfo_.additional_; /* If the flag is triggered - the block is in use - dump the information */ if (*flag == 0x1) { fn(block, ObjSize_ - Config_->HBlockInfo_.size_); } } else if (Config_->HBlockInfo_.type_ == OAConfig::HBLOCK_TYPE::hbExternal) { /* If the flag is triggered - the block is in use - dump the information */ if ((*reinterpret_cast<MemBlockInfo**>(header)) && (*reinterpret_cast<MemBlockInfo**>(header))->in_use) { fn(block, ObjSize_ - Config_->HBlockInfo_.size_); } } } /* Continue iteration */ page = page->Next; } /* Return the number of blocks in use by the client */ return static_cast<unsigned>(Stats_->ObjectsInUse_); } unsigned ObjectAllocator::ValidatePages(VALIDATECALLBACK fn) const { /* If Debug is off or there aren't any pad bytes - return */ if (Config_->DebugOn_ == false || Config_->PadBytes_ == 0) return 0; /* Initialize the corrupted counter */ unsigned corrupted = 0; /* Get a stepper variable for the page list */ GenericObject* page_list = PageList_; /* Iterate through the page list */ for (unsigned int i = 0; i < Stats_->PagesInUse_; i++) { /* Get the modifiable pointer for the start of the object list */ unsigned char* page_start = reinterpret_cast<unsigned char*>(page_list) + PtrSize_ ; /* Go through the objects in the page */ for (unsigned int j = 0; j < Config_->ObjectsPerPage_; j++) { /* Get the left and right padding of the object */ unsigned char* left_pad = page_start + ObjSize_ * j - Config_->PadBytes_; unsigned char* right_pad = page_start + ObjSize_ * j + Stats_->ObjectSize_; /* Iterate through the padding */ for (unsigned int k = 0; k < Config_->PadBytes_; k++) { /* If the paddings have been overwritten */ if (*(left_pad + k) != PAD_PATTERN || *(right_pad + k) != PAD_PATTERN) { /* Increment the statistics and call the dump function */ corrupted++; fn(left_pad, Stats_->ObjectSize_); break; } } } /* Go to the next page */ page_list = page_list->Next; } /* Return number of corrupted blocks - TO DO */ return corrupted; } unsigned ObjectAllocator::FreeEmptyPages(void) { /* Initialize free page counter */ unsigned int pages_freed = 0; /* Get the stepper for the PageList */ GenericObject* page_list = PageList_; /* Get a prev pointer to link up the list later */ GenericObject* page_prev = NULL; /* Iterate through the pages using the stepper */ while (page_list) { /* Get the modifiable pointer for the current page (starting at the beginning of the first object */ unsigned char* page_ptr = reinterpret_cast<unsigned char*>(page_list) + PtrSize_; /* Reset the freed object count */ unsigned int freed_objects = 0; /* Iterate through the objects on the page */ for (unsigned int i = 0; i < Config_->ObjectsPerPage_; i++) { /* Get the current object */ unsigned char* curr_obj = page_ptr + ObjSize_ * i; /* Get the stepper for the FreeList */ GenericObject* free_list = FreeList_; /* Iterate through the FreeList using the stepper */ while (free_list) { /* Compare the current free list object to the current object on the page */ if (free_list == reinterpret_cast<GenericObject*>(curr_obj)) { /* If they are the same object, increment the freed object counter */ freed_objects++; } /* Go to the next object on the free list */ free_list = free_list->Next; } } /* Check to see if all the objects on the current page have been freed */ if (freed_objects == Config_->ObjectsPerPage_) { /* Iterate through the objects on the page again */ for (unsigned int i = 0; i < Config_->ObjectsPerPage_; i++) { /* Get the current object */ unsigned char* curr_obj = page_ptr + ObjSize_ * i; /* Get the stepper for the FreeList */ GenericObject* free_list = FreeList_; /* Create a prev pointer to link up the FreeList */ GenericObject* free_prev = NULL; /* Iterate through the FreeList using the stepper */ while (free_list) { /* Compare the current free list object to the current object on the page */ if (free_list == reinterpret_cast<GenericObject*>(curr_obj)) { /* Remove the object from the FreeList */ free_list = free_list->Next; /* If the head of the list has been removed - update the necessary pointer */ if (free_prev == NULL) { FreeList_ = free_list; } else { free_prev->Next = free_list; } /* Update the statistics */ Stats_->FreeObjects_--; /* Continue iterating through the list */ break; } else { /* Update the pointers */ free_prev = free_list; free_list = free_list->Next; } } } /* Delete the current page and link the page list back up */ GenericObject* temp = page_list; page_list = page_list->Next; delete [] reinterpret_cast<unsigned char*>(temp); /* Link the list back up */ if (page_prev == NULL) { PageList_ = page_list; } else { page_prev->Next = page_list; } /* Update the statistics */ Stats_->PagesInUse_--; pages_freed++; /* Continue through the iteration */ continue; } else { /* Update the pointers */ page_prev = page_list; page_list = page_list->Next; } } /* Return the number of pages that has been freed */ return pages_freed; } bool ObjectAllocator::ImplementedExtraCredit(void) { return true; } void ObjectAllocator::SetDebugState(bool State) { Config_->DebugOn_ = State; } const void* ObjectAllocator::GetFreeList(void) const { return FreeList_; } const void* ObjectAllocator::GetPageList(void) const { return PageList_; } OAConfig ObjectAllocator::GetConfig(void) const { return *Config_; } OAStats ObjectAllocator::GetStats(void) const { return *Stats_; }
Below is the interface provided by the course. This code is not my own and merely here for cross referencing how the interface and my above code interact.
//--------------------------------------------------------------------------- #ifndef OBJECTALLOCATORH #define OBJECTALLOCATORH //--------------------------------------------------------------------------- #include <string> #include <iostream> // If the client doesn't specify these: static const int DEFAULT_OBJECTS_PER_PAGE = 4; static const int DEFAULT_MAX_PAGES = 3; class OAException { public: // Possible exception codes enum OA_EXCEPTION { E_NO_MEMORY, // out of physical memory (operator new fails) E_NO_PAGES, // out of logical memory (max pages has been reached) E_BAD_BOUNDARY, // block address is on a page, but not on any block-boundary E_MULTIPLE_FREE, // block has already been freed E_CORRUPTED_BLOCK // block has been corrupted (pad bytes have been overwritten) }; OAException(OA_EXCEPTION ErrCode, const std::string& Message) : error_code_(ErrCode), message_(Message) {}; virtual ~OAException() { } OA_EXCEPTION code(void) const { return error_code_; } virtual const char *what(void) const { return message_.c_str(); } private: OA_EXCEPTION error_code_; std::string message_; }; // ObjectAllocator configuration parameters struct OAConfig { static const size_t BASIC_HEADER_SIZE = sizeof(unsigned) + 1; // allocation number + flags static const size_t EXTERNAL_HEADER_SIZE = sizeof(void*); // just a pointer enum HBLOCK_TYPE{hbNone, hbBasic, hbExtended, hbExternal}; struct HeaderBlockInfo { HBLOCK_TYPE type_; size_t size_; size_t additional_; HeaderBlockInfo(HBLOCK_TYPE type = hbNone, unsigned additional = 0) : type_(type), size_(0), additional_(additional) { if (type_ == hbBasic) size_ = BASIC_HEADER_SIZE; else if (type_ == hbExtended) // alloc # + use counter + flag byte + user-defined size_ = sizeof(unsigned int) + sizeof(unsigned short) + sizeof(char) + additional_; else if (type_ == hbExternal) size_ = EXTERNAL_HEADER_SIZE; }; }; OAConfig(bool UseCPPMemManager = false, unsigned ObjectsPerPage = DEFAULT_OBJECTS_PER_PAGE, unsigned MaxPages = DEFAULT_MAX_PAGES, bool DebugOn = false, unsigned PadBytes = 0, const HeaderBlockInfo &HBInfo = HeaderBlockInfo(), unsigned Alignment = 0) : UseCPPMemManager_(UseCPPMemManager), ObjectsPerPage_(ObjectsPerPage), MaxPages_(MaxPages), DebugOn_(DebugOn), PadBytes_(PadBytes), HBlockInfo_(HBInfo), Alignment_(Alignment) { HBlockInfo_ = HBInfo; LeftAlignSize_ = 0; InterAlignSize_ = 0; } bool UseCPPMemManager_; // by-pass the functionality of the OA and use new/delete unsigned ObjectsPerPage_; // number of objects on each page unsigned MaxPages_; // maximum number of pages the OA can allocate (0=unlimited) bool DebugOn_; // enable/disable debugging code (signatures, checks, etc.) unsigned PadBytes_; // size of the left/right padding for each block HeaderBlockInfo HBlockInfo_; // size of the header for each block (0=no headers) unsigned Alignment_; // address alignment of each block unsigned LeftAlignSize_; // number of alignment bytes required to align first block unsigned InterAlignSize_; // number of alignment bytes required between remaining blocks }; // ObjectAllocator statistical info struct OAStats { OAStats(void) : ObjectSize_(0), PageSize_(0), FreeObjects_(0), ObjectsInUse_(0), PagesInUse_(0), MostObjects_(0), Allocations_(0), Deallocations_(0) {}; size_t ObjectSize_; // size of each object size_t PageSize_; // size of a page including all headers, padding, etc. unsigned FreeObjects_; // number of objects on the free list unsigned ObjectsInUse_; // number of objects in use by client unsigned PagesInUse_; // number of pages allocated unsigned MostObjects_; // most objects in use by client at one time unsigned Allocations_; // total requests to allocate memory unsigned Deallocations_; // total requests to free memory }; // This allows us to easily treat raw objects as nodes in a linked list struct GenericObject { GenericObject *Next; }; struct MemBlockInfo { bool in_use; // Is the block free or in use? char *label; // A dynamically allocated NUL-terminated string unsigned alloc_num; // The allocation number (count) of this block }; // This memory manager class class ObjectAllocator { public: // Defined by the client (pointer to a block, size of block) typedef void (*DUMPCALLBACK)(const void *, size_t); typedef void (*VALIDATECALLBACK)(const void *, size_t); // Predefined values for memory signatures static const unsigned char UNALLOCATED_PATTERN = 0xAA; static const unsigned char ALLOCATED_PATTERN = 0xBB; static const unsigned char FREED_PATTERN = 0xCC; static const unsigned char PAD_PATTERN = 0xDD; static const unsigned char ALIGN_PATTERN = 0xEE; // Creates the ObjectManager per the specified values // Throws an exception if the construction fails. (Memory allocation problem) ObjectAllocator(size_t ObjectSize, const OAConfig& config); // Destroys the ObjectManager (never throws) ~ObjectAllocator(); // Take an object from the free list and give it to the client (simulates new) // Throws an exception if the object can't be allocated. (Memory allocation problem) void *Allocate(const char *label = 0); // Returns an object to the free list for the client (simulates delete) // Throws an exception if the the object can't be freed. (Invalid object) void Free(void *Object); // Calls the callback fn for each block still in use unsigned DumpMemoryInUse(DUMPCALLBACK fn) const; // Calls the callback fn for each block that is potentially corrupted unsigned ValidatePages(VALIDATECALLBACK fn) const; // Frees all empty pages (extra credit) unsigned FreeEmptyPages(void); // Returns true if FreeEmptyPages and alignments are implemented static bool ImplementedExtraCredit(void); // Testing/Debugging/Statistic methods void SetDebugState(bool State); // true=enable, false=disable const void *GetFreeList(void) const; // returns a pointer to the internal free list const void *GetPageList(void) const; // returns a pointer to the internal page list OAConfig GetConfig(void) const; // returns the configuration parameters OAStats GetStats(void) const; // returns the statistics for the allocator private: // Some "suggested" members (only a suggestion!) GenericObject *PageList_; // the beginning of the list of pages GenericObject *FreeList_; // the beginning of the list of objects void allocate_new_page(void); // allocates another page of objects void put_on_freelist(void *Object); // puts Object onto the free list OAStats* Stats_; OAConfig* Config_; size_t PtrSize_; size_t ObjSize_; // Make private to prevent copy construction and assignment ObjectAllocator(const ObjectAllocator &oa); ObjectAllocator &operator=(const ObjectAllocator &oa); }; #endif