// // Z_FreeTags // void (Z_FreeTags)(int lowtag, int hightag, const char *file, int line) { memblock_t *block; // haleyjd 03/30/2011: delete ZoneObjects of the same tags as well ZoneObject::FreeTags(lowtag, hightag); if(lowtag <= PU_FREE) lowtag = PU_FREE+1; if(hightag > PU_CACHE) hightag = PU_CACHE; for(; lowtag <= hightag; ++lowtag) { for(block = blockbytag[lowtag], blockbytag[lowtag] = NULL; block;) { memblock_t *next = block->next; Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_FreeTags: Changed a tag without ZONEID", block, file, line); (Z_Free)((byte *)block + header_size, file, line); block = next; // Advance to next block } } Z_LogPrintf("* Z_FreeTags(lowtag=%d, hightag=%d, file=%s:%d)\n", lowtag, hightag, file, line); }
void (Z_CheckHeap)(const char *file, int line) { memblock_t *block; memblock_t *prev; int i; // // Check all chains // for(i = 0; i < PU_MAX; ++i) { prev = NULL; for(block = allocated_blocks[i]; block != NULL; block = block->next) { if(block->id != ZONEID) { I_Error("Z_CheckHeap: Block without a ZONEID! (%s:%d)", file, line); } if(block->prev != prev) { I_Error("Z_CheckHeap: Doubly-linked list corrupted! (%s:%d)", file, line); } prev = block; } } #ifdef ZONEFILE Z_LogPrintf("* Z_CheckHeap(file=%s:%d)\n", file, line); #endif }
// // Z_FreeTags // void (Z_FreeTags)(int lowtag, int hightag, __string file, int line) { register memblock_t __near *block = zone; if(lowtag <= PU_FREE) lowtag = (int)PU_FREE + 1; // haleyjd: code inside this do loop has been updated with // cph's fix for memory wastage do // Scan through list, searching for tags in range { if(block->tag >= lowtag && block->tag <= hightag) { register memblock_t __near *prev = block->prev, *cur = block; (Z_Free)((char __near *)block + HEADER_SIZE, file, line); /* cph - be more careful here, we were skipping blocks! * If the current block was not merged with the previous, * cur is still a valid pointer, prev->next == cur, and cur is * already free so skip to the next. * If the current block was merged with the previous, * the next block to analyse is prev->next. * Note that the while() below does the actual step forward */ block = (prev->next == cur) ? cur : prev; } } while((block = block->next) != zone); Z_LogPrintf("* Z_FreeTags(lowtag=%d, hightag=%d, file=%s:%d)\n", lowtag, hightag, file, line); }
// // Z_ChangeTag // void (Z_ChangeTag)(void *ptr, int tag, __string file, int line) { register memblock_t __near *block = VoidToBlock(ptr); DEBUG_CHECKHEAP(); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_ChangeTag: Changed a tag without ZONEID", block, file, line); Z_IDCheck(IDBOOL(tag >= PU_PURGELEVEL && !block->user), "Z_ChangeTag: an owner is required for purgable blocks", block, file, line); #ifdef INSTRUMENTED if(block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL) { active_memory -= block->size - block->extra; purgable_memory += block->size - block->extra; } else if(block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL) { active_memory += block->size - block->extra; purgable_memory -= block->size - block->extra; } #endif block->tag = tag; Z_LogPrintf("* Z_ChangeTag(p=%p, tag=%d, file=%s:%d)\n", ptr, tag, file, line); }
// // Z_Init // void (Z_Init)(__string file, int line) { register size_t size; // Allocate the memory // davidph 06/17/12: For DS, memory is a static allocation. zonebase = (memblock_t __near *)heap; zonebase_size = HEAPSIZE; // Align on cache boundary // davidph 08/03/12 FIXME: This causes invalid codegen. //zone = (memblock_t *)(((uintptr_t)zonebase + (CACHE_ALIGN - 1)) & ~(CACHE_ALIGN - 1)); zone = zonebase; size = zonebase_size - ((char __near *)zone - (char __near *)zonebase); rover = zone; // Rover points to base of zone mem zone->next = zone->prev = zone; // Single node zone->size = size - HEADER_SIZE; // All memory in one block zone->tag = PU_FREE; // A free block #ifdef ZONEIDCHECK zone->id = 0; #endif INSTRUMENT(free_memory = zone->size); INSTRUMENT(inactive_memory = zonebase_size - zone->size); INSTRUMENT(active_memory = purgable_memory = 0); Z_OpenLogFile(); Z_LogPrintf("Initialized zone heap with size of %u bytes (zonebase = %p)\n", zonebase_size, (void *)zonebase); }
// // Z_CheckHeap // void (Z_CheckHeap)(__string file, int line) { register memblock_t __near *block = zone; // Start at base of zone mem do // Consistency check (last node treated special) { if((block->next != zone && (memblock_t __near *)((char __near *)block + HEADER_SIZE + block->size) != block->next) || block->next->prev != block || block->prev->next != block) { zonef("Z_CheckHeap: Block size does not touch the next block\nSource: %s:%d\n" #ifdef INSTRUMENTED "Source of offending block: %s:%d\n" , file, line, block->file, block->line #else , file, line #endif ); abort(); } } while((block = block->next) != zone); #ifndef CHECKHEAP Z_LogPrintf("* Z_CheckHeap(file=%s:%d)\n", file, line); #endif }
char *(Z_Strdup)(const char *s, int tag, void *user, const char *file, int line) { #ifdef ZONEFILE Z_LogPrintf("* Z_Strdup(file=%s:%d)\n", file, line); #endif return strcpy((Z_Malloc)(strlen(s)+1, tag, user, file, line), s); }
void *(Z_Calloc)(int n1, int tag, void *user, const char *file, int line) { #ifdef ZONEFILE Z_LogPrintf("* Z_Calloc(file=%s:%d)\n", file, line); #endif return memset((Z_Malloc)(n1, tag, user, file, line), 0, n1); }
void (Z_ChangeTag)(void *ptr, int tag, const char *file, int line) { memblock_t* block; block = (memblock_t*)((byte *)ptr - sizeof(memblock_t)); if(block->id != ZONEID) I_Error("Z_ChangeTag: block without a ZONEID! (%s:%d)", file, line); if(tag >= PU_PURGELEVEL && block->user == NULL) { I_Error("Z_ChangeTag: an owner is required for purgable blocks (%s:%d)", file, line); } // // Remove the block from its current list, and rehook it into // its new list. // Z_RemoveBlock(block); block->tag = tag; Z_InsertBlock(block); #ifdef ZONEFILE Z_LogPrintf("* Z_ChangeTag(ptr=%p, tag=%d, file=%s:%d)\n", ptr, tag, file, line); #endif }
// // Z_Malloc // // You can pass a NULL user if the tag is < PU_PURGELEVEL. // void *(Z_Malloc)(size_t size, int tag, void **user, const char *file, int line) { memblock_t *block; byte *ret; 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 if(!(block = (memblock_t *)(malloc(size + header_size)))) { if(blockbytag[PU_CACHE]) { Z_FreeTags(PU_CACHE, PU_CACHE); block = (memblock_t *)(malloc(size + header_size)); } } if(!block) { I_FatalError(I_ERR_KILL, "Z_Malloc: Failure trying to allocate %u bytes\n" "Source: %s:%d\n", (unsigned int)size, file, line); } block->size = size; if((block->next = blockbytag[tag])) block->next->prev = &block->next; blockbytag[tag] = block; block->prev = &blockbytag[tag]; INSTRUMENT(memorybytag[tag] += block->size); 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 ret = ((byte *) block + header_size); if(user) // if there is a user *user = ret; // set user to point to new block // scramble memory -- weed out any bugs SCRAMBLER(ret, size); Z_LogPrintf("* %p = Z_Malloc(size=%lu, tag=%d, user=%p, source=%s:%d)\n", ret, size, tag, user, file, line); return ret; }
void Z_Init(void) { dmemset(allocated_blocks, 0, sizeof(allocated_blocks)); #ifdef ZONEFILE atexit(Z_CloseLogFile); // exit handler Z_OpenLogFile(); Z_LogPrintf("* Z_Init\n"); #endif }
void (Z_Touch)(void *ptr, const char *file, int line) { memblock_t *block; block = (memblock_t*)((byte*)ptr - sizeof(memblock_t)); if(block->id != ZONEID) { I_Error("Z_Touch: touched a pointer without ZONEID (%s:%d)", file, line); } #ifdef ZONEFILE Z_LogPrintf("* Z_Touch(ptr=%p, file=%s:%d)\n", ptr, file, line); #endif }
void *(Z_Malloc) (int size, int tag, void *user, const char *file, int line) { memblock_t *newblock; unsigned char *data; void *result; if (tag < 0 || tag >= PU_MAX) I_Error("Z_Malloc: tag out of range: %i (%s:%d)", tag, file, line); if (user == NULL && tag >= PU_PURGELEVEL) I_Error ("Z_Malloc: an owner is required for purgable blocks (%s:%d)", file, line); // Malloc a block of the required size newblock = NULL; if (!(newblock = (memblock_t *) malloc(sizeof(memblock_t) + size))) { if (Z_ClearCache(sizeof(memblock_t) + size)) newblock = (memblock_t *) malloc(sizeof(memblock_t) + size); } if (!newblock) I_Error("Z_Malloc: failed on allocation of %u bytes (%s:%d)", size, file, line); // Hook into the linked list for this tag type newblock->tag = tag; newblock->id = ZONEID; newblock->user = user; newblock->size = size; Z_InsertBlock(newblock); data = (unsigned char *)newblock; result = data + sizeof(memblock_t); if (user != NULL) *newblock->user = result; #ifdef ZONEFILE Z_LogPrintf ("* %p = Z_Malloc(size=%lu, tag=%d, user=%p, source=%s:%d)\n", result, size, tag, user, file, line); #endif return result; }
// // Z_Free // void (Z_Free)(void *p, const char *file, int line) { DEBUG_CHECKHEAP(); if(p) { memblock_t *block = (memblock_t *)((byte *) p - header_size); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_Free: freed a pointer without ZONEID", block, file, line); // haleyjd: permanent blocks are never freed even if the code tries. if(block->tag == PU_PERMANENT) return; 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) { I_FatalError(I_ERR_KILL, "Z_Free: freed a pointer with invalid tag %d\n" "Source: %s:%d\n" #if defined(ZONEVERBOSE) && defined(INSTRUMENTED) "Source of malloc: %s:%d\n" , block->tag, file, line, block->file, block->line #else , block->tag, file, line #endif ); } INSTRUMENT(memorybytag[block->tag] -= block->size); block->tag = PU_FREE; // Mark block freed // scramble memory -- weed out any bugs SCRAMBLER(p, block->size); if(block->user) // Nullify user if one exists *block->user = NULL; if((*block->prev = block->next)) block->next->prev = block->prev; free(block); Z_LogPrintf("* Z_Free(p=%p, file=%s:%d)\n", p, file, line); } }
// // Z_Alloca // // haleyjd 12/06/06: // Implements a portable garbage-collected alloca on the zone heap. // void *(Z_Alloca)(size_t n, const char *file, int line) { void *ptr; if(n == 0) return NULL; // allocate it ptr = (Z_Calloc)(n, 1, PU_AUTO, NULL, file, line); Z_LogPrintf("* %p = Z_Alloca(n = %lu, file = %s, line = %d)\n", ptr, n, file, line); return ptr; }
// // Z_Realloc // // haleyjd 05/29/08: *Something* is wrong with my Z_Realloc routine, and I // cannot figure out what! So we're back to using Old Faithful for now. // // davidph 12/09/12: You fool, you threw away gold! Gold, I say! // void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user, __string file, int line) { register void *p = (Z_Malloc)(n, tag, user, file, line); if(ptr) { memblock_t *block = VoidToBlock(ptr); if(p) // haleyjd 09/18/06: allow to return NULL without crashing memcpy_near((void __near *p, (void __near *)ptr, n <= block->size ? n : block->size); (Z_Free)(ptr, file, line); if(user) // in case Z_Free nullified same user *user=p; } Z_LogPrintf("* %p = Z_Realloc(ptr=%p, n=%u, tag=%d, user=%p, source=%s:%d)\n", p, ptr, n, tag, user, file, line); return p; }
// // Z_ChangeTag // void (Z_ChangeTag)(void *ptr, int tag, const char *file, int line) { memblock_t *block; DEBUG_CHECKHEAP(); if(!ptr) { I_FatalError(I_ERR_KILL, "Z_ChangeTag: can't change a NULL pointer at %s:%d\n", file, line); } block = (memblock_t *)((byte *) ptr - header_size); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_ChangeTag: Changed a tag without ZONEID", block, file, line); // haleyjd: permanent blocks are not re-tagged even if the code tries. if(block->tag == PU_PERMANENT) return; Z_IDCheck(IDBOOL(tag >= PU_PURGELEVEL && !block->user), "Z_ChangeTag: an owner is required for purgable blocks", block, file, line); if((*block->prev = block->next)) block->next->prev = block->prev; if((block->next = blockbytag[tag])) block->next->prev = &block->next; block->prev = &blockbytag[tag]; blockbytag[tag] = block; INSTRUMENT(memorybytag[block->tag] -= block->size); INSTRUMENT(memorybytag[tag] += block->size); block->tag = tag; Z_LogPrintf("* Z_ChangeTag(p=%p, tag=%d, file=%s:%d)\n", ptr, tag, file, line); }
// // Z_CheckHeap // void (Z_CheckHeap)(const char *file, int line) { #ifdef ZONEIDCHECK memblock_t *block; int lowtag; for(lowtag = PU_FREE+1; lowtag < PU_MAX; ++lowtag) { for(block = blockbytag[lowtag]; block; block = block->next) { Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_CheckHeap: Block found without ZONEID", block, file, line); } } #endif #ifndef CHECKHEAP Z_LogPrintf("* Z_CheckHeap(file=%s:%d)\n", file, line); #endif }
// // Z_Alloca // // haleyjd 12/06/06: // Implements a portable garbage-collected alloca on the zone heap. // void *(Z_Alloca)(size_t n, __string file, int line) { register alloca_header_t __near *hdr; register void *ptr; if(n == 0) return NULL; // add an alloca_header_t to the requested allocation size ptr = (Z_Malloc)(n + sizeof(alloca_header_t), PU_STATIC, NULL, file, line); Z_LogPrintf("* %p = Z_Alloca(n = %u, file = %s, line = %d)\n", ptr, n, file, line); // add to linked list hdr = (alloca_header_t __near *)ptr; hdr->next = alloca_root; alloca_root = hdr; // return a pointer to the actual allocation return (void *)((char __near *)ptr + sizeof(alloca_header_t)); }
void (Z_Free) (void *ptr, const char *file, int line) { memblock_t *block; block = (memblock_t *) ((byte *) ptr - sizeof(memblock_t)); if (block->id != ZONEID) I_Error("Z_Free: freed a pointer without ZONEID (%s:%d)", file, line); // clear the user's mark if (block->user != NULL) *block->user = NULL; Z_RemoveBlock(block); // Free back to system free(block); #ifdef ZONEFILE Z_LogPrintf("* Z_Free(ptr=%p, file=%s:%d)\n", ptr, file, line); #endif }
void (Z_FreeTags)(int lowtag, int hightag, const char *file, int line) { int i; for(i = lowtag; i <= hightag; ++i) { memblock_t *block; memblock_t *next; // Free all in this chain for(block = allocated_blocks[i]; block != NULL;) { next = block->next; if(block->id != ZONEID) { I_Error("Z_FreeTags: Changed a tag without ZONEID (%s:%d)", file, line); } // Free this block if(block->user != NULL) { *block->user = NULL; } free(block); // Jump to the next in the chain block = next; } // This chain is empty now allocated_blocks[i] = NULL; } #ifdef ZONEFILE Z_LogPrintf("* Z_FreeTags(lowtag=%d, hightag=%d, file=%s:%d)\n", lowtag, hightag, file, line); #endif }
// // Z_Realloca // // haleyjd 07/08/10: realloc for automatic allocations. // void *(Z_Realloca)(void *ptr, size_t n, const char *file, int line) { void *ret; if(ptr) { // get zone block memblock_t *block = (memblock_t *)((byte *)ptr - header_size); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_Realloca: block found without ZONEID", block, file, line); if(block->tag != PU_AUTO) I_FatalError(I_ERR_KILL, "Z_Realloca: strange block tag %d\n", block->tag); } ret = (Z_Realloc)(ptr, n, PU_AUTO, NULL, file, line); Z_LogPrintf("* %p = Z_Realloca(ptr = %p, n = %lu, file = %s, line = %d)\n", ret, ptr, n, file, line); return ret; }
char *(Z_Strdupa)(const char *s, const char *file, int line) { #ifdef ZONEFILE Z_LogPrintf("* Z_Strdupa(file=%s:%d)\n", file, line); #endif return dstrcpy((Z_Alloca)(strlen(s)+1, file, line), s); }
// // 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_Realloc // // For the native heap, this can easily behave as a real realloc, and not // just an ignorant copy-and-free. // void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user, const char *file, int line) { void *p; memblock_t *block, *newblock, *origblock; // if not allocated at all, defer to Z_Malloc if(!ptr) return (Z_Malloc)(n, tag, user, file, line); // size == 0 is a special case that cannot be handled below if(n == 0) { (Z_Free)(ptr, file, line); return NULL; } DEBUG_CHECKHEAP(); block = origblock = (memblock_t *)((byte *)ptr - header_size); Z_IDCheck(IDBOOL(block->id != ZONEID), "Z_Realloc: Reallocated a block without ZONEID\n", block, file, line); // haleyjd: realloc cannot change the tag of a permanent block if(block->tag == PU_PERMANENT) tag = PU_PERMANENT; // nullify current user, if any if(block->user) *(block->user) = NULL; // detach from list before reallocation if((*block->prev = block->next)) block->next->prev = block->prev; block->next = NULL; block->prev = NULL; INSTRUMENT(memorybytag[block->tag] -= block->size); if(!(newblock = (memblock_t *)(realloc(block, n + header_size)))) { // haleyjd 07/09/10: Note that unlinking the block above makes this safe // even if the current block is PU_CACHE; Z_FreeTags won't find it. if(blockbytag[PU_CACHE]) { Z_FreeTags(PU_CACHE, PU_CACHE); newblock = (memblock_t *)(realloc(block, n + header_size)); } } if(!(block = newblock)) { if(origblock->size >= n) { block = origblock; // restore original block if size was equal or smaller n = block->size; // keep same size in this event } else { I_FatalError(I_ERR_KILL, "Z_Realloc: Failure trying to allocate %u bytes\n" "Source: %s:%d\n", (unsigned int)n, file, line); } } block->size = n; block->tag = tag; p = (byte *)block + header_size; // set new user, if any block->user = user; if(user) *user = p; // reattach to list at possibly new address, new tag if((block->next = blockbytag[tag])) block->next->prev = &block->next; blockbytag[tag] = block; block->prev = &blockbytag[tag]; INSTRUMENT(memorybytag[tag] += block->size); INSTRUMENT(block->file = file); INSTRUMENT(block->line = line); Z_LogPrintf("* %p = Z_Realloc(ptr=%p, n=%lu, tag=%d, user=%p, source=%s:%d)\n", p, ptr, n, tag, user, file, line); return p; }
void *(Z_Realloc)(void *ptr, int size, int tag, void *user, const char *file, int line) { memblock_t *block; memblock_t *newblock; unsigned char *data; void *result; if(!ptr) { return (Z_Malloc)(size, tag, user, file, line); } if(size == 0) { (Z_Free)(ptr, file, line); return NULL; } if(tag < 0 || tag >= PU_MAX) { I_Error("Z_Realloc: tag out of range: %i (%s:%d)", tag, file, line); } if(user == NULL && tag >= PU_PURGELEVEL) { I_Error("Z_Realloc: an owner is required for purgable blocks (%s:%d)", file, line); } block = (memblock_t*)((byte *)ptr - sizeof(memblock_t)); newblock = NULL; if(block->id != ZONEID) { I_Error("Z_Realloc: Reallocated a pointer without ZONEID (%s:%d)", file, line); } Z_RemoveBlock(block); block->next = NULL; block->prev = NULL; if(block->user) { *block->user = NULL; } if(!(newblock = (memblock_t*)realloc(block, sizeof(memblock_t) + size))) { if(Z_ClearCache(sizeof(memblock_t) + size)) { newblock = (memblock_t*)realloc(block, sizeof(memblock_t) + size); } } if(!newblock) { I_Error("Z_Realloc: failed on allocation of %u bytes (%s:%d)", size, file, line); } newblock->tag = tag; newblock->id = ZONEID; newblock->user = user; newblock->size = size; Z_InsertBlock(newblock); data = (unsigned char*)newblock; result = data + sizeof(memblock_t); if(user != NULL) { *newblock->user = result; } #ifdef ZONEFILE Z_LogPrintf("* %p = Z_Realloc(ptr=%p, n=%lu, tag=%d, user=%p, source=%s:%d)\n", result, ptr, size, tag, user, file, line); #endif return result; }
void (Z_FreeAlloca)(const char *file, int line) { #ifdef ZONEFILE Z_LogPrintf("* Z_FreeAlloca(file=%s:%d)\n", file, line); #endif (Z_FreeTags)(PU_AUTO, PU_AUTO, file, line); }
void *(Z_Alloca)(int n, const char *file, int line) { #ifdef ZONEFILE Z_LogPrintf("* Z_Alloca(file=%s:%d)\n", file, line); #endif return n == 0 ? NULL : (Z_Calloc)(n, PU_AUTO, NULL, file, line); }
void Z_Init(void) { atexit(Z_Close); // exit handler Z_LogPrintf("Initialized zone heap (using native implementation)\n"); }