void (Z_Free)(void *p, const char *file, int line) { #ifdef INSTRUMENTED #ifdef CHECKHEAP Z_CheckHeap(); #endif file_history[free_history][history_index[free_history]] = file; line_history[free_history][history_index[free_history]++] = line; history_index[free_history] &= ZONE_HISTORY-1; #endif if (p) { memblock_t *other, *block = (memblock_t *)((char *) p - HEADER_SIZE); #ifdef ZONEIDCHECK if (block->id != ZONEID) I_Error("Z_Free: freed a pointer without ZONEID\n" "Source: %s:%d" #ifdef INSTRUMENTED "\nSource of malloc: %s:%d" , file, line, block->file, block->line #else , file, line #endif ); block->id = 0; // Nullify id so another free fails #endif #ifdef INSTRUMENTED // scramble memory -- weed out any bugs memset(p, gametic & 0xff, block->size - block->extra); #endif if (block->user) // Nullify user if one exists *block->user = NULL; if (block->vm) { if ((*(memblock_t **) block->prev = block->next)) block->next->prev = block->prev; #ifdef INSTRUMENTED virtual_memory -= block->size; #endif (free)(block); } else { #ifdef INSTRUMENTED free_memory += block->size; inactive_memory -= block->extra; if (block->tag >= PU_PURGELEVEL) purgable_memory -= block->size - block->extra; else active_memory -= block->size - block->extra; #endif block->tag = PU_FREE; // Mark block freed if (block != zone) { other = block->prev; // Possibly merge with previous block if (other->tag == PU_FREE) { if (rover == block) // Move back rover if it points at block rover = other; (other->next = block->next)->prev = other; other->size += block->size + HEADER_SIZE; block = other; #ifdef INSTRUMENTED inactive_memory -= HEADER_SIZE; free_memory += HEADER_SIZE; #endif } } other = block->next; // Possibly merge with next block if (other->tag == PU_FREE && other != zone) { if (rover == other) // Move back rover if it points at next block rover = block; (block->next = other->next)->prev = block; block->size += other->size + HEADER_SIZE; #ifdef INSTRUMENTED inactive_memory -= HEADER_SIZE; free_memory += HEADER_SIZE; #endif } } #ifdef INSTRUMENTED Z_PrintStats(); // print memory allocation stats #endif } }
void *(Z_Malloc)(size_t size, int tag, void **user, const char *file, int line) { register memblock_t *block; memblock_t *start; #ifdef INSTRUMENTED size_t size_orig = size; #ifdef CHECKHEAP Z_CheckHeap(); #endif file_history[malloc_history][history_index[malloc_history]] = file; line_history[malloc_history][history_index[malloc_history]++] = line; history_index[malloc_history] &= ZONE_HISTORY-1; #endif #ifdef ZONEIDCHECK if (tag >= PU_PURGELEVEL && !user) I_Error ("Z_Malloc: an owner is required for purgable blocks\n" "Source: %s:%d", file, line); #endif if (!size) return user ? *user = NULL : NULL; // malloc(0) returns NULL size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size block = rover; if (block->prev->tag == PU_FREE) block = block->prev; start = block; do { if (block->tag >= PU_PURGELEVEL) // Free purgable blocks { // replacement is roughly FIFO start = block->prev; Z_Free((char *) block + HEADER_SIZE); block = start = start->next; // Important: resets start } if (block->tag == PU_FREE && block->size >= size) // First-fit { size_t extra = block->size - size; if (extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { memblock_t *newb = (memblock_t *)((char *) block + HEADER_SIZE + size); (newb->next = block->next)->prev = newb; (newb->prev = block)->next = newb; // Split up block block->size = size; newb->size = extra - HEADER_SIZE; newb->tag = PU_FREE; newb->vm = 0; #ifdef INSTRUMENTED inactive_memory += HEADER_SIZE; free_memory -= HEADER_SIZE; #endif } rover = block->next; // set roving pointer for next search #ifdef INSTRUMENTED inactive_memory += block->extra = block->size - size_orig; if (tag >= PU_PURGELEVEL) purgable_memory += size_orig; else active_memory += size_orig; free_memory -= block->size; #endif allocated: #ifdef INSTRUMENTED block->file = file; block->line = line; #endif #ifdef ZONEIDCHECK block->id = ZONEID; // signature required in block header #endif block->tag = tag; // tag block->user = user; // user block = (memblock_t *)((char *) block + HEADER_SIZE); if (user) // if there is a user *user = block; // set user to point to new block #ifdef INSTRUMENTED Z_PrintStats(); // print memory allocation stats // scramble memory -- weed out any bugs memset(block, gametic & 0xff, size); #endif return block; } } while ((block = block->next) != start); // detect cycles as failure // We've run out of physical memory, or so we think. // Although less efficient, we'll just use ordinary malloc. // This will squeeze the remaining juice out of this machine // and start cutting into virtual memory if it has it. while (!(block = (malloc)(size + HEADER_SIZE))) { if (!blockbytag[PU_CACHE]) I_Error ("Z_Malloc: Failure trying to allocate %lu bytes" "\nSource: %s:%d",(unsigned long) size, file, line); Z_FreeTags(PU_CACHE,PU_CACHE); } if ((block->next = blockbytag[tag])) block->next->prev = (memblock_t *) &block->next; blockbytag[tag] = block; block->prev = (memblock_t *) &blockbytag[tag]; block->vm = 1; #ifdef INSTRUMENTED virtual_memory += #endif block->size = size + HEADER_SIZE; // CPhipps - this was lost in the #ifdef above goto allocated; }
void* (Z_Malloc)(size_t size, int tag, void** user, const char* file, int line) { register memblock_t* block; memblock_t* start; #ifdef INSTRUMENTED size_t size_orig = size; #ifdef CHECKHEAP Z_CheckHeap(); #endif file_history[malloc_history][history_index[malloc_history]] = file; line_history[malloc_history][history_index[malloc_history]++] = line; history_index[malloc_history] &= ZONE_HISTORY - 1; #endif #ifdef ZONEIDCHECK if (tag >= PU_PURGELEVEL && !user) I_Error("Z_Malloc: an owner is required for purgable blocks\n" "Source: %s:%d", file, line); #endif if (!size) return user ? *user = NULL : NULL; // malloc(0) returns NULL size = (size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1); // round to chunk size block = rover; if (block->prev->tag == PU_FREE) block = block->prev; start = block; // haleyjd 06/17/08: import from EE: // the first if() inside the loop below contains cph's memory // purging efficiency fix do { // Free purgable blocks; replacement is roughly FIFO if (block->tag >= PU_PURGELEVEL) { start = block->prev; Z_Free((char*) block + HEADER_SIZE); /* cph - If start->next == block, we did not merge with the previous * If !=, we did, so we continue from start. * Important: we've reset start! */ if (start->next == block) start = start->next; else block = start; } if (block->tag == PU_FREE && block->size >= size) // First-fit { size_t extra = block->size - size; if (extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { memblock_t* newb = (memblock_t*)((char*) block + HEADER_SIZE + size); (newb->next = block->next)->prev = newb; (newb->prev = block)->next = newb; // Split up block block->size = size; newb->size = extra - HEADER_SIZE; newb->tag = PU_FREE; newb->vm = 0; #ifdef INSTRUMENTED inactive_memory += HEADER_SIZE; free_memory -= HEADER_SIZE; #endif } rover = block->next; // set roving pointer for next search #ifdef INSTRUMENTED inactive_memory += block->extra = block->size - size_orig; if (tag >= PU_PURGELEVEL) purgable_memory += size_orig; else active_memory += size_orig; free_memory -= block->size; #endif allocated: #ifdef INSTRUMENTED block->file = file; block->line = line; #endif #ifdef ZONEIDCHECK block->id = ZONEID; // signature required in block header #endif block->tag = tag; // tag block->user = user; // user block = (memblock_t*)((char*) block + HEADER_SIZE); if (user) // if there is a user *user = block; // set user to point to new block #ifdef INSTRUMENTED Z_PrintStats(); // print memory allocation stats // scramble memory -- weed out any bugs memset(block, gametic & 0xff, size); #endif return block; } } while ((block = block->next) != start); // detect cycles as failure // We've run out of physical memory, or so we think. // Although less efficient, we'll just use ordinary malloc. // This will squeeze the remaining juice out of this machine // and start cutting into virtual memory if it has it. while (!(block = (malloc)(size + HEADER_SIZE))) { if (!blockbytag[PU_CACHE]) I_Error("Z_Malloc: Failure trying to allocate %lu bytes" "\nSource: %s:%d", (unsigned long) size, file, line); Z_FreeTags(PU_CACHE, PU_CACHE); } if ((block->next = blockbytag[tag])) block->next->prev = (memblock_t*) &block->next; blockbytag[tag] = block; block->prev = (memblock_t*) &blockbytag[tag]; block->vm = 1; // haleyjd: cph's virtual memory error fix #ifdef INSTRUMENTED virtual_memory += size + HEADER_SIZE; // haleyjd 06/17/08: Import from EE: // Big problem: extra wasn't being initialized for vm // blocks. This caused the memset used to randomize freed memory when // INSTRUMENTED is defined to stomp all over the C heap. block->extra = 0; #endif /* cph - the next line was lost in the #ifdef above, and also added an * extra HEADER_SIZE to block->size, which was incorrect */ block->size = size; goto allocated; }
// // Z_Realloc // // haleyjd 09/18/06: Rewritten to be an actual realloc routine. The // various cases are as follows: // // 1. If the block is NULL, is in virtual memory, or we're trying to set it to // zero-byte size, we use Z_ReallocOld above. // 2. If the block is smaller than the new size, we need to expand it. If the // next block on the zone heap is free, check to see if it together with the // current block is large enough. If so, merge the blocks. Now test to make // sure the internal fragmentation does not exceed the split limit. If it // does, resplit the blocks at the new boundary. If the next block wasn't // free, we have to call Z_ReallocOld to move the entire block elsewhere. // 3. If the block is larger than the new size, we can shrink it, but we only // need to shrink it if the wasted space is larger than the split limit. // If so, the block is split at its new boundary. If the next block on the // zone heap is free, it is then necessary to merge the new free block with // the next block on the heap. In the event the block is not shrunk, only the // INSTRUMENTED data needs to be updated to reflect the new internal fragmen- // tation. // 4. If the block is already the same size as "n", we don't need to do anything // aside from adjusting the INSTRUMENTED block data for debugging purposes. // void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user, __string file, int line) { register memblock_t __near *block, *other; register size_t curr_size = 0; // davidph 12/09/12: Handle null and size 0 right here instead of in Z_ReallocOld. if(n == 0) { (Z_Free)(ptr, file, line); return NULL; } if(!ptr) return (Z_Malloc)(n, tag, user, file, line); // get current size of block block = VoidToBlock(ptr); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_Realloc: Reallocated a block without ZONEID\n", block, file, line); other = block->next; // save pointer to next block curr_size = block->size; // round new size to CHUNK_SIZE n = (n + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1); if(n > curr_size) // is new allocation size larger than current? { register size_t extra; // haleyjd 10/03/06: free adjacent purgable blocks while(other != zone && other != block && (other->tag == PU_FREE || other->tag >= PU_PURGELEVEL)) { if(other->tag >= PU_PURGELEVEL) { (Z_Free)(BlockToVoid(other), file, line); // reset pointer to next block other = block->next; } // use current size of block; note it may have increased if it was // merged with an adjacent free block // if we've freed enough, stop if(curr_size + other->size + HEADER_SIZE >= n) break; // move to next block other = other->next; } // reset pointer other = block->next; // check to see if it can fit if we merge with the next block if(other != zone && other->tag == PU_FREE && curr_size + other->size + HEADER_SIZE >= n) { // merge the blocks if(rover == other) rover = block; (block->next = other->next)->prev = block; block->size += other->size + HEADER_SIZE; #ifdef INSTRUMENTED // lost a block... inactive_memory -= HEADER_SIZE; // lost a free block... free_memory -= other->size; // increased active or purgable if(block->tag >= PU_PURGELEVEL) purgable_memory += other->size + HEADER_SIZE; else active_memory += other->size + HEADER_SIZE; #endif // check to see if there's enough extra to warrant splitting off // a new free block extra = block->size - n; if(extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { register memblock_t __near *newb = (memblock_t __near *)((char __near *)block + HEADER_SIZE + n); (newb->next = block->next)->prev = newb; (newb->prev = block)->next = newb; block->size = n; newb->size = extra - HEADER_SIZE; newb->tag = PU_FREE; if(rover == block) rover = newb; #ifdef INSTRUMENTED // added a block... inactive_memory += HEADER_SIZE; // added a free block... free_memory += newb->size; // decreased active or purgable if(block->tag >= PU_PURGELEVEL) purgable_memory -= newb->size + HEADER_SIZE; else active_memory -= newb->size + HEADER_SIZE; #endif } // subtract old internal fragmentation and add new INSTRUMENT(inactive_memory -= block->extra); INSTRUMENT(inactive_memory += (block->extra = block->size - n)); } else // else, do old realloc (make new, copy old, free old) { // davidph 12/10/12: This was the only place Z_ReallocOld was called. // And we know that ptr != NULL and n != 0 and n > curr_size. register void *p = (Z_Malloc)(n, tag, user, file, line); memcpy_near((void __near *)p, (void __near *)ptr, curr_size); (Z_Free)(ptr, file, line); if(user) // in case Z_Free nullified same user *user = p; return p; } } else if(n < curr_size) // is new allocation size smaller than current? { // check to see if there's enough extra to warrant splitting off // a new free block size_t extra = curr_size - n; if(extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { register memblock_t __near *newb = (memblock_t __near *)((char __near *)block + HEADER_SIZE + n); (newb->next = block->next)->prev = newb; (newb->prev = block)->next = newb; block->size = n; newb->size = extra - HEADER_SIZE; newb->tag = PU_FREE; #ifdef INSTRUMENTED // added a block... inactive_memory += HEADER_SIZE; // added a free block... free_memory += newb->size; // decreased purgable or active if(block->tag >= PU_PURGELEVEL) purgable_memory -= newb->size + HEADER_SIZE; else active_memory -= newb->size + HEADER_SIZE; #endif // may need to merge new block with next block if(other && other->tag == PU_FREE && other != zone) { if(rover == other) // Move back rover if it points at next block rover = newb; (newb->next = other->next)->prev = newb; newb->size += other->size + HEADER_SIZE; // deleted a block... INSTRUMENT(inactive_memory -= HEADER_SIZE); // space between blocks is now free INSTRUMENT(free_memory += HEADER_SIZE); } } // else, leave block the same size // subtract old internal fragmentation and add new INSTRUMENT(inactive_memory -= block->extra); INSTRUMENT(inactive_memory += (block->extra = block->size - n)); } // else new allocation size is same as current, don't change it // modify the block INSTRUMENT(block->file = file); INSTRUMENT(block->line = line); // reset ptr for consistency ptr = BlockToVoid(block); if(block->user != user) { if(block->user) // nullify old user if any *(block->user) = NULL; block->user = user; // set block's new user if(user) // if non-null, set user to allocation *user = ptr; } // let Z_ChangeTag handle changing the tag if(block->tag != tag) (Z_ChangeTag)(ptr, tag, file, line); Z_PrintStats(); // print memory allocation stats Z_LogPrintf("* Z_Realloc(ptr=%p, n=%u, tag=%d, user=%p, source=%s:%d)\n", ptr, n, tag, user, file, line); return ptr; }
// // Z_Free // void (Z_Free)(void *p, __string file, int line) { register memblock_t __near *other, *block; if(!p) return; DEBUG_CHECKHEAP(); block = VoidToBlock(p); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_Free: freed a pointer without ZONEID", block, file, line); IDCHECK(block->id = 0); // Nullify id so another free fails // haleyjd 01/20/09: check invalid tags // catches double frees and possible selective heap corruption if(block->tag == PU_FREE || block->tag >= PU_MAX) { zonef("Z_Free: freed a pointer with invalid tag %d\nSource: %s:%d\n" #ifdef INSTRUMENTED "Source of malloc: %s:%d\n" , block->tag, file, line, block->file, block->line #else , block->tag, file, line #endif ); abort(); } SCRAMBLER(p, block->size); if(block->user) // Nullify user if one exists *block->user = NULL; #ifdef INSTRUMENTED free_memory += block->size; inactive_memory -= block->extra; if(block->tag >= PU_PURGELEVEL) purgable_memory -= block->size - block->extra; else active_memory -= block->size - block->extra; #endif block->tag = PU_FREE; // Mark block freed if(block != zone) { other = block->prev; // Possibly merge with previous block if(other->tag == PU_FREE) { if(rover == block) // Move back rover if it points at block rover = other; (other->next = block->next)->prev = other; other->size += block->size + HEADER_SIZE; block = other; INSTRUMENT(inactive_memory -= HEADER_SIZE); INSTRUMENT(free_memory += HEADER_SIZE); } } other = block->next; // Possibly merge with next block if(other->tag == PU_FREE && other != zone) { if(rover == other) // Move back rover if it points at next block rover = block; (block->next = other->next)->prev = block; block->size += other->size + HEADER_SIZE; INSTRUMENT(inactive_memory -= HEADER_SIZE); INSTRUMENT(free_memory += HEADER_SIZE); } Z_PrintStats(); // print memory allocation stats Z_LogPrintf("* Z_Free(p=%p, file=%s:%d)\n", p, file, line); }
// // Z_Malloc // // You can pass a NULL user if the tag is < PU_PURGELEVEL. // void *(Z_Malloc)(size_t size, int tag, void **user, __string file, int line) { register memblock_t __near *block, *start; INSTRUMENT(register size_t size_orig = size); // davidph 12/10/12: If zone not initialized, do so now. if(!zone) (Z_Init)(file, line); DEBUG_CHECKHEAP(); Z_IDCheckNB(IDBOOL(tag >= PU_PURGELEVEL && !user), "Z_Malloc: an owner is required for purgable blocks", file, line); if(!size) return user ? *user = NULL : NULL; // malloc(0) returns NULL size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size block = rover; if(block->prev->tag == PU_FREE) block = block->prev; start = block; // haleyjd 01/01/01 (happy new year!): // the first if() inside the loop below contains cph's memory // purging efficiency fix do { // Free purgable blocks; replacement is roughly FIFO if(block->tag >= PU_PURGELEVEL) { start = block->prev; Z_Free((char __near *)block + HEADER_SIZE); // cph - If start->next == block, we did not merge with the previous // If !=, we did, so we continue from start. // Important: we've reset start! if(start->next == block) start = start->next; else block = start; } if(block->tag == PU_FREE && block->size >= size) // First-fit { size_t extra = block->size - size; if(extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { memblock_t __near *newb = (memblock_t __near *)((char __near *)block + HEADER_SIZE + size); (newb->next = block->next)->prev = newb; (newb->prev = block)->next = newb; // Split up block block->size = size; newb->size = extra - HEADER_SIZE; newb->tag = PU_FREE; INSTRUMENT(inactive_memory += HEADER_SIZE); INSTRUMENT(free_memory -= HEADER_SIZE); } rover = block->next; // set roving pointer for next search #ifdef INSTRUMENTED inactive_memory += block->extra = block->size - size_orig; if(tag >= PU_PURGELEVEL) purgable_memory += size_orig; else active_memory += size_orig; free_memory -= block->size; #endif INSTRUMENT(block->file = file); INSTRUMENT(block->line = line); IDCHECK(block->id = ZONEID);// signature required in block header block->tag = tag; // tag block->user = user; // user block = BlockToVoid(block); if(user) // if there is a user *user = block; // set user to point to new block Z_PrintStats(); // print memory allocation stats // scramble memory -- weed out any bugs SCRAMBLER(block, size); Z_LogPrintf("* %p = Z_Malloc(size=%u, tag=%d, user=%p, source=%s:%d)\n", block, size, tag, user, file, line); return block; } } while((block = block->next) != start); // detect cycles as failure zonef("Z_Malloc: Failure trying to allocate %u bytes\nSource: %s:%d\n", size, file, line); abort(); }