Directory restructuring to make it easier to version projects that don't build zdoom.exe.

SVN r4 (trunk)
This commit is contained in:
Randy Heit 2006-02-24 04:48:15 +00:00
commit cf11cbdb30
821 changed files with 361202 additions and 0 deletions

435
src/zstringpool.cpp Normal file
View file

@ -0,0 +1,435 @@
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "zstring.h"
string::PoolGroup string::Pond;
#ifndef NOPOOLS
struct string::Pool
{
// The pool's performance (and thus the string class's performance) is
// controlled via these two constants. A small pool size will result
// in more frequent garbage collection, while a large pool size will
// result in longer garbage collection. A large pool can also end up
// wasting memory. Something that's not too small and not too large
// is ideal. Similarly, making the granularity too big will also result
// in more frequent garbage collection. But if you do a lot of
// concatenation with the += operator, then a large granularity is good
// because it gives the string more room to grow without needing to
// be reallocated.
//
// Note that the granularity must be a power of 2. The pool size need
// not be, although it's best to make it a multiple of the granularity.
enum { POOL_SIZE = 64*1024 };
enum { BLOCK_GRANULARITY = 16 };
Pool (size_t minSize);
~Pool ();
char *Alloc (string *owner, size_t len);
char *Realloc (char *chars, size_t newlen);
void Free (char *chars);
void MergeFreeBlocks (StringHeader *block);
void CollectGarbage (bool noGenerations);
bool BigEnough (size_t len) const;
size_t RoundLen (size_t len) const;
Pool *Next;
size_t FreeSpace;
char *PoolData;
char *MaxAlloc;
StringHeader *NextAlloc;
StringHeader *GarbageStart;
int GenerationNum;
};
// The PoolGroup does not get a constructor, because there is no way to
// guarantee it will be constructed before any strings that need it.
// Instead, we rely on the loader to initialize Pools to NULL for us.
string::PoolGroup::~PoolGroup ()
{
int count = 0;
Pool *pool = Pools, *next;
while (pool != NULL)
{
count++;
next = pool->Next;
delete pool;
pool = next;
}
Pools = NULL;
}
char *string::PoolGroup::Alloc (string *owner, size_t len)
{
char *mem;
Pool *pool, *best, **prev, **bestprev;
// If no pools, create one
if (Pools == NULL)
{
Pools = new string::Pool (len);
}
// Try to allocate space from an existing pool
for (pool = Pools; pool != NULL; pool = pool->Next)
{
mem = pool->Alloc (owner, len);
if (mem != NULL)
{
return mem;
}
}
// Compact the pool with the most free space and try again
best = Pools;
bestprev = &Pools;
pool = best->Next;
prev = &best->Next;
while (pool != NULL)
{
if (pool->FreeSpace > best->FreeSpace)
{
bestprev = prev;
best = pool;
}
prev = &pool->Next;
pool = pool->Next;
}
if (best->BigEnough (len))
{
best->CollectGarbage (false);
mem = best->Alloc (owner, len);
if (mem == NULL)
{
best->CollectGarbage (true);
mem = best->Alloc (owner, len);
}
// Move the pool to the front of the list
*bestprev = best->Next;
best->Next = Pools;
Pools = best;
}
else
{
// No pools were large enough to hold the string, so create a new one
pool = new string::Pool (len);
pool->Next = Pools;
Pools = pool;
mem = pool->Alloc (owner, len);
}
return mem;
}
char *string::PoolGroup::Realloc (string *owner, char *chars, size_t newlen)
{
if (chars == NULL)
{
chars = Alloc (owner, newlen);
if (chars != NULL)
{
chars[0] = '\0';
}
return chars;
}
Pool *pool = FindPool (chars);
char *newchars = pool->Realloc (chars, newlen);
if (newchars == NULL)
{
newchars = Alloc (owner, newlen);
if (newchars != NULL)
{
StrCopy (newchars, chars, GetHeader (chars)->Len);
pool->Free (chars);
}
}
return newchars;
}
void string::PoolGroup::Free (char *chars)
{
Pool *pool = FindPool (chars);
pool->Free (chars);
}
string::Pool *string::PoolGroup::FindPool (char *chars) const
{
Pool *pool = Pools;
while (pool != NULL)
{
if (pool->PoolData <= chars && pool->MaxAlloc > chars)
{
break;
}
pool = pool->Next;
}
return pool;
}
string::StringHeader *string::PoolGroup::GetHeader (char *chars)
{
return (StringHeader *)(chars - sizeof(StringHeader));
}
string::Pool::Pool (size_t minSize)
{
if (minSize < POOL_SIZE)
{
minSize = POOL_SIZE;
}
minSize = RoundLen (minSize-1);
PoolData = new char[minSize];
FreeSpace = minSize;
MaxAlloc = PoolData + minSize;
Next = NULL;
NextAlloc = (StringHeader *)PoolData;
NextAlloc->Owner = NULL;
NextAlloc->Len = minSize;
GarbageStart = NextAlloc;
GenerationNum = 0;
}
string::Pool::~Pool ()
{
if (PoolData != NULL)
{
// Watch out! During program exit, the pool may be deleted before
// all the strings stored in it. So we need to walk through the pool
// and make any owned strings un-owned.
StringHeader *str;
for (str = (StringHeader *)PoolData; str < NextAlloc; )
{
if (str->Owner != NULL)
{
str->Owner->Chars = NULL;
str->Owner = NULL;
str = (StringHeader *)((char *)str + RoundLen(str->Len));
}
else
{
str = (StringHeader *)((char *)str + str->Len);
}
}
delete[] PoolData;
PoolData = NULL;
}
}
char *string::Pool::Alloc (string *owner, size_t len)
{
if (NextAlloc == (StringHeader *)MaxAlloc)
{
return NULL;
}
size_t needlen = RoundLen (len);
if (NextAlloc->Len >= needlen)
{
char *chars = (char *)NextAlloc + sizeof(StringHeader);
chars[0] = '\0';
NextAlloc->Owner = owner;
NextAlloc->Len = len;
NextAlloc = (StringHeader *)((char *)NextAlloc + needlen);
if (NextAlloc != (StringHeader *)MaxAlloc)
{
NextAlloc->Owner = NULL;
NextAlloc->Len = MaxAlloc - (char *)NextAlloc;
}
FreeSpace -= needlen;
return chars;
}
return NULL;
}
char *string::Pool::Realloc (char *chars, size_t newlen)
{
size_t needlen = RoundLen (newlen);
StringHeader *oldhead = (StringHeader *)(chars - sizeof(StringHeader));
size_t oldtruelen = RoundLen (oldhead->Len);
if (oldtruelen > needlen)
{ // Shrinking, so make a new free block after this one.
StringHeader *nextblock = (StringHeader *)((char *)oldhead + needlen);
nextblock->Owner = NULL;
nextblock->Len = oldtruelen - needlen;
MergeFreeBlocks (nextblock);
oldhead->Len = newlen;
return chars;
}
if (oldtruelen == needlen)
{ // There is already enough space allocated for the needed growth
oldhead->Len = newlen;
return chars;
}
// If there is free space after this string, try to grow into it.
StringHeader *nexthead = (StringHeader *)((char *)oldhead + oldtruelen);
if (nexthead < (StringHeader *)MaxAlloc && nexthead->Owner == NULL)
{
// Make sure there's only one free block past this string
MergeFreeBlocks (nexthead);
// Is there enough room to grow?
if (oldtruelen + nexthead->Len >= needlen)
{
oldhead->Len = newlen;
size_t newfreelen = oldtruelen + nexthead->Len - needlen;
if (newfreelen > 0)
{
StringHeader *nextnewhead = (StringHeader *)((char *)oldhead + needlen);
nextnewhead->Owner = NULL;
nextnewhead->Len = newfreelen;
// If this is the last string in the pool, then the NextAlloc marker also needs to move
if (nexthead == NextAlloc)
{
NextAlloc = nextnewhead;
}
}
FreeSpace -= needlen - oldtruelen;
return chars;
}
}
// There was insufficient room for growth, so try to allocate space at the end of the pool
char *newchars = Alloc (oldhead->Owner, newlen);
if (newchars != NULL)
{
string::StrCopy (newchars, chars, oldhead->Len);
Free (chars);
return newchars;
}
// There was not enough space
return NULL;
}
void string::Pool::Free (char *chars)
{
StringHeader *head = (StringHeader *)(chars - sizeof(StringHeader));
size_t truelen = RoundLen (head->Len);
FreeSpace += truelen;
head->Owner = NULL;
head->Len = truelen;
MergeFreeBlocks (head);
}
void string::Pool::MergeFreeBlocks (StringHeader *head)
{
StringHeader *block;
for (block = head;
block->Owner == NULL && block != NextAlloc;
block = (StringHeader *)((char *)block + block->Len))
{
}
// If this chain of blocks meets up with the free space, then they can join up with it.
if (block == NextAlloc)
{
if (GarbageStart == NextAlloc)
{
GarbageStart = head;
}
NextAlloc = head;
head->Len = MaxAlloc - (char *)head;
}
else
{
head->Len = (char *)block - (char *)head;
}
}
bool string::Pool::BigEnough (size_t len) const
{
return FreeSpace >= RoundLen (len);
}
size_t string::Pool::RoundLen (size_t len) const
{
return (len + 1 + sizeof(StringHeader) + BLOCK_GRANULARITY - 1) & ~(BLOCK_GRANULARITY - 1);
}
void string::Pool::CollectGarbage (bool noGenerations)
{
// This is a generational garbage collector. The space occupied by strings from
// the first two generations will not be collected unless noGenerations is set true.
if (noGenerations)
{
GarbageStart = (StringHeader *)PoolData;
GenerationNum = 0;
}
StringHeader *moveto, *movefrom;
moveto = movefrom = GarbageStart;
while (movefrom < NextAlloc)
{
if (movefrom->Owner != NULL)
{
size_t truelen = RoundLen (movefrom->Len);
if (moveto != movefrom)
{
memmove (moveto, movefrom, truelen);
moveto->Owner->Chars = (char *)moveto + sizeof(StringHeader);
}
moveto = (StringHeader *)((char *)moveto + truelen);
movefrom = (StringHeader *)((char *)movefrom + truelen);
}
else
{
movefrom = (StringHeader *)((char *)movefrom + movefrom->Len);
}
}
NextAlloc = moveto;
if (NextAlloc != (StringHeader *)MaxAlloc)
{
NextAlloc->Len = MaxAlloc - (char *)moveto;
NextAlloc->Owner = NULL;
if (NextAlloc->Len != FreeSpace)
FreeSpace = FreeSpace;
}
else if (FreeSpace != 0)
FreeSpace = FreeSpace;
if (++GenerationNum <= 3)
{
GarbageStart = moveto;
}
}
#else
char *string::PoolGroup::Alloc (string *owner, size_t len)
{
char *mem = (char *)malloc (len + 1 + sizeof(StringHeader));
StringHeader *head = (StringHeader *)mem;
mem += sizeof(StringHeader);
head->Len = len;
return mem;
}
char *string::PoolGroup::Realloc (string *owner, char *chars, size_t newlen)
{
if (chars == NULL)
{
chars = Alloc (owner, newlen);
chars[0] = '\0';
return chars;
}
StringHeader *head = (StringHeader *)(chars - sizeof(StringHeader));
head = (StringHeader *)realloc (head, newlen+1+sizeof(StringHeader));
head->Len = newlen;
return (char *)head + sizeof(StringHeader);
}
void string::PoolGroup::Free (char *chars)
{
free (chars - sizeof(StringHeader));
}
#endif