/* Copyright (C) 1994-2004 Revolution Software Ltd * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * $Header$ */ // The new memory manager, now only used by the resource manager. Unlike the // original, this one does not allocate a 12 MB memory pool at startup. // // There is one thing that prevents us from replacing the whole memory manager // with the standard memory allocation functions: Broken Sword II absolutely, // positively needs to be able to encode pointers as 32-bit integers. The // original engine did this simply by casting between pointers and integers, // but as far as I know that's not a very portable thing to do. // // For a while I was hopeing that the engine only needed pointers as function // parameters, in which case I could have extended the stack to use a struct or // a union instead, but no such luck. There is code in walker.cpp that // obviously violates that assumption, and there are probably other, more // well-hidden places, as well. // // This attacks the problem from the direction of another limitation in the // original memory manager: it could only handle up to 999 blocks of memory. // This memory manager has the same limitation, although it's probably way too // large now, which means that a pointer can be encoded as 10 bits to store the // block id, and another 22 bits to store an index into that block. #include "common/stdafx.h" #include "sword2/sword2.h" #include "sword2/console.h" namespace Sword2 { #define MAX_BLOCKS 999 #define Debug_Printf _vm->_debugger->DebugPrintf MemoryManager::MemoryManager(Sword2Engine *vm) : _vm(vm) { _memBlocks = (MemBlock *) malloc(MAX_BLOCKS * sizeof(MemBlock)); _memBlockIndex = (MemBlock **) malloc(MAX_BLOCKS * sizeof(MemBlock *)); _idStack = (int16 *) malloc(MAX_BLOCKS * sizeof(int16)); _totAlloc = 0; _numBlocks = 0; for (int i = 0; i < MAX_BLOCKS; i++) { _idStack[i] = MAX_BLOCKS - i - 1; _memBlocks[i].ptr = NULL; _memBlockIndex[i] = NULL; } _idStackPtr = MAX_BLOCKS; } MemoryManager::~MemoryManager() { for (int i = 0; i < MAX_BLOCKS; i++) free(_memBlocks[i].ptr); free(_memBlocks); free(_memBlockIndex); free(_idStack); } int32 MemoryManager::encodePtr(byte *ptr) { int idx = findPointerInIndex(ptr); if (idx == -1) error("Encoding non-allocated pointer %p", ptr); int id = _memBlockIndex[idx]->id; return (id << 22) | (ptr - _memBlocks[id].ptr); } byte *MemoryManager::decodePtr(int32 n) { int16 id = (n >> 22) & 0x03ff; int32 offset = n & 0x003fffff; return _memBlocks[id].ptr + offset; } int16 MemoryManager::findExactPointerInIndex(byte *ptr) { int left = 0; int right = _numBlocks - 1; while (right >= left) { int n = (left + right) / 2; if (_memBlockIndex[n]->ptr == ptr) return n; if (_memBlockIndex[n]->ptr > ptr) right = n - 1; else left = n + 1; } return -1; } int16 MemoryManager::findPointerInIndex(byte *ptr) { int left = 0; int right = _numBlocks - 1; while (right >= left) { int n = (left + right) / 2; if (_memBlockIndex[n]->ptr <= ptr && _memBlockIndex[n]->ptr + _memBlockIndex[n]->size > ptr) return n; if (_memBlockIndex[n]->ptr > ptr) right = n - 1; else left = n + 1; } return -1; } int16 MemoryManager::findInsertionPointInIndex(byte *ptr) { if (_numBlocks == 0) return 0; int left = 0; int right = _numBlocks - 1; int n = 0; while (right >= left) { n = (left + right) / 2; if (_memBlockIndex[n]->ptr == ptr) return -1; if (_memBlockIndex[n]->ptr > ptr) right = n - 1; else left = n + 1; } if (_memBlockIndex[n]->ptr < ptr) n++; return n; } byte *MemoryManager::memAlloc(uint32 size, int16 uid) { byte *ptr = (byte *) malloc(size); assert(ptr); assert(_idStackPtr > 0); // Get the new block's id from the stack. int16 id = _idStack[--_idStackPtr]; _memBlocks[id].id = id; _memBlocks[id].uid = uid; _memBlocks[id].ptr = ptr; _memBlocks[id].size = size; // The memory index provides a method for figuring out which memory // block an arbitrary pointer points to. A balanced tree might be more // efficient, but such beasts are tricky to implement. int16 idx = findInsertionPointInIndex(ptr); assert(idx != -1); for (int i = _numBlocks; i > idx; i--) _memBlockIndex[i] = _memBlockIndex[i - 1]; _memBlockIndex[idx] = &_memBlocks[id]; _numBlocks++; _totAlloc += size; return _memBlocks[id].ptr; } void MemoryManager::memFree(byte *ptr) { int16 idx = findExactPointerInIndex(ptr); if (idx == -1) { warning("Freeing non-allocated pointer %p", ptr); return; } // Put back the id on the stack _idStack[_idStackPtr++] = _memBlockIndex[idx]->id; free(_memBlockIndex[idx]->ptr); _memBlockIndex[idx]->ptr = NULL; _totAlloc -= _memBlockIndex[idx]->size; // Remove the pointer from the index _numBlocks--; for (int i = idx; i < _numBlocks; i++) _memBlockIndex[i] = _memBlockIndex[i + 1]; } void MemoryManager::memDisplay() { for (int i = 0; i < _numBlocks; i++) Debug_Printf("%d: %ld bytes allocated by resource %d\n", i, _memBlocks[i].size, _memBlocks[i].uid); } void MemoryManager::memStatusStr(char *buf) { if (_totAlloc < 1024) sprintf(buf, "%u bytes in %d memory blocks", _totAlloc, _numBlocks); else if (_totAlloc < 1048576) sprintf(buf, "%uK in %d memory blocks", _totAlloc / 1024, _numBlocks); else sprintf(buf, "%.02fM in %d memory blocks", _totAlloc / 1048576., _numBlocks); } } // End of namespace Sword2