/* All of this first stuff goes in recycle_handler.h */ #include "bitmask.h" // You must have the bitmask code and them defined in this scope. // I use bitmask.h. // The code for them can be found on mudbytes. The C version is all // That is used here. typedef BITMASK BITLIST; typedef struct chunk_data CHUNK_DATA; typedef struct freed_chunk_data FREED_CHUNK_DATA; typedef struct recycle_data RECYCLE_DATA; typedef struct _mem_info MEM_INFO; // A single node in the chunk_list that contains all raw memory. struct chunk_data { int amount; // This is the size of the memory in this chunk. int used; // This is the size used of the memory in this chunk. void *pool; // This points to a chunk. BITLIST minfo_datalist; // list for all our our data. BITLIST minfo_inlist; // Simply a list of which minfos are in the data list. }; // A single node pointing back to the chunks with an amount it points to. // Latent recycled data will be reinserted into the system using this method. struct freed_chunk_data { int amount; // Total in this chunk. int used; // How much is used out of this chunk void *pool; // This points to a chunk. }; struct recycle_data { int size; // These nodes are of this size. BITLIST pool_list; // A list of memory of this size. }; // saves info on memory give away. struct _mem_info { void *pool; // memory pool in question int amount; // Tells us how much was requested. bool free; // We can add more info later if we ever need it. }; extern BITLIST chunk_list; extern BITLIST freed_chunk_list; extern BITLIST recycle_list; void *recycle_malloc(int); void recycle_free(void *); bool validate_memory(void *); **** END OF recycle_handler.h ****/ #include #include #include #include "merc.h" #include "recycle_handler.h" // These lists contain all of our memory. // These are lists using bitmasks as a list. // You need Runter's bitmask code for this to work. BITLIST chunk_list; BITLIST freed_chunk_list; BITLIST recycle_list; int mem_alloc=0; // total in circulation. int mem_used=0; // currently used int mem_recycle=0; // memory in the recycle system currently // Size of our chunks each and every time they are called. int iSizePerm; // Just tells you if a pointer is allocated or not. bool memory_validate(void *mem) { int value = (int) mem; int value2; bool found = false; CHUNK_DATA* *clist = (CHUNK_DATA**) serialize_bitmask(&chunk_list); int i; for(i = 0;clist[i];++i) { value2 = (int)clist[i]->pool; if (value < value2 && value >= (value2 - clist[i]->used)) found = true; } free (clist); return found; } // this function is called any time we need memory. CHUNK_DATA* new_chunk() { CHUNK_DATA *pChunk = (CHUNK_DATA *)malloc(sizeof(CHUNK_DATA)); pChunk->amount = iSizePerm; // Chunk is always of iSizePerm. // It is indeed likely we could assume that we // wouldn't to match the initial load if we set the // initial chunk to almost as much as it is required // to run the program, but in reality in my tests // I have found it's best to set this value about // 10% of the total ram and have a few more malloc // calls. pChunk->used = 0; // Nothing used so far. We will increase this value as we use more from this chunk. pChunk->pool = calloc(1, iSizePerm); // Allocate iSizePerm bytes. mem_alloc += iSizePerm; set_bit(&chunk_list, (int)pChunk); init_bitmask(&pChunk->minfo_datalist); init_bitmask(&pChunk->minfo_inlist); return pChunk; } bool memory_initd = false; // Initiate our recyclable memory by a certain amount. // Each time it needs to call a chunk it will reuse this // value. It's best to make this more than half of your total // memory usage. void init_memory(int iSize) { iSizePerm = iSize; memory_initd = true; // Initialize all of our lists. init_bitmask(&chunk_list); init_bitmask(&freed_chunk_list); init_bitmask(&recycle_list); new_chunk(); // Get our first chunk. } void new_recycle(int ofsize) { RECYCLE_DATA *p = (RECYCLE_DATA *)malloc(sizeof(RECYCLE_DATA)); p->size = ofsize; init_bitmask(&p->pool_list); set_bit(&recycle_list, (int)p); } // find some data on a pointer we've given away MEM_INFO* find_mdata(void *trash) { CHUNK_DATA **clist = (CHUNK_DATA **) serialize_bitmask(&chunk_list); int i; MEM_INFO *found = NULL; for(i = 0;clist[i];++i) { // now let's see if the trash is ever here. if (is_set(&clist[i]->minfo_inlist, (int)trash)) { // Yes, it is here. MEM_INFO **mlist = (MEM_INFO **) serialize_bitmask(&clist[i]->minfo_datalist); int z; for(z = 0;mlist[z];z++) { if (mlist[z]->pool == trash) { found = mlist[z]; free(mlist); break; } } break; } } free(clist); return found; } // recursive function to add some memory to the trash list. void recycle_free(void *trash) { // We'll free some trash and put it back in the chunk list. RECYCLE_DATA **rlist = (RECYCLE_DATA **) serialize_bitmask(&recycle_list); bool found = false; int i; MEM_INFO*pMemData = find_mdata(trash); int ofsize; if (pMemData->free == TRUE) { // was already free free (rlist); return; } ofsize = pMemData->amount; for(i = 0;rlist[i];++i) { if (ofsize == rlist[i]->size) { set_bit(&rlist[i]->pool_list, (int)trash); found = true; mem_used -= ofsize; mem_recycle += ofsize; pMemData->free = TRUE; break; } } free(rlist); if (found == false) { new_recycle(ofsize); recycle_free(trash); } } // Finds some recycled memory of a certain size, if it exists. void *get_recycled(int ofsize) { RECYCLE_DATA **rlist = (RECYCLE_DATA **) serialize_bitmask(&recycle_list); int i; void *ptr = 0; for(i = 0;rlist[i];++i) if(rlist[i]->size == ofsize) ptr = pop_bitmask(&rlist[i]->pool_list); free(rlist); if (ptr) { mem_used += ofsize; mem_recycle -= ofsize; } return ptr; } void new_minfo(CHUNK_DATA *pChunk, int ofsize) { MEM_INFO *pMemInfo = (MEM_INFO *) malloc(sizeof(struct _mem_info)); pMemInfo->free = FALSE; pMemInfo->amount = ofsize; pMemInfo->pool = pChunk->pool; set_bit(&pChunk->minfo_datalist, (int)pMemInfo); set_bit(&pChunk->minfo_inlist, (int)pChunk->pool); } // recursive function to grab some memory. void *recycle_malloc(int ofsize) { CHUNK_DATA **pChunkList; int i; void *ptr=0; int found = FALSE; if (!memory_initd) init_memory(50000); // init our memory system. // First let's look through our recycled memory for one of exactly this size. ptr = get_recycled(ofsize); if (ptr) { MEM_INFO*pMemData = find_mdata(ptr); pMemData->free = FALSE; return ptr; } pChunkList = (CHUNK_DATA**) serialize_bitmask(&chunk_list); // Look for a chunk with enough free memory to give us our request. for(i = 0;pChunkList[i];++i) { if ((pChunkList[i]->amount - pChunkList[i]->used) < ofsize) // Not enough in this chunk, continue on in the loop. continue; new_minfo(pChunkList[i], ofsize); mem_alloc -= ofsize; mem_used += ofsize; // There's enough so let's grab a piece of it. pChunkList[i]->used += ofsize; // We're using more now. ptr = pChunkList[i]->pool; // This is what we'll end up returning. found = TRUE; pChunkList[i]->pool = (void*)((int)(pChunkList[i]->pool) + ofsize); // Next location we'll pull from. break; } free(pChunkList); // free the list we created with serialize_bitmask. if (found == FALSE) { // This means nothing was found, so we need to create a new chunk. // EDIT: It was brought to my attention that if the amount called // was bigger than the chunk that we'd hit an infinite loop. // So we're going to set the chunk size to the amount of the call if // the amount requested is bigger than our var if (iSizePerm < ofsize) iSizePerm = ofsize; // Okay, so we need to create a new chunk. new_chunk(); // creates a new chunk. This shoudl be big enough. } return ptr ? ptr : recycle_malloc(ofsize); } // Dumps debug information to a string. You can do whatever you want to with said string. char *dump_mem_info() { static char buf[512]; CHUNK_DATA **clist = (CHUNK_DATA**) serialize_bitmask(&chunk_list); int i, count = 0, totmem = 0; int pieces = 0, free_pieces= 0; int total_pieces_data = 0; for(i = 0;clist[i];++i) { MEM_INFO **mlist = (MEM_INFO**) serialize_bitmask(&clist[i]->minfo_datalist); int z; for(z = 0;mlist[z];++z) { ++pieces; if(mlist[z]->free) ++free_pieces; total_pieces_data += mlist[z]->amount; } free(mlist); totmem += clist[i]->amount; count++; } free(clist); sprintf(buf, " Total pieces defined: %d pieces\r\n" " Of those, pieces in active use: %d pieces\r\n" "Of those, pieces in recycle bin: %d pieces\r\n" " For Verified Total: %d bytes\r\n" " --- --- --- --- --- --- --- --- --- ---\r\n" " Memory currently in use: %d bytes\r\n" " Memory currently standing by: %d bytes\r\n" "Memory currently in recycle bin: %d bytes\r\n" " --- --- --- --- --- --- --- --- --- ---\r\n" " %3d chunks for Verified Total: %d bytes\r\n",pieces, pieces - free_pieces, free_pieces, total_pieces_data, mem_used, mem_alloc, mem_recycle, count, totmem); return buf; } // Command function for testing and values. void do_memtest(CHAR_DATA *ch, char *argument) { send_to_char(dump_mem_info(), ch); }