mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-20 20:29:10 +00:00
Hoist MOULOpenSourceClientPlugin/Plasma20/* to top level
to match H'uru layout and make patching/cherry-picking easier.
This commit is contained in:
874
Sources/Plasma/CoreLib/hsMemory.cpp
Normal file
874
Sources/Plasma/CoreLib/hsMemory.cpp
Normal file
@ -0,0 +1,874 @@
|
||||
/*==LICENSE==*
|
||||
|
||||
CyanWorlds.com Engine - MMOG client, server and tools
|
||||
Copyright (C) 2011 Cyan Worlds, Inc.
|
||||
|
||||
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 3 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Additional permissions under GNU GPL version 3 section 7
|
||||
|
||||
If you modify this Program, or any covered work, by linking or
|
||||
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
||||
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
||||
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
||||
(or a modified version of those libraries),
|
||||
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
||||
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
||||
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
||||
licensors of this Program grant you additional
|
||||
permission to convey the resulting work. Corresponding Source for a
|
||||
non-source form of such a combination shall include the source code for
|
||||
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
||||
work.
|
||||
|
||||
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
||||
or by snail mail at:
|
||||
Cyan Worlds, Inc.
|
||||
14617 N Newport Hwy
|
||||
Mead, WA 99021
|
||||
|
||||
*==LICENSE==*/
|
||||
#if HS_BUILD_FOR_MAC
|
||||
#include <Memory.h>
|
||||
#else
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
#include "hsMemory.h"
|
||||
#include "hsExceptions.h"
|
||||
|
||||
#define DO_MEMORY_REPORTS // dumps memory reports upon start up of engine
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if HS_BUILD_FOR_MAC
|
||||
void HSMemory::BlockMove(const void* src, void* dst, UInt32 length)
|
||||
{
|
||||
::BlockMoveData(src, dst, length);
|
||||
}
|
||||
#else
|
||||
void HSMemory::BlockMove(const void* src, void* dst, UInt32 length)
|
||||
{
|
||||
memmove(dst, src, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
hsBool HSMemory::EqualBlocks(const void* block1, const void* block2, UInt32 length)
|
||||
{
|
||||
const Byte* byte1 = (Byte*)block1;
|
||||
const Byte* byte2 = (Byte*)block2;
|
||||
|
||||
while (length--)
|
||||
if (*byte1++ != *byte2++)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void* HSMemory::New(UInt32 size)
|
||||
{
|
||||
return TRACKED_NEW UInt32[(size + 3) >> 2];
|
||||
}
|
||||
|
||||
void HSMemory::Delete(void* block)
|
||||
{
|
||||
delete[] (UInt32*)block;
|
||||
}
|
||||
|
||||
void* HSMemory::Copy(UInt32 length, const void* source)
|
||||
{
|
||||
void* destination = HSMemory::New(length);
|
||||
|
||||
HSMemory::BlockMove(source, destination, length);
|
||||
return destination;
|
||||
}
|
||||
|
||||
void HSMemory::Clear(void* m, UInt32 byteLen)
|
||||
{
|
||||
UInt8* mem = (UInt8*)m;
|
||||
UInt8* memStop = mem + byteLen;
|
||||
|
||||
if (byteLen > 8)
|
||||
{ while (UInt32(mem) & 3)
|
||||
*mem++ = 0;
|
||||
|
||||
UInt32* mem32 = (UInt32*)mem;
|
||||
UInt32* mem32Stop = (UInt32*)(UInt32(memStop) & ~3);
|
||||
do {
|
||||
*mem32++ = 0;
|
||||
} while (mem32 < mem32Stop);
|
||||
|
||||
mem = (UInt8*)mem32;
|
||||
// fall through to finish any remaining bytes (0..3)
|
||||
}
|
||||
while (mem < memStop)
|
||||
*mem++ = 0;
|
||||
|
||||
hsAssert(mem == memStop, "oops");
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if 0
|
||||
template <class T> T* hsSoftNew(T*& obj)
|
||||
{
|
||||
try {
|
||||
obj = TRACKED_NEW T;
|
||||
}
|
||||
catch (...) {
|
||||
obj = nil;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
inline template <class T> T* hsSoftNew(T*& obj, unsigned count)
|
||||
{
|
||||
try {
|
||||
obj = TRACKED_NEW T[count];
|
||||
}
|
||||
catch (...) {
|
||||
obj = nil;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
#endif
|
||||
|
||||
void* HSMemory::SoftNew(UInt32 size)
|
||||
{
|
||||
UInt32* p;
|
||||
|
||||
hsTry {
|
||||
p = TRACKED_NEW UInt32[(size + 3) >> 2];
|
||||
} hsCatch(...) {
|
||||
p = nil;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct hsPrivateChunk {
|
||||
hsPrivateChunk* fNext;
|
||||
char* fAvailableAddr;
|
||||
UInt32 fAvailableSize;
|
||||
|
||||
hsDebugCode(UInt32 fSize;)
|
||||
hsDebugCode(UInt32 fCount;)
|
||||
|
||||
static hsPrivateChunk* NewPrivateChunk(hsPrivateChunk* next, UInt32 chunkSize);
|
||||
};
|
||||
|
||||
hsPrivateChunk* hsPrivateChunk::NewPrivateChunk(hsPrivateChunk* next, UInt32 chunkSize)
|
||||
{
|
||||
hsPrivateChunk* chunk = (hsPrivateChunk*)HSMemory::New(sizeof(hsPrivateChunk) + chunkSize);
|
||||
|
||||
chunk->fNext = next;
|
||||
chunk->fAvailableAddr = (char*)chunk + sizeof(hsPrivateChunk);
|
||||
chunk->fAvailableSize = chunkSize;
|
||||
hsDebugCode(chunk->fSize = chunkSize;)
|
||||
hsDebugCode(chunk->fCount = 0;)
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
hsChunkAllocator::hsChunkAllocator(UInt32 chunkSize) : fChunkSize(chunkSize), fChunk(nil)
|
||||
{
|
||||
hsDebugCode(fChunkCount = 0;)
|
||||
}
|
||||
|
||||
hsChunkAllocator::~hsChunkAllocator()
|
||||
{
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
void hsChunkAllocator::Reset()
|
||||
{
|
||||
hsPrivateChunk* chunk = fChunk;
|
||||
|
||||
while (chunk)
|
||||
{ hsPrivateChunk* next = chunk->fNext;
|
||||
HSMemory::Delete(chunk);
|
||||
chunk = next;
|
||||
}
|
||||
fChunk = nil;
|
||||
hsDebugCode(fChunkCount = 0;)
|
||||
}
|
||||
|
||||
void hsChunkAllocator::SetChunkSize(UInt32 chunkSize)
|
||||
{
|
||||
fChunkSize = chunkSize;
|
||||
}
|
||||
|
||||
void* hsChunkAllocator::Allocate(UInt32 size, const void* data)
|
||||
{
|
||||
void* addr;
|
||||
|
||||
if (fChunk == nil || fChunk->fAvailableSize < size)
|
||||
{ if (size > fChunkSize)
|
||||
fChunkSize = size;
|
||||
fChunk = hsPrivateChunk::NewPrivateChunk(fChunk, fChunkSize);
|
||||
hsDebugCode(fChunkCount += 1;)
|
||||
}
|
||||
|
||||
addr = fChunk->fAvailableAddr;
|
||||
fChunk->fAvailableAddr += size;
|
||||
fChunk->fAvailableSize -= size;
|
||||
hsDebugCode(fChunk->fCount += 1;)
|
||||
|
||||
if (data)
|
||||
HSMemory::BlockMove(data, addr, size);
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
void* hsChunkAllocator::SoftAllocate(UInt32 size, const void* data)
|
||||
{
|
||||
void* addr;
|
||||
|
||||
hsTry {
|
||||
addr = this->Allocate(size, data);
|
||||
}
|
||||
hsCatch(...) {
|
||||
addr = nil;
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct hsAppenderHead {
|
||||
struct hsAppenderHead* fNext;
|
||||
struct hsAppenderHead* fPrev;
|
||||
void* fFirst;
|
||||
void* fStop;
|
||||
void* fBottom;
|
||||
|
||||
void* GetTop() const { return (char*)this + sizeof(*this); }
|
||||
void* GetBottom() const { return fBottom; }
|
||||
void* GetStop() const { return fStop; }
|
||||
|
||||
void* GetFirst() const { return fFirst; }
|
||||
void* GetLast(UInt32 elemSize) const { return (char*)fStop - elemSize; }
|
||||
UInt32 GetSize() const { return (char*)fStop - (char*)fFirst; }
|
||||
|
||||
hsBool CanPrepend() const { return fFirst != this->GetTop(); }
|
||||
int PrependSize() const { return (char*)fFirst - (char*)this->GetTop(); }
|
||||
hsBool CanAppend() const { return fStop != this->GetBottom(); }
|
||||
int AppendSize() const { return (char*)this->GetBottom() - (char*)fStop; }
|
||||
|
||||
void* Prepend(UInt32 elemSize)
|
||||
{
|
||||
hsAssert(this->CanPrepend(), "bad prepend");
|
||||
fFirst = (char*)fFirst - elemSize;
|
||||
hsAssert((char*)fFirst >= (char*)this->GetTop(), "bad elemSize");
|
||||
return fFirst;
|
||||
}
|
||||
void* Append(UInt32 elemSize)
|
||||
{
|
||||
hsAssert(this->CanAppend(), "bad append");
|
||||
void* data = fStop;
|
||||
fStop = (char*)fStop + elemSize;
|
||||
hsAssert((char*)fStop <= (char*)fBottom, "bad elemSize");
|
||||
return data;
|
||||
}
|
||||
hsBool PopHead(UInt32 elemSize, void* data)
|
||||
{
|
||||
hsAssert(fFirst != fStop, "Empty");
|
||||
if( data )
|
||||
HSMemory::BlockMove(fFirst, data, elemSize);
|
||||
fFirst = (char*)fFirst + elemSize;
|
||||
return fFirst == fStop;
|
||||
}
|
||||
hsBool PopTail(UInt32 elemSize, void* data)
|
||||
{
|
||||
hsAssert(fFirst != fStop, "Empty");
|
||||
fStop = (char*)fStop - elemSize;
|
||||
if( data )
|
||||
HSMemory::BlockMove(fStop, data, elemSize);
|
||||
return fFirst == fStop;
|
||||
}
|
||||
|
||||
static hsAppenderHead* NewAppend(UInt32 elemSize, UInt32 elemCount, hsAppenderHead* prev)
|
||||
{
|
||||
UInt32 dataSize = elemSize * elemCount;
|
||||
hsAppenderHead* head = (hsAppenderHead*)HSMemory::New(sizeof(hsAppenderHead) + dataSize);
|
||||
|
||||
head->fNext = nil;
|
||||
head->fPrev = prev;
|
||||
head->fFirst = head->GetTop();
|
||||
head->fStop = head->fFirst;
|
||||
head->fBottom = (char*)head->fFirst + dataSize;
|
||||
return head;
|
||||
}
|
||||
static hsAppenderHead* NewPrepend(UInt32 elemSize, UInt32 elemCount, hsAppenderHead* next)
|
||||
{
|
||||
UInt32 dataSize = elemSize * elemCount;
|
||||
hsAppenderHead* head = (hsAppenderHead*)HSMemory::New(sizeof(hsAppenderHead) + dataSize);
|
||||
|
||||
head->fNext = next;
|
||||
head->fPrev = nil;
|
||||
head->fBottom = (char*)head->GetTop() + dataSize;
|
||||
head->fFirst = head->fBottom;
|
||||
head->fStop = head->fBottom;
|
||||
return head;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
hsAppender::hsAppender(UInt32 elemSize, UInt32 elemCount)
|
||||
: fFirstBlock(nil), fElemSize(elemSize), fElemCount(elemCount), fCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
hsAppender::~hsAppender()
|
||||
{
|
||||
this->Reset();
|
||||
}
|
||||
|
||||
UInt32 hsAppender::CopyInto(void* data) const
|
||||
{
|
||||
if (data)
|
||||
{ const hsAppenderHead* head = fFirstBlock;
|
||||
hsDebugCode(UInt32 totalSize = 0;)
|
||||
|
||||
while (head != nil)
|
||||
{ UInt32 size = head->GetSize();
|
||||
HSMemory::BlockMove(head->GetFirst(), data, size);
|
||||
|
||||
data = (char*)data + size;
|
||||
head = head->fNext;
|
||||
hsDebugCode(totalSize += size;)
|
||||
}
|
||||
hsAssert(totalSize == fCount * fElemSize, "bad size");
|
||||
}
|
||||
return fCount * fElemSize;
|
||||
}
|
||||
|
||||
void hsAppender::Reset()
|
||||
{
|
||||
hsAppenderHead* head = fFirstBlock;
|
||||
|
||||
while (head != nil)
|
||||
{ hsAppenderHead* next = head->fNext;
|
||||
HSMemory::Delete(head);
|
||||
head = next;
|
||||
}
|
||||
|
||||
fCount = 0;
|
||||
fFirstBlock = nil;
|
||||
fLastBlock = nil;
|
||||
}
|
||||
|
||||
void* hsAppender::PushHead()
|
||||
{
|
||||
if (fFirstBlock == nil)
|
||||
{ fFirstBlock = hsAppenderHead::NewPrepend(fElemSize, fElemCount, nil);
|
||||
fLastBlock = fFirstBlock;
|
||||
}
|
||||
else if (fFirstBlock->CanPrepend() == false)
|
||||
fFirstBlock = hsAppenderHead::NewPrepend(fElemSize, fElemCount, fFirstBlock);
|
||||
|
||||
fCount += 1;
|
||||
return fFirstBlock->Prepend(fElemSize);
|
||||
}
|
||||
|
||||
void hsAppender::PushHead(const void* data)
|
||||
{
|
||||
void* addr = this->PushHead();
|
||||
if (data)
|
||||
HSMemory::BlockMove(data, addr, fElemSize);
|
||||
}
|
||||
|
||||
void* hsAppender::PeekHead() const
|
||||
{
|
||||
if (fFirstBlock)
|
||||
return (char*)fFirstBlock->fFirst;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
hsBool hsAppender::PopHead(void* data)
|
||||
{
|
||||
if (fCount == 0)
|
||||
return false;
|
||||
|
||||
fCount -= 1;
|
||||
|
||||
if (fFirstBlock->PopHead(fElemSize, data))
|
||||
{ hsAppenderHead* next = fFirstBlock->fNext;
|
||||
if (next)
|
||||
next->fPrev = nil;
|
||||
HSMemory::Delete(fFirstBlock);
|
||||
fFirstBlock = next;
|
||||
if (next == nil)
|
||||
fLastBlock = nil;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int hsAppender::PopHead(int count, void* data)
|
||||
{
|
||||
hsThrowIfBadParam(count >= 0);
|
||||
|
||||
int sizeNeeded = count * fElemSize;
|
||||
int origCount = fCount;
|
||||
|
||||
while (fCount > 0)
|
||||
{ int size = fFirstBlock->GetSize();
|
||||
if (size > sizeNeeded)
|
||||
size = sizeNeeded;
|
||||
|
||||
if (fFirstBlock->PopHead(size, data))
|
||||
{ hsAppenderHead* next = fFirstBlock->fNext;
|
||||
if (next)
|
||||
next->fPrev = nil;
|
||||
HSMemory::Delete(fFirstBlock);
|
||||
fFirstBlock = next;
|
||||
if (next == nil)
|
||||
fLastBlock = nil;
|
||||
}
|
||||
|
||||
if (data)
|
||||
data = (void*)((char*)data + size);
|
||||
sizeNeeded -= size;
|
||||
fCount -= size / fElemSize;
|
||||
hsAssert(int(fCount) >= 0, "bad fElemSize");
|
||||
}
|
||||
return origCount - fCount; // return number of elements popped
|
||||
}
|
||||
|
||||
void* hsAppender::PushTail()
|
||||
{
|
||||
if (fFirstBlock == nil)
|
||||
{ fFirstBlock = hsAppenderHead::NewAppend(fElemSize, fElemCount, nil);
|
||||
fLastBlock = fFirstBlock;
|
||||
}
|
||||
else if (fLastBlock->CanAppend() == false)
|
||||
{ fLastBlock->fNext = hsAppenderHead::NewAppend(fElemSize, fElemCount, fLastBlock);
|
||||
fLastBlock = fLastBlock->fNext;
|
||||
}
|
||||
|
||||
fCount += 1;
|
||||
return fLastBlock->Append(fElemSize);
|
||||
}
|
||||
|
||||
void hsAppender::PushTail(const void* data)
|
||||
{
|
||||
void* addr = this->PushTail();
|
||||
if (data)
|
||||
HSMemory::BlockMove(data, addr, fElemSize);
|
||||
}
|
||||
|
||||
void hsAppender::PushTail(int count, const void* data)
|
||||
{
|
||||
hsThrowIfBadParam(count < 0);
|
||||
|
||||
int sizeNeeded = count * fElemSize;
|
||||
|
||||
while (sizeNeeded > 0)
|
||||
{ if (fFirstBlock == nil)
|
||||
{ hsAssert(fCount == 0, "uninited count");
|
||||
fFirstBlock = hsAppenderHead::NewAppend(fElemSize, fElemCount, nil);
|
||||
fLastBlock = fFirstBlock;
|
||||
}
|
||||
else if (fLastBlock->CanAppend() == false)
|
||||
{ fLastBlock->fNext = hsAppenderHead::NewAppend(fElemSize, fElemCount, fLastBlock);
|
||||
fLastBlock = fLastBlock->fNext;
|
||||
}
|
||||
|
||||
int size = fLastBlock->AppendSize();
|
||||
hsAssert(size > 0, "bad appendsize");
|
||||
if (size > sizeNeeded)
|
||||
size = sizeNeeded;
|
||||
void* dst = fLastBlock->Append(size);
|
||||
|
||||
if (data)
|
||||
{ HSMemory::BlockMove(data, dst, size);
|
||||
data = (char*)data + size;
|
||||
}
|
||||
sizeNeeded -= size;
|
||||
}
|
||||
fCount += count;
|
||||
}
|
||||
|
||||
void* hsAppender::PeekTail() const
|
||||
{
|
||||
if (fLastBlock)
|
||||
return (char*)fLastBlock->fStop - fElemSize;
|
||||
else
|
||||
return nil;
|
||||
}
|
||||
|
||||
hsBool hsAppender::PopTail(void* data)
|
||||
{
|
||||
if (fCount == 0)
|
||||
return false;
|
||||
|
||||
fCount -= 1;
|
||||
|
||||
if (fLastBlock->PopTail(fElemSize, data))
|
||||
{ hsAppenderHead* prev = fLastBlock->fPrev;
|
||||
if (prev)
|
||||
prev->fNext = nil;
|
||||
HSMemory::Delete(fLastBlock);
|
||||
fLastBlock = prev;
|
||||
if (prev == nil)
|
||||
fFirstBlock = nil;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
hsAppenderIterator::hsAppenderIterator(const hsAppender* list)
|
||||
{
|
||||
this->ResetToHead(list);
|
||||
}
|
||||
|
||||
void hsAppenderIterator::ResetToHead(const hsAppender* list)
|
||||
{
|
||||
fAppender = list;
|
||||
fCurrBlock = nil;
|
||||
|
||||
if (fAppender)
|
||||
{ fCurrBlock = fAppender->fFirstBlock;
|
||||
if (fCurrBlock)
|
||||
fCurrItem = fCurrBlock->GetFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void hsAppenderIterator::ResetToTail(const hsAppender* list)
|
||||
{
|
||||
fAppender = list;
|
||||
fCurrBlock = nil;
|
||||
|
||||
if (fAppender)
|
||||
{ fCurrBlock = fAppender->fLastBlock;
|
||||
if (fCurrBlock)
|
||||
fCurrItem = fCurrBlock->GetLast(fAppender->fElemSize);
|
||||
}
|
||||
}
|
||||
|
||||
void* hsAppenderIterator::Next()
|
||||
{
|
||||
void* item = nil;
|
||||
|
||||
if (fCurrBlock)
|
||||
{ item = fCurrItem;
|
||||
fCurrItem = (char*)fCurrItem + fAppender->fElemSize;
|
||||
if (fCurrItem == fCurrBlock->GetBottom())
|
||||
{ fCurrBlock = fCurrBlock->fNext;
|
||||
if (fCurrBlock)
|
||||
fCurrItem = fCurrBlock->GetFirst();
|
||||
}
|
||||
else if (fCurrItem == fCurrBlock->GetStop())
|
||||
{ hsAssert(fCurrBlock->fNext == nil, "oops");
|
||||
fCurrBlock = nil;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
hsBool hsAppenderIterator::Next(void* data)
|
||||
{
|
||||
void* addr = this->Next();
|
||||
if (addr)
|
||||
{ if (data)
|
||||
HSMemory::BlockMove(addr, data, fAppender->fElemSize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int hsAppenderIterator::Next(int count, void* data)
|
||||
{
|
||||
int origCount = count;
|
||||
|
||||
while (count > 0 && this->Next(data))
|
||||
{ if (data)
|
||||
data = (void*)((char*)data + fAppender->fElemSize);
|
||||
count -= 1;
|
||||
}
|
||||
return origCount - count;
|
||||
}
|
||||
|
||||
void* hsAppenderIterator::Prev()
|
||||
{
|
||||
void* item = nil;
|
||||
|
||||
if (fCurrBlock)
|
||||
{ item = fCurrItem;
|
||||
fCurrItem = (char*)fCurrItem - fAppender->fElemSize;
|
||||
if (item == fCurrBlock->GetTop())
|
||||
{ fCurrBlock = fCurrBlock->fPrev;
|
||||
if (fCurrBlock)
|
||||
fCurrItem = fCurrBlock->GetLast(fAppender->fElemSize);
|
||||
}
|
||||
else if (item == fCurrBlock->GetFirst())
|
||||
{ hsAssert(fCurrBlock->fPrev == nil, "oops");
|
||||
fCurrBlock = nil;
|
||||
}
|
||||
}
|
||||
return item;
|
||||
}
|
||||
|
||||
hsBool hsAppenderIterator::Prev(void* data)
|
||||
{
|
||||
void* addr = this->Prev();
|
||||
if (addr)
|
||||
{ if (data)
|
||||
HSMemory::BlockMove(addr, data, fAppender->fElemSize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
//
|
||||
// MEMORY USE REPORTING CODE
|
||||
//
|
||||
//-------------------------------------------------------------
|
||||
|
||||
#if 1//!(defined(HS_DEBUGGING)&&(HS_BUILD_FOR_WIN32)&& defined(HS_FIND_MEM_LEAKS))
|
||||
// EMPTY STUB
|
||||
void SortNDumpUnfreedMemory(const char *, bool ) // file name base, and FULL report indicator
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
typedef struct _CrtMemBlockHeader
|
||||
{
|
||||
// Pointer to the block allocated just before this one:
|
||||
struct _CrtMemBlockHeader *pBlockHeaderNext;
|
||||
// Pointer to the block allocated just after this one:
|
||||
struct _CrtMemBlockHeader *pBlockHeaderPrev;
|
||||
char *szFileName; // File name
|
||||
int nLine; // Line number
|
||||
size_t nDataSize; // Size of user block
|
||||
int nBlockUse; // Type of block
|
||||
long lRequest; // Allocation number
|
||||
// Buffer just before (lower than) the user's memory:
|
||||
unsigned char gap[4];
|
||||
} _CrtMemBlockHeader;
|
||||
|
||||
/* In an actual memory block in the debug heap,
|
||||
* this structure is followed by:
|
||||
* unsigned char data[nDataSize];
|
||||
* unsigned char anotherGap[4];
|
||||
*/
|
||||
|
||||
//
|
||||
// Dump formatted string to OutputDebugString
|
||||
//
|
||||
void __cdecl DebugMsg( LPSTR fmt, ... )
|
||||
{
|
||||
|
||||
char buff[256];
|
||||
wvsprintf(buff, fmt, (char *)(&fmt+1));
|
||||
hsStatusMessage(buff);
|
||||
|
||||
}
|
||||
|
||||
char *TrimFileName(char *name) // Trim file name of leading Directories
|
||||
{
|
||||
int len = 0;
|
||||
char *ptr;
|
||||
if (!name) return NULL;
|
||||
|
||||
len = strlen(name);
|
||||
ptr = name + len;
|
||||
for ( ptr--; ptr > name; ptr--)
|
||||
{
|
||||
if (*ptr == '\\')
|
||||
{
|
||||
ptr++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
//
|
||||
// Loop thru all unfreed blocks in the heap and dump out detailed info
|
||||
//
|
||||
|
||||
struct looktbl {
|
||||
char * fName; // Name of file
|
||||
// long fAllocs; // Number of Alloc calls
|
||||
long fBytes; // Total Bytes Alloc'd
|
||||
};
|
||||
#define LTBLMAX 300
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// This routine will report on the memory used in the engine.
|
||||
// If argument full is true, it gives a full dump from the start of the program
|
||||
// if !full, then each time the routine is called it remembers where it finishes off, then the next
|
||||
// call with !full, it will (attempt) to report on the newest allocations, backward to the last checkpoint
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
void SortNDumpUnfreedMemory(const char *nm, bool full) // file name base, and FULL report indicator
|
||||
{
|
||||
#ifndef DO_MEMORY_REPORTS
|
||||
if (!full) // full is launched by control M...partials are called each time the engine starts
|
||||
return;
|
||||
#endif
|
||||
|
||||
char fname[512];
|
||||
sprintf(fname,"%s_dmp.txt",nm);
|
||||
char *errStr = "";
|
||||
|
||||
|
||||
_CrtMemState heap_state;
|
||||
static UInt32 GrandTotal =0;
|
||||
static _CrtMemBlockHeader *cmbh_last; // Remember this header for next incremental check DANGER this
|
||||
// could break if this is freed...(gives bad report)
|
||||
_CrtMemBlockHeader *cmbh_last_good;
|
||||
|
||||
_CrtMemBlockHeader *cmbh;
|
||||
// Get Current heap state
|
||||
|
||||
_CrtMemCheckpoint(&heap_state);
|
||||
|
||||
cmbh = heap_state.pBlockHeader;
|
||||
|
||||
long totsize= 0; // Track Total Bytes
|
||||
long normsize = 0; // Track total of NORMAL Blocks
|
||||
|
||||
looktbl *ltb = TRACKED_NEW looktbl[LTBLMAX];
|
||||
long tblEnd=1; // first is "NULL";
|
||||
|
||||
memset((void *)ltb,0,sizeof(looktbl) * LTBLMAX); // clear table area
|
||||
|
||||
char *ftrim;
|
||||
|
||||
|
||||
ltb[0].fName = "NULL"; // Use first Table Pos for NULL
|
||||
|
||||
long tblpos;
|
||||
while (cmbh != NULL) // Accumulate Stats to table
|
||||
{
|
||||
if (cmbh == cmbh_last && !full) // full indicates ignore last "checkpoint", stop at last checkpoint if !full
|
||||
break;
|
||||
cmbh_last_good = cmbh;
|
||||
totsize += cmbh->nDataSize;
|
||||
if (cmbh->nBlockUse == _NORMAL_BLOCK)
|
||||
{
|
||||
normsize += cmbh->nDataSize;
|
||||
|
||||
if (cmbh->szFileName != NULL) // Shorten to just the file name, looks better, and strcmps faster
|
||||
{
|
||||
ftrim = TrimFileName(cmbh->szFileName);
|
||||
for (tblpos = 1; tblpos < tblEnd; tblpos++) // find the name in the table
|
||||
{
|
||||
if (!strcmp(ftrim,ltb[tblpos].fName))
|
||||
break; // found it
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tblpos = 0; // Use "NULL", first pos of table
|
||||
}
|
||||
|
||||
if (tblpos == tblEnd) // Did not find it...add it
|
||||
{
|
||||
tblEnd++;
|
||||
if (tblEnd >= LTBLMAX)
|
||||
{ DebugMsg("DumpUnfreedMemoryInfo: EXCEED MAX TABLE LENGTH\n");
|
||||
tblEnd--;
|
||||
break;
|
||||
}
|
||||
ltb[tblpos].fName = ftrim; // Add name
|
||||
}
|
||||
// Add Stats
|
||||
// ltb[tblpos].fAllocs++;
|
||||
ltb[tblpos].fBytes += cmbh->nDataSize;
|
||||
|
||||
|
||||
}
|
||||
cmbh = cmbh->pBlockHeaderNext;
|
||||
|
||||
}
|
||||
// This Code relies on the _CrtMemBlockHeader *cmbh_last_good for the "last" checkpoint to still be around...
|
||||
// If the following occurs, that chunk has been deleted. we could fix this by allocating our own
|
||||
// chunk and keeping it (watch for mem leaks though) or figuring out an "approximat" re syncying routine
|
||||
// that works before we run thru collecting data. PBG
|
||||
|
||||
if (cmbh_last && !full && cmbh == NULL)
|
||||
{
|
||||
//hsAssert(0,"Stats error: incremental mem check point has been deleted");
|
||||
errStr = "CHECK POINT ERROR, Results Inacurate";
|
||||
}
|
||||
|
||||
if (normsize) // Don't write out about nothing
|
||||
{
|
||||
|
||||
CreateDirectory("Reports",NULL); // stick em in a sub directory
|
||||
char fnm[512];
|
||||
sprintf(fnm,"Reports\\%s",fname);
|
||||
|
||||
FILE * DumpLogFile = fopen( fnm, "w" );
|
||||
// long allocs=0;
|
||||
if ( DumpLogFile != NULL )
|
||||
{
|
||||
// Print Stats
|
||||
fprintf(DumpLogFile, "Filename Total=%ld(k) %s\n",(normsize + 500)/1000,errStr);
|
||||
for (int i = 0; i < tblEnd; i++)
|
||||
{ //fprintf(DumpLogFile,"%s\t%ld\n",ltb[i].fName, (ltb[i].fBytes+500)/1000);//,ltb[i].fAllocs);
|
||||
fprintf(DumpLogFile,"%s ",ltb[i].fName);
|
||||
int len = strlen(ltb[i].fName);
|
||||
|
||||
for(int x=len; x < 25; x++)
|
||||
fputc(' ',DumpLogFile); // make even columns
|
||||
fprintf(DumpLogFile,"%5ld K\n",(UInt32)( ltb[i].fBytes+500)/1000);//,ltb[i].fAllocs);
|
||||
|
||||
//allocs += ltb[i].fAllocs;
|
||||
}
|
||||
|
||||
DebugMsg("MEMORY USE FILE DUMPED TO %s \n",fname);
|
||||
DebugMsg("MEMORY Check: Total size %ld, Normal Size: %ld\n",totsize,normsize);
|
||||
|
||||
fclose(DumpLogFile);
|
||||
}
|
||||
static int first=1;
|
||||
if (!full) // if this is a partial mem dump, write to the ROOMS.txt file a summary
|
||||
{
|
||||
sprintf(fnm,"Reports\\%s","ROOMS.txt");
|
||||
|
||||
if (first)
|
||||
{ DumpLogFile = fopen( fnm, "w" ); // first time clobber the old
|
||||
if (DumpLogFile)
|
||||
fprintf(DumpLogFile, "Filename Memory-Used(K) RunningTotal\n");// \tAllocation Calls \n" );
|
||||
first = 0;
|
||||
}
|
||||
else
|
||||
DumpLogFile = fopen( fnm, "a+" );
|
||||
if( DumpLogFile)
|
||||
{ fprintf(DumpLogFile,"%s ",nm);
|
||||
int len = strlen(nm);
|
||||
GrandTotal += (UInt32)(normsize+500)/1000;
|
||||
|
||||
for(int x=len; x < 25; x++)
|
||||
fputc(' ',DumpLogFile); // make even columns
|
||||
fprintf(DumpLogFile,"%5ld K %5ld %s\n",(UInt32)(normsize+500)/1000,GrandTotal,errStr);//, allocs);
|
||||
fclose(DumpLogFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cmbh_last = heap_state.pBlockHeader;
|
||||
delete ltb;
|
||||
}
|
||||
#endif
|
Reference in New Issue
Block a user