//------------------------------------------------------------------------- /* Copyright (C) 2010-2019 EDuke32 developers and contributors Copyright (C) 2019 Nuke.YKT This file is part of NBlood. NBlood is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //------------------------------------------------------------------------- #include "ns.h" // Must come before everything else! #ifdef USE_QHEAP #include #include #include #include #include "common_game.h" #include "qheap.h" void InstallFenceposts(HEAPNODE *n) { char *address = (char*)n; memset(address + 0x20, 0xcc, 0x10); memset(address + n->size - 0x10, 0xcc, 0x10); } void CheckFenceposts(HEAPNODE *n) { char *data = (char*)n + 0x20; for (int i = 0; i < 0x10; i++) { if (data[i] != 0xcc) { ThrowError("Block underwritten"); } } data = (char*)n + n->size - 0x10; for (int i = 0; i < 0x10; i++) { if (data[i] != 0xcc) { ThrowError("Block overwritten"); } } } QHeap::QHeap(int heapSize) { dassert(heapSize > 0); size = heapSize; while (size > 0 && (heapPtr = malloc(size)) == NULL) { size -= 0x1000; } if (!heapPtr) { ThrowError("Allocation failure\n"); } heap.isFree = false; freeHeap.isFree = false; HEAPNODE *node = (HEAPNODE*)(((intptr_t)heapPtr + 0xf) & ~0xf); heap.next = heap.prev = node; node->next = node->prev = &heap; freeHeap.freeNext = freeHeap.freePrev = node; node->freeNext = node->freePrev = &freeHeap; node->isFree = true; node->size = size & ~0xf; } QHeap::~QHeap(void) { Check(); free(heapPtr); heapPtr = NULL; } void QHeap::Check(void) { HEAPNODE *node = heap.next; while (node != &heap) { if (!node->isFree) { CheckFenceposts(node); } node = node->next; } } void QHeap::Debug(void) { #if 0 char s[4]; FILE *f = fopen("MEMFRAG.TXT", "wt"); if (!f) { return; } HEAPNODE *node = heap.next; while (node != &heap) { if (node->isFree) { fprintf(f, "%P %10d FREE", node, node->size); } else { char *data = (char*)node + 0x20; for (int i = 0; i < 4; i++) { if (isalpha(data[i])) { s[i] = data[i]; } else { s[i] = '_'; } } fprintf(f, "%P %10d %4s", node, node->size, s); } } fclose(f); #endif } void *QHeap::Alloc(int blockSize) { dassert(blockSize > 0); dassert(heapPtr != NULL); if (blockSize > 0) { blockSize = ((blockSize + 0xf) & ~0xf) + 0x40; HEAPNODE *freeNode = freeHeap.freeNext; while (freeNode != &freeHeap) { dassert(freeNode->isFree); if (blockSize <= freeNode->size) { if (blockSize + 0x30 <= freeNode->size) { freeNode->size -= blockSize; HEAPNODE *nextNode = (HEAPNODE *)((char*)freeNode + freeNode->size); nextNode->size = blockSize; nextNode->prev = freeNode; nextNode->next = freeNode->next; nextNode->prev->next = nextNode; nextNode->next->prev = nextNode; nextNode->isFree = false; InstallFenceposts(nextNode); return (void*)((char*)nextNode + 0x30); } else { freeNode->freePrev->freeNext = freeNode->freeNext; freeNode->freeNext->freePrev = freeNode->freePrev; freeNode->isFree = false; InstallFenceposts(freeNode); return (void*)((char*)freeNode + 0x30); } } freeNode = freeNode->freeNext; } } return NULL; } int QHeap::Free(void *p) { if (!p) { return 0; } dassert(heapPtr != NULL); HEAPNODE *node = (HEAPNODE*)((char*)p - 0x30); if (node->isFree) { ThrowError("Free on bad or freed block"); } CheckFenceposts(node); if (node->prev->isFree) { node->prev->size += node->size; node->prev->next = node->next; node->next->prev = node->prev; node = node->prev; } else { node->freeNext = freeHeap.freeNext; node->freePrev = &freeHeap; node->freePrev->freeNext = node; node->freeNext->freePrev = node; node->isFree = true; } HEAPNODE *nextNode = node->next; if (nextNode->isFree) { node->size += nextNode->size; nextNode->freePrev->freeNext = nextNode->freeNext; nextNode->freeNext->freePrev = nextNode->freePrev; nextNode->prev->next = nextNode->next; nextNode->next->prev = nextNode->prev; } return node->size - 0x40; } #endif