SVN r116 (trunk)

This commit is contained in:
Randy Heit 2006-05-16 02:50:18 +00:00
parent afd6a1258f
commit ac53ed6ecd
25 changed files with 600 additions and 745 deletions

View file

@ -270,7 +270,6 @@ ${COMPILER} "autostart.cpp \
vectors.cpp \
name.cpp \
zstring.cpp \
zstringpool.cpp \
zstrformat.cpp \
w_wad.cpp \
wi_stuff.cpp \

View file

@ -1,3 +1,19 @@
May 15, 2006
- Changed the memory management for FString. Instead of using a garbage
collected heap, it now uses normal heap calls and reference counting to
implement lazy copying. You may now use bitwise operators to move
(but not copy!) FStrings around in memory. This means that the
CopyForTArray template function is gone, since TArrays can now freely
move their contents around without bothering with their specifics.
There is one important caveat, however. It is not acceptable to blindly 0
an FString's contents. This necessitated the creation of a proper
constructor for player_s so that it can be reset without using memset. I
did a quick scan of all memsets in the source and didn't see anything else
with a similar problem, but it's possible I missed something.
- Fixed: Build tiles were never deallocated.
- Fixed: Using Build's palette.dat only got half the palette right.
May 14, 2006 (Changes by Graf Zahl)
- Added a show_obituaries option to disable obituaries without disabling
other more important message types which have lower priority for some

View file

@ -64,7 +64,8 @@ void DCajunMaster::ClearPlayer (int i, bool keepTeam)
bot->inuse = false;
bot->lastteam = keepTeam ? players[i].userinfo.team : TEAM_None;
}
memset (&players[i], 0, sizeof(player_t));
players[i].~player_t();
::new(&players[i]) player_t;
playeringame[i] = false;
}

View file

@ -509,7 +509,7 @@ void C_SetDefaultBindings ()
BOOL C_DoKey (event_t *ev)
{
char *binding = NULL;
FString binding;
bool dclick;
int dclickspot;
byte dclickmask;
@ -553,33 +553,27 @@ BOOL C_DoKey (event_t *ev)
}
if (binding == NULL || *binding==0)
if (binding.IsEmpty())
{
binding = Bindings[ev->data1];
dclick = false;
}
if (binding != NULL && *binding!=0 && (chatmodeon == 0 || ev->data1 < 256))
if (!binding.IsEmpty() && (chatmodeon == 0 || ev->data1 < 256))
{
if (ev->type == EV_KeyUp)
if (ev->type == EV_KeyUp && binding[0] != '+')
{
if (binding[0] != '+')
{
return false;
}
binding[0] = '-';
return false;
}
char *copy = binding.LockBuffer();
if (ev->type == EV_KeyUp)
{
copy[0] = '-';
}
// Copy the command in case it rebinds the key
char copy[1024];
strncpy (copy, binding, 1023);
copy[1023] = 0;
AddCommandString (copy, dclick ? ev->data1 | KEY_DBLCLICKED : ev->data1);
if (ev->type == EV_KeyUp)
{
binding[0] = '+';
}
return true;
}
return false;
@ -711,7 +705,7 @@ void C_ChangeBinding (const char *str, int newone)
}
}
char *C_GetBinding (int key)
const char *C_GetBinding (int key)
{
return (unsigned int)key < NUM_KEYS ? Bindings[key].GetChars() : NULL;
}

View file

@ -53,6 +53,6 @@ void C_SetDefaultBindings ();
void C_UnbindAll ();
// Returns string bound to given key (NULL if none)
char *C_GetBinding (int key);
const char *C_GetBinding (int key);
#endif //__C_BINDINGS_H__

View file

@ -146,7 +146,7 @@ void DefaultExtension (char *path, const char *extension)
void DefaultExtension (FString &path, const char *extension)
{
char *src = &path[int(path.Len())-1];
const char *src = &path[int(path.Len())-1];
while (src != &path[0] && !IsSeperator(*src))
{

View file

@ -83,6 +83,7 @@
#include "gameconfigfile.h"
#include "sbar.h"
#include "decallib.h"
#include "version.h"
#include "r_polymost.h"
#include "version.h"
#include "v_text.h"
@ -1409,7 +1410,6 @@ static EIWADType ScanIWAD (const char *iwad)
static int CheckIWAD (const char *doomwaddir, WadStuff *wads)
{
const char *slash;
char iwad[512];
int i;
int numfound;
@ -1422,8 +1422,10 @@ static int CheckIWAD (const char *doomwaddir, WadStuff *wads)
{
if (wads[i].Path.IsEmpty())
{
sprintf (iwad, "%s%s%s", doomwaddir, slash, IWADNames[i]);
FixPathSeperator (iwad);
FString iwad;
iwad.Format ("%s%s%s", doomwaddir, slash, IWADNames[i]);
FixPathSeperator (iwad.LockBuffer());
if (FileExists (iwad))
{
wads[i].Type = ScanIWAD (iwad);
@ -1495,12 +1497,10 @@ static EIWADType IdentifyVersion (const char *zdoom_wad)
bool iwadparmfound = false;
FString custwad;
memset (wads, 0, sizeof(wads));
if (iwadparm)
{
custwad = iwadparm;
FixPathSeperator (custwad.GetChars());
FixPathSeperator (custwad.LockBuffer());
if (CheckIWAD (custwad, wads))
{ // -iwad parameter was a directory
iwadparm = NULL;
@ -1514,7 +1514,7 @@ static EIWADType IdentifyVersion (const char *zdoom_wad)
}
}
if (iwadparm == NULL || wads[0].Path == NULL)
if (iwadparm == NULL || wads[0].Path.IsEmpty())
{
if (GameConfig->SetSection ("IWADSearch.Directories"))
{
@ -1552,7 +1552,7 @@ static EIWADType IdentifyVersion (const char *zdoom_wad)
}
}
if (iwadparm != NULL && wads[0].Path != NULL)
if (iwadparm != NULL && !wads[0].Path.IsEmpty())
{
iwadparmfound = true;
}
@ -1612,17 +1612,19 @@ static EIWADType IdentifyVersion (const char *zdoom_wad)
if (wads[pickwad].Type == IWAD_Strife)
{ // Try to load voices.wad along with strife1.wad
char *filepart = strrchr (wads[pickwad].Path, '/');
if (filepart == NULL)
long lastslash = wads[pickwad].Path.LastIndexOf ('/');
FString path;
if (lastslash == -1)
{
filepart = wads[pickwad].Path;
path = wads[pickwad].Path;
}
else
{
++filepart;
path = FString (wads[pickwad].Path, lastslash + 1);
}
strcpy (filepart, "voices.wad");
D_AddFile (wads[pickwad].Path);
path += "voices.wad";
D_AddFile (path);
}
return wads[pickwad].Type;

View file

@ -77,6 +77,8 @@ enum EIWADType
struct WadStuff
{
WadStuff() : Type(IWAD_Doom2TNT) {}
FString Path;
EIWADType Type;
};

View file

@ -2194,7 +2194,7 @@ void Net_DoCommand (int type, byte **stream, int player)
{
// Paths sent over the network will be valid for the system that sent
// the save command. For other systems, the path needs to be changed.
char *fileonly = savegamefile.GetChars();
const char *fileonly = savegamefile.GetChars();
char *slash = strrchr (fileonly, '\\');
if (slash != NULL)
{

View file

@ -155,6 +155,8 @@ enum
class player_s
{
public:
player_s();
void Serialize (FArchive &arc);
void FixPointers (const DObject *obj, DObject *replacement);

View file

@ -37,6 +37,7 @@
#include <stdlib.h>
#include "tarray.h"
#include "doomtype.h"
#include "m_alloc.h"
struct PClass;

View file

@ -738,7 +738,7 @@ BOOL G_Responder (event_t *ev)
if (gameaction == ga_nothing &&
(demoplayback || gamestate == GS_DEMOSCREEN || gamestate == GS_TITLELEVEL))
{
char *cmd = C_GetBinding (ev->data1);
const char *cmd = C_GetBinding (ev->data1);
if (ev->type == EV_KeyDown)
{
@ -1118,7 +1118,7 @@ void G_PlayerReborn (int player)
botskill_t b_skill;//Added by MC:
APlayerPawn *actor;
const PClass *cls;
char *log;
FString log;
p = &players[player];
@ -1134,7 +1134,9 @@ void G_PlayerReborn (int player)
cls = p->cls;
log = p->LogText;
memset (p, 0, sizeof(*p));
// Reset player structure to its defaults
p->~player_t();
::new(p) player_t;
memcpy (p->frags, frags, sizeof(p->frags));
p->fragcount = fragcount;
@ -1483,7 +1485,7 @@ void G_ScreenShot (char *filename)
// G_InitFromSavegame
// Can be called by the startup code or the menu task.
//
void G_LoadGame (char* name)
void G_LoadGame (const char* name)
{
if (name != NULL)
{

View file

@ -37,7 +37,7 @@ void G_DeferedPlayDemo (char* demo);
// Can be called by the startup code or M_Responder,
// calls P_SetupLevel or W_EnterWorld.
void G_LoadGame (char* name);
void G_LoadGame (const char* name);
void G_DoLoadGame (void);

View file

@ -148,8 +148,6 @@ static void ReadMultiplePlayers (FArchive &arc, int numPlayers, int numPlayersNo
BYTE *tempPlayerUsed = new BYTE[numPlayers];
BYTE *playerUsed = new BYTE[MAXPLAYERS];
memset (playertemp, 0, numPlayers*sizeof(player_t));
for (i = 0; i < numPlayers; ++i)
{
nametemp[i] = NULL;

View file

@ -67,6 +67,102 @@ static TArray<sector_t *> PredictionTouchingSectorsBackup;
BOOL onground;
// The player_s constructor. Since LogText is not a POD, we cannot just
// memset it all to 0.
player_s::player_s()
: mo(0),
playerstate(0),
cls(0),
DesiredFOV(0),
FOV(0),
viewz(0),
viewheight(0),
defaultviewheight(0),
deltaviewheight(0),
bob(0),
momx(0),
momy(0),
centering(0),
turnticks(0),
oldbuttons(0),
attackdown(0),
health(0),
InvFirst(0),
InvSel(0),
inventorytics(0),
CurrentPlayerClass(0),
pieces(0),
backpack(0),
fragcount(0),
lastkilltime(0),
multicount(0),
spreecount(0),
ReadyWeapon(0),
PendingWeapon(0),
cheats(0),
Powers(0),
refire(0),
inconsistant(0),
killcount(0),
itemcount(0),
secretcount(0),
damagecount(0),
bonuscount(0),
hazardcount(0),
poisoncount(0),
poisoner(0),
attacker(0),
extralight(0),
xviewshift(0),
morphTics(0),
PremorphWeapon(0),
chickenPeck(0),
jumpTics(0),
respawn_time(0),
camera(0),
air_finished(0),
accuracy(0),
stamina(0),
savedyaw(0),
savedpitch(0),
angle(0),
dest(0),
prev(0),
enemy(0),
missile(0),
mate(0),
last_mate(0),
t_active(0),
t_respawn(0),
t_strafe(0),
t_react(0),
t_fight(0),
t_roam(0),
t_rocket(0),
isbot(0),
first_shot(0),
sleft(0),
allround(0),
oldx(0),
oldy(0),
skin(0),
BlendR(0),
BlendG(0),
BlendB(0),
BlendA(0),
LogText(),
crouching(0),
crouchdir(0),
crouchfactor(0),
crouchoffset(0),
crouchviewdelta(0)
{
memset (&cmd, 0, sizeof(cmd));
memset (&userinfo, 0, sizeof(userinfo));
memset (frags, 0, sizeof(frags));
memset (psprites, 0, sizeof(psprites));
memset (&skill, 0, sizeof(skill));
}
// This function supplements the pointer cleanup in dobject.cpp, because
// player_s is not derived from DObject. (I tried it, and DestroyScan was

View file

@ -93,7 +93,7 @@ byte** warpedflats;
int* flatwarpedwhen;
static TArray<BYTE *> BuildTileFiles;
FTextureManager TexMan;
FTextureManager::FTextureManager ()
@ -2771,7 +2771,7 @@ void R_InitBuildTiles ()
int slashat = rffpath.LastIndexOf ('/');
if (slashat >= 0)
{
rffpath.Resize (slashat + 1);
rffpath.Truncate (slashat + 1);
}
else
{
@ -2793,8 +2793,6 @@ void R_InitBuildTiles ()
break;
}
// BADBAD: This memory is never explicitly deleted except when the
// version number is wrong.
int len = Q_filelength (f);
BYTE *art = new BYTE[len];
if (fread (art, 1, len, f) != len || LittleLong(*(DWORD *)art) != 1)
@ -2803,6 +2801,7 @@ void R_InitBuildTiles ()
}
else
{
BuildTileFiles.Push (art);
TexMan.AddTiles (art);
}
fclose (f);
@ -2820,8 +2819,6 @@ void R_InitBuildTiles ()
break;
}
// BADBAD: This memory is never explicitly deleted except when the
// version number is wrong.
BYTE *art = new BYTE[Wads.LumpLength (lumpnum)];
Wads.ReadLump (lumpnum, art);
@ -2831,11 +2828,20 @@ void R_InitBuildTiles ()
}
else
{
BuildTileFiles.Push (art);
TexMan.AddTiles (art);
}
}
}
void R_DeinitBuildTiles ()
{
for (unsigned int i = 0; i < BuildTileFiles.Size(); ++i)
{
delete[] BuildTileFiles[i];
}
BuildTileFiles.Clear();
}
static struct FakeCmap {
char name[8];
@ -3030,6 +3036,7 @@ void R_InitData ()
void R_DeinitData ()
{
R_DeinitColormaps ();
R_DeinitBuildTiles();
// Free openings
if (openings != NULL)

View file

@ -81,13 +81,6 @@ struct FRandomSoundList
WORD NumSounds;
};
template<>
void CopyForTArray<FRandomSoundList> (FRandomSoundList &dst, FRandomSoundList &src)
{
dst = src;
}
struct FPlayerClassLookup
{
char Name[MAX_SNDNAME+1];

View file

@ -417,14 +417,15 @@ bool TimiditySong::ValidateTimidity ()
bool TimiditySong::LaunchTimidity ()
{
if (CommandLine.Len() == 0)
if (CommandLine.IsEmpty())
{
return false;
}
// Tell Timidity whether it should loop or not
CommandLine[LoopPos] = m_Looping ? 'l' : ' ';
DPrintf ("cmd: \x1cG%s\n", CommandLine.GetChars());
char *cmdline = CommandLine.LockBuffer();
cmdline[LoopPos] = m_Looping ? 'l' : ' ';
DPrintf ("cmd: \x1cG%s\n", cmdline);
#ifdef _WIN32
STARTUPINFO startup = { sizeof(startup), };
@ -440,14 +441,16 @@ bool TimiditySong::LaunchTimidity ()
startup.lpTitle = "TiMidity (ZDoom Launched)";
startup.wShowWindow = SW_SHOWMINNOACTIVE;
if (CreateProcess (NULL, CommandLine.GetChars(), NULL, NULL, TRUE,
if (CreateProcess (NULL, cmdline, NULL, NULL, TRUE,
/*HIGH_PRIORITY_CLASS|*/DETACHED_PROCESS, NULL, NULL, &startup, &procInfo))
{
ChildProcess = procInfo.hProcess;
//SetThreadPriority (procInfo.hThread, THREAD_PRIORITY_HIGHEST);
CloseHandle (procInfo.hThread); // Don't care about the created thread
CommandLine.UnlockBuffer();
return true;
}
CommandLine.UnlockBuffer();
char hres[9];
LPTSTR msgBuf;

View file

@ -38,37 +38,9 @@
#include <stdlib.h>
#include <assert.h>
#include <new>
#include "m_alloc.h"
// This function is called once for each entry in the TArray after it grows.
// The old entries will be immediately freed afterwards, so if they need to
// be destroyed, it needs to happen in this function.
template<class T>
void CopyForTArray (T &dst, T &src)
{
::new((void*)&dst) T(src);
src.~T();
}
// This function is called by Push to copy the the element to an unconstructed
// area of memory. Basically, if NeedsDestructor is overloaded to return true,
// then this should be overloaded to use placement new.
template<class T>
void ConstructInTArray (T *dst, const T &src)
{
::new((void*)dst) T(src);
}
// This function is much like the above function, except it is called when
// the array is explicitly enlarged without using Push.
template<class T>
void ConstructEmptyInTArray (T *dst)
{
::new((void*)dst) T;
}
template <class T>
class TArray
{
@ -140,7 +112,7 @@ public:
unsigned int Push (const T &item)
{
Grow (1);
ConstructInTArray (&Array[Count], item);
::new((void*)&Array[Count]) T(item);
return Count++;
}
bool Pop (T &item)
@ -148,7 +120,7 @@ public:
if (Count > 0)
{
item = Array[--Count];
DoDelete (Count, Count);
Array[Count].~T();
return true;
}
return false;
@ -157,12 +129,9 @@ public:
{
if (index < Count)
{
for (unsigned int i = index; i < Count - 1; ++i)
{
Array[i] = Array[i+1];
}
Array[index].~T();
memmove (&Array[index], &Array[index+1], Count - index - 1);
Count--;
DoDelete (Count, Count);
}
}
// Inserts an item into the array, shifting elements as needed
@ -173,24 +142,19 @@ public:
// Inserting somewhere past the end of the array, so we can
// just add it without moving things.
Resize (index + 1);
ConstructInTArray (&Array[index], item);
::new ((void *)&Array[index]) T(item);
}
else
{
// Inserting somewhere in the middle of the array, so make
// room for it and shift old entries out of the way.
// Inserting somewhere in the middle of the array,
// so make room for it
Resize (Count + 1);
// Now copy items from the index and onward
for (unsigned int i = Count - 1; i-- > index; )
{
Array[i+1] = Array[i];
}
// Now move items from the index and onward out of the way
memmove (&Array[index+1], &Array[index], sizeof(T)*(Count - index - 1));
// Now put the new element in
DoDelete (index, index);
ConstructInTArray (&Array[index], item);
// And put the new element in
::new ((void *)&Array[index]) T(item);
}
}
void ShrinkToFit ()
@ -233,7 +197,7 @@ public:
Grow (amount - Count);
for (unsigned int i = Count; i < amount; ++i)
{
ConstructEmptyInTArray (&Array[i]);
::new((void *)&Array[i]) T;
}
}
else if (Count != amount)
@ -293,13 +257,7 @@ private:
void DoResize ()
{
size_t allocsize = sizeof(T)*Most;
T *newarray = (T *)M_Malloc (allocsize);
for (unsigned int i = 0; i < Count; ++i)
{
CopyForTArray (newarray[i], Array[i]);
}
free (Array);
Array = newarray;
Array = (T *)M_Realloc (Array, allocsize);
}
void DoDelete (unsigned int first, unsigned int last)

View file

@ -311,13 +311,10 @@ static bool FixBuildPalette (BYTE *opal, int lump, bool blood)
// Reverse the palette because BUILD used entry 255 as
// transparent, but we use 0 as transparent.
for (int c = 0; c < 768/2; c += 3)
for (int c = 0; c < 768; c += 3)
{
if (!blood)
{
opal[765-c] = (ipal[0] << 2) | (ipal[0] >> 4);
opal[766-c] = (ipal[1] << 2) | (ipal[1] >> 4);
opal[767-c] = (ipal[2] << 2) | (ipal[2] >> 4);
opal[c] = (ipal[765-c] << 2) | (ipal[765-c] >> 4);
opal[c+1] = (ipal[766-c] << 2) | (ipal[766-c] >> 4);
opal[c+2] = (ipal[767-c] << 2) | (ipal[767-c] >> 4);
@ -327,9 +324,6 @@ static bool FixBuildPalette (BYTE *opal, int lump, bool blood)
opal[c] = ipal[765-c];
opal[c+1] = ipal[766-c];
opal[c+2] = ipal[767-c];
opal[765-c] = ipal[c];
opal[766-c] = ipal[c+1];
opal[767-c] = ipal[c+2];
}
}
return true;

View file

@ -634,7 +634,7 @@ BOOL CALLBACK IWADBoxCallback (HWND hDlg, UINT message, WPARAM wParam, LPARAM lP
for (i = 0; i < NumWads; i++)
{
char work[256];
char *filepart = strrchr (WadList[i].Path, '/');
const char *filepart = strrchr (WadList[i].Path, '/');
if (filepart == NULL)
filepart = WadList[i].Path;
else

View file

@ -1,39 +1,74 @@
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <new> // for bad_alloc
#include "zstring.h"
FString::FString (size_t len)
FNullStringData FString::NullString =
{
Chars = Pond.Alloc (this, len);
0, // Length of string
2, // Size of character buffer
2, // RefCount; it must never be modified, so keep it above 1 user at all times
"\0"
};
void FString::AttachToOther (const FString &other)
{
assert (other.Chars != NULL);
if (other.Data()->RefCount < 0)
{
AllocBuffer (other.Data()->Len);
StrCopy (Chars, other.Chars, other.Data()->Len);
}
else
{
Chars = const_cast<FString &>(other).Data()->AddRef();
}
}
FString::FString (const char *copyStr)
{
size_t len = strlen (copyStr);
Chars = Pond.Alloc (this, len);
StrCopy (Chars, copyStr, len);
if (copyStr == NULL || *copyStr == '\0')
{
NullString.RefCount++;
Chars = &NullString.Nothing[0];
}
else
{
size_t len = strlen (copyStr);
AllocBuffer (len);
StrCopy (Chars, copyStr, len);
}
}
FString::FString (const char *copyStr, size_t len)
{
Chars = Pond.Alloc (this, len);
AllocBuffer (len);
StrCopy (Chars, copyStr, len);
}
FString::FString (char oneChar)
{
Chars = Pond.Alloc (this, 1);
Chars[0] = oneChar;
Chars[1] = '\0';
if (oneChar == '\0')
{
NullString.RefCount++;
Chars = &NullString.Nothing[0];
}
else
{
AllocBuffer (1);
Chars[0] = oneChar;
Chars[1] = '\0';
}
}
FString::FString (const FString &head, const FString &tail)
{
size_t len1 = head.Len();
size_t len2 = tail.Len();
Chars = Pond.Alloc (this, len1 + len2);
AllocBuffer (len1 + len2);
StrCopy (Chars, head);
StrCopy (Chars + len1, tail);
}
@ -42,7 +77,7 @@ FString::FString (const FString &head, const char *tail)
{
size_t len1 = head.Len();
size_t len2 = strlen (tail);
Chars = Pond.Alloc (this, len1 + len2);
AllocBuffer (len1 + len2);
StrCopy (Chars, head);
StrCopy (Chars + len1, tail, len2);
}
@ -50,7 +85,7 @@ FString::FString (const FString &head, const char *tail)
FString::FString (const FString &head, char tail)
{
size_t len1 = head.Len();
Chars = Pond.Alloc (this, len1 + 1);
AllocBuffer (len1 + 1);
StrCopy (Chars, head);
Chars[len1] = tail;
Chars[len1+1] = '\0';
@ -60,7 +95,7 @@ FString::FString (const char *head, const FString &tail)
{
size_t len1 = strlen (head);
size_t len2 = tail.Len();
Chars = Pond.Alloc (this, len1 + len2);
AllocBuffer (len1 + len2);
StrCopy (Chars, head, len1);
StrCopy (Chars + len1, tail);
}
@ -69,7 +104,7 @@ FString::FString (const char *head, const char *tail)
{
size_t len1 = strlen (head);
size_t len2 = strlen (tail);
Chars = Pond.Alloc (this, len1 + len2);
AllocBuffer (len1 + len2);
StrCopy (Chars, head, len1);
StrCopy (Chars + len1, tail, len2);
}
@ -77,62 +112,76 @@ FString::FString (const char *head, const char *tail)
FString::FString (char head, const FString &tail)
{
size_t len2 = tail.Len();
Chars = Pond.Alloc (this, 1 + len2);
AllocBuffer (1 + len2);
Chars[0] = head;
StrCopy (Chars + 1, tail);
}
FString::~FString ()
{
if (Chars != NULL)
Data()->Release();
}
char *FString::LockBuffer()
{
if (Data()->RefCount == 1)
{ // We're the only user, so we can lock it straight away
Data()->RefCount = -1;
}
else if (Data()->RefCount < -1)
{ // Already locked; just add to the lock count
Data()->RefCount--;
}
else
{ // Somebody else is also using this character buffer, so create a copy
FStringData *old = Data();
AllocBuffer (old->Len);
StrCopy (Chars, old->Chars(), old->Len);
old->Release();
}
return Chars;
}
void FString::UnlockBuffer()
{
assert (Data()->RefCount < 0);
if (++Data()->RefCount == 0)
{
Pond.Free (Chars);
Chars = NULL;
Data()->RefCount = 1;
}
}
FString &FString::operator = (const FString &other)
{
if (Chars != NULL)
assert (Chars != NULL);
if (&other != this)
{
Pond.Free (Chars);
}
if (other.Chars == NULL)
{
Chars = NULL;
}
else
{
Chars = Pond.Alloc (this, other.GetHeader()->Len);
StrCopy (Chars, other);
int oldrefcount = Data()->RefCount < 0;
Data()->Release();
AttachToOther(other);
if (oldrefcount < 0)
{
LockBuffer();
Data()->RefCount = oldrefcount;
}
}
return *this;
}
FString &FString::operator = (const char *copyStr)
{
if (Chars != NULL)
Data()->Release();
if (copyStr == NULL || *copyStr == '\0')
{
Pond.Free (Chars);
}
if (copyStr == NULL)
{
Chars = NULL;
NullString.RefCount++;
Chars = &NullString.Nothing[0];
}
else
{
size_t len = strlen (copyStr);
/*
if (len == 0)
{
Chars = NULL;
}
else
*/
{
Chars = Pond.Alloc (this, len);
StrCopy (Chars, copyStr, len);
}
AllocBuffer (len);
StrCopy (Chars, copyStr, len);
}
return *this;
}
@ -147,11 +196,8 @@ void FString::Format (const char *fmt, ...)
void FString::VFormat (const char *fmt, va_list arglist)
{
if (Chars != NULL)
{
Pond.Free (Chars);
}
Chars = NULL;
Data()->Release();
Chars = (char *)(FStringData::Alloc(128) + 1);
StringFormat::VWorker (FormatHelper, this, fmt, arglist);
}
@ -159,8 +205,12 @@ int FString::FormatHelper (void *data, const char *cstr, int len)
{
FString *str = (FString *)data;
size_t len1 = str->Len();
str->Chars = Pond.Realloc (str, str->Chars, len1 + len);
if (len1 + len > str->Data()->AllocLen)
{
str->Chars = (char *)(str->Data()->Realloc((len1 + len + 127) & ~127) + 1);
}
StrCopy (str->Chars + len1, cstr, len);
str->Data()->Len = (unsigned int)(len1 + len);
return len;
}
@ -193,7 +243,7 @@ FString &FString::operator += (const FString &tail)
{
size_t len1 = Len();
size_t len2 = tail.Len();
Chars = Pond.Realloc (this, Chars, len1 + len2);
ReallocBuffer (len1 + len2);
StrCopy (Chars + len1, tail);
return *this;
}
@ -202,7 +252,7 @@ FString &FString::operator += (const char *tail)
{
size_t len1 = Len();
size_t len2 = strlen(tail);
Chars = Pond.Realloc (this, Chars, len1 + len2);
ReallocBuffer (len1 + len2);
StrCopy (Chars + len1, tail, len2);
return *this;
}
@ -210,17 +260,17 @@ FString &FString::operator += (const char *tail)
FString &FString::operator += (char tail)
{
size_t len1 = Len();
Chars = Pond.Realloc (this, Chars, len1 + 1);
ReallocBuffer (len1 + 1);
Chars[len1] = tail;
Chars[len1+1] = '\0';
return *this;
}
void FString::Resize (long newlen)
void FString::Truncate (long newlen)
{
if (newlen >= 0)
if (newlen >= 0 && newlen < (long)Len())
{
Chars = Pond.Realloc (this, Chars, newlen);
ReallocBuffer (newlen);
Chars[newlen] = '\0';
}
}
@ -250,7 +300,7 @@ FString FString::Mid (size_t pos, size_t numChars) const
size_t len = Len();
if (pos >= len)
{
return FString("");
return FString();
}
if (pos + numChars > len)
{
@ -402,24 +452,29 @@ long FString::LastIndexOfAny (const char *charset, long endIndex) const
void FString::ToUpper ()
{
LockBuffer();
size_t max = Len();
for (size_t i = 0; i < max; ++i)
{
Chars[i] = toupper(Chars[i]);
}
UnlockBuffer();
}
void FString::ToLower ()
{
LockBuffer();
size_t max = Len();
for (size_t i = 0; i < max; ++i)
{
Chars[i] = tolower(Chars[i]);
}
UnlockBuffer();
}
void FString::SwapCase ()
{
LockBuffer();
size_t max = Len();
for (size_t i = 0; i < max; ++i)
{
@ -432,6 +487,7 @@ void FString::SwapCase ()
Chars[i] = toupper(Chars[i]);
}
}
UnlockBuffer();
}
void FString::StripLeft ()
@ -442,11 +498,21 @@ void FString::StripLeft ()
if (!isspace(Chars[i]))
break;
}
for (j = 0; i <= max; ++j, ++i)
if (Data()->RefCount <= 1)
{
Chars[j] = Chars[i];
for (j = 0; i <= max; ++j, ++i)
{
Chars[j] = Chars[i];
}
ReallocBuffer (j-1);
}
else
{
FStringData *old = Data();
AllocBuffer (max - i);
StrCopy (Chars, old->Chars() + i, max - i);
old->Release();
}
Pond.Realloc (this, Chars, j-1);
}
void FString::StripLeft (const FString &charset)
@ -462,11 +528,21 @@ void FString::StripLeft (const char *charset)
if (!strchr (charset, Chars[i]))
break;
}
for (j = 0; i <= max; ++j, ++i)
if (Data()->RefCount <= 1)
{
Chars[j] = Chars[i];
for (j = 0; i <= max; ++j, ++i)
{
Chars[j] = Chars[i];
}
ReallocBuffer (j-1);
}
else
{
FStringData *old = Data();
AllocBuffer (max - i);
StrCopy (Chars, old->Chars() + i, max - i);
old->Release();
}
Pond.Realloc (this, Chars, j-1);
}
void FString::StripRight ()
@ -477,8 +553,18 @@ void FString::StripRight ()
if (!isspace(Chars[i]))
break;
}
Chars[i+1] = '\0';
Pond.Realloc (this, Chars, i+1);
if (Data()->RefCount <= 1)
{
Chars[i+1] = '\0';
ReallocBuffer (i+1);
}
else
{
FStringData *old = Data();
AllocBuffer (i+1);
StrCopy (Chars, old->Chars(), i+1);
old->Release();
}
}
void FString::StripRight (const FString &charset)
@ -494,8 +580,18 @@ void FString::StripRight (const char *charset)
if (!strchr (charset, Chars[i]))
break;
}
Chars[i+1] = '\0';
Pond.Realloc (this, Chars, i+1);
if (Data()->RefCount <= 1)
{
Chars[i+1] = '\0';
ReallocBuffer (i+1);
}
else
{
FStringData *old = Data();
AllocBuffer (i+1);
StrCopy (Chars, old->Chars(), i+1);
old->Release();
}
}
void FString::StripLeftRight ()
@ -511,17 +607,27 @@ void FString::StripLeftRight ()
if (!isspace(Chars[j]))
break;
}
for (k = 0; i <= j; ++i, ++k)
if (Data()->RefCount <= 1)
{
Chars[k] = Chars[i];
for (k = 0; i <= j; ++i, ++k)
{
Chars[k] = Chars[i];
}
Chars[k] = '\0';
ReallocBuffer (k);
}
else
{
FStringData *old = Data();
AllocBuffer (j - i);
StrCopy (Chars, old->Chars(), j - i);
old->Release();
}
Chars[k] = '\0';
Pond.Realloc (this, Chars, k);
}
void FString::StripLeftRight (const FString &charset)
{
return StripLeft (charset.Chars);
return StripLeftRight (charset.Chars);
}
void FString::StripLeftRight (const char *charset)
@ -537,12 +643,22 @@ void FString::StripLeftRight (const char *charset)
if (!strchr (charset, Chars[j]))
break;
}
for (k = 0; i <= j; ++i, ++k)
if (Data()->RefCount <= 1)
{
Chars[k] = Chars[i];
for (k = 0; i <= j; ++i, ++k)
{
Chars[k] = Chars[i];
}
Chars[k] = '\0';
ReallocBuffer (k);
}
else
{
FStringData *old = Data();
AllocBuffer (j - i);
StrCopy (Chars, old->Chars(), j - i);
old->Release();
}
Chars[k] = '\0';
Pond.Realloc (this, Chars, k);
}
void FString::Insert (size_t index, const FString &instr)
@ -562,21 +678,28 @@ void FString::Insert (size_t index, const char *instr, size_t instrlen)
{
index = mylen;
}
Pond.Realloc (this, Chars, mylen + instrlen);
if (index < mylen)
if (Data()->RefCount <= 1)
{
ReallocBuffer (mylen + instrlen);
memmove (Chars + index + instrlen, Chars + index, (mylen - index + 1)*sizeof(char));
memcpy (Chars + index, instr, instrlen*sizeof(char));
}
memcpy (Chars + index, instr, instrlen*sizeof(char));
if (index == mylen)
else
{
Chars[mylen + instrlen] = '\0';
FStringData *old = Data();
AllocBuffer (mylen + instrlen);
StrCopy (Chars, old->Chars(), index);
StrCopy (Chars + index, instr, instrlen);
StrCopy (Chars + index + instrlen, Chars + index, mylen - index + 1);
old->Release();
}
}
void FString::ReplaceChars (char oldchar, char newchar)
{
size_t i, j;
LockBuffer();
for (i = 0, j = Len(); i < j; ++i)
{
if (Chars[i] == oldchar)
@ -584,11 +707,14 @@ void FString::ReplaceChars (char oldchar, char newchar)
Chars[i] = newchar;
}
}
UnlockBuffer();
}
void FString::ReplaceChars (const char *oldcharset, char newchar)
{
size_t i, j;
LockBuffer();
for (i = 0, j = Len(); i < j; ++i)
{
if (strchr (oldcharset, Chars[i]) != NULL)
@ -596,11 +722,14 @@ void FString::ReplaceChars (const char *oldcharset, char newchar)
Chars[i] = newchar;
}
}
UnlockBuffer();
}
void FString::StripChars (char killchar)
{
size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; ++read)
{
if (Chars[read] != killchar)
@ -609,12 +738,15 @@ void FString::StripChars (char killchar)
}
}
Chars[write] = '\0';
Pond.Realloc (this, Chars, write);
ReallocBuffer (write);
UnlockBuffer();
}
void FString::StripChars (const char *killchars)
{
size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; ++read)
{
if (strchr (killchars, Chars[read]) == NULL)
@ -623,7 +755,8 @@ void FString::StripChars (const char *killchars)
}
}
Chars[write] = '\0';
Pond.Realloc (this, Chars, write);
ReallocBuffer (write);
UnlockBuffer();
}
void FString::MergeChars (char merger)
@ -634,6 +767,8 @@ void FString::MergeChars (char merger)
void FString::MergeChars (char merger, char newchar)
{
size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; )
{
if (Chars[read] == merger)
@ -649,12 +784,15 @@ void FString::MergeChars (char merger, char newchar)
}
}
Chars[write] = '\0';
Pond.Realloc (this, Chars, write);
ReallocBuffer (write);
UnlockBuffer();
}
void FString::MergeChars (const char *charset, char newchar)
{
size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; )
{
if (strchr (charset, Chars[read]) != NULL)
@ -670,7 +808,8 @@ void FString::MergeChars (const char *charset, char newchar)
}
}
Chars[write] = '\0';
Pond.Realloc (this, Chars, write);
ReallocBuffer (write);
UnlockBuffer();
}
void FString::Substitute (const FString &oldstr, const FString &newstr)
@ -695,6 +834,7 @@ void FString::Substitute (const char *oldstr, const char *newstr)
void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrlen, size_t newstrlen)
{
LockBuffer();
for (size_t checkpt = 0; checkpt < Len(); )
{
char *match = strstr (Chars + checkpt, oldstr);
@ -704,7 +844,7 @@ void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrl
size_t matchpt = match - Chars;
if (oldstrlen != newstrlen)
{
Pond.Realloc (this, Chars, len + newstrlen - oldstrlen);
ReallocBuffer (len + newstrlen - oldstrlen);
memmove (Chars + matchpt + newstrlen, Chars + matchpt + oldstrlen, (len + 1 - matchpt - oldstrlen)*sizeof(char));
}
memcpy (Chars + matchpt, newstr, newstrlen);
@ -715,6 +855,7 @@ void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrl
break;
}
}
UnlockBuffer();
}
bool FString::IsInt () const
@ -842,12 +983,6 @@ double FString::ToDouble () const
return strtod (Chars, NULL);
}
FString::StringHeader *FString::GetHeader () const
{
if (Chars == NULL) return NULL;
return (StringHeader *)(Chars - sizeof(StringHeader));
}
void FString::StrCopy (char *to, const char *from, size_t len)
{
memcpy (to, from, len*sizeof(char));
@ -858,3 +993,106 @@ void FString::StrCopy (char *to, const FString &from)
{
StrCopy (to, from.Chars, from.Len());
}
void FString::AllocBuffer (size_t len)
{
Chars = (char *)(FStringData::Alloc(len) + 1);
Data()->Len = (unsigned int)len;
}
void FString::ReallocBuffer (size_t newlen)
{
if (Data()->RefCount > 1)
{ // If more than one reference, we must use a new copy
FStringData *old = Data();
AllocBuffer (newlen);
StrCopy (Chars, old->Chars(), old->Len);
old->Release();
}
else
{
if (newlen > Data()->AllocLen)
{
Chars = (char *)(Data()->Realloc(newlen) + 1);
}
Data()->Len = (unsigned int)newlen;
}
}
// Under Windows, use the system heap functions for managing string memory.
// Under other OSs, use ordinary memory management instead.
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
static HANDLE StringHeap;
#endif
FStringData *FStringData::Alloc (size_t strlen)
{
strlen += 1 + sizeof(FStringData); // Add space for header and terminating null
strlen = (strlen + 7) & ~7; // Pad length up
#ifdef _WIN32
if (StringHeap == NULL)
{
StringHeap = HeapCreate (0, 64*1024, 0);
if (StringHeap == NULL)
{
throw std::bad_alloc();
}
}
FStringData *block = (FStringData *)HeapAlloc (StringHeap, 0, strlen);
#else
FStringData *block = (FStringData *)malloc (strlen);
#endif
if (block == NULL)
{
throw std::bad_alloc();
}
block->Len = 0;
block->AllocLen = (unsigned int)strlen - sizeof(FStringData) - 1;
block->RefCount = 1;
return block;
}
FStringData *FStringData::Realloc (size_t newstrlen)
{
assert (RefCount <= 1);
newstrlen += 1 + sizeof(FStringData); // Add space for header and terminating null
newstrlen = (newstrlen + 7) & ~7; // Pad length up
#ifdef _WIN32
FStringData *block = (FStringData *)HeapReAlloc (StringHeap, 0, this, newstrlen);
#else
FStringData *block = (FStringData *)realloc (this, newstrlen);
#endif
if (block == NULL)
{
throw std::bad_alloc();
}
block->AllocLen = (unsigned int)newstrlen - sizeof(FStringData) - 1;
return block;
}
void FStringData::Dealloc ()
{
assert (RefCount <= 0);
#ifdef _WIN32
HeapFree (StringHeap, 0, this);
#else
free (this);
#endif
}
FStringData *FStringData::MakeCopy ()
{
FStringData *copy = Alloc (Len);
copy->Len = Len;
FString::StrCopy (copy->Chars(), Chars(), Len);
return copy;
}

View file

@ -4,16 +4,71 @@
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <stddef.h>
#include "tarray.h"
//#define NOPOOLS
struct FStringData
{
unsigned int Len; // Length of string, excluding terminating null
unsigned int AllocLen; // Amount of memory allocated for string
int RefCount; // < 0 means it's locked
// char StrData[xxx];
char *Chars()
{
return (char *)(this + 1);
}
const char *Chars() const
{
return (const char *)(this + 1);
}
char *AddRef()
{
if (RefCount < 0)
{
return (char *)(MakeCopy() + 1);
}
else
{
RefCount++;
return (char *)(this + 1);
}
}
void Release()
{
assert (RefCount != 0);
if (--RefCount <= 0)
{
Dealloc();
}
}
FStringData *MakeCopy();
static FStringData *Alloc (size_t strlen);
FStringData *Realloc (size_t newstrlen);
void Dealloc ();
};
struct FNullStringData
{
unsigned int Len;
unsigned int AllocLen;
int RefCount;
char Nothing[2];
};
class FString
{
public:
FString () : Chars(NULL) {}
FString () : Chars(&NullString.Nothing[0]) { NullString.RefCount++; }
// Copy constructors
FString (const FString &other) { Chars = NULL; *this = other; }
FString (const FString &other) { AttachToOther (other); }
FString (const char *copyStr);
FString (const char *copyStr, size_t copyLen);
FString (char oneChar);
@ -28,12 +83,14 @@ public:
~FString ();
operator char *() { return Chars; }
char *LockBuffer(); // Obtain write access to the character buffer
void UnlockBuffer(); // Allow shared access to the character buffer
operator const char *() const { return Chars; }
char *GetChars() const { return Chars; }
char &operator[] (int index) { return Chars[index]; }
char &operator[] (size_t index) { return Chars[index]; }
const char *GetChars() const { return Chars; }
const char &operator[] (int index) const { return Chars[index]; }
const char &operator[] (size_t index) const { return Chars[index]; }
FString &operator = (const FString &other);
FString &operator = (const char *copyStr);
@ -117,10 +174,10 @@ public:
unsigned long ToULong (int base=0) const;
double ToDouble () const;
size_t Len() const { return Chars == NULL ? 0 : ((StringHeader *)(Chars - sizeof(StringHeader)))->Len; }
size_t Len() const { return Data()->Len; }
bool IsEmpty() const { return Len() == 0; }
void Resize (long newlen);
void Truncate (long newlen);
int Compare (const FString &other) const { return strcmp (Chars, other.Chars); }
int Compare (const char *other) const { return strcmp (Chars, other); }
@ -129,81 +186,24 @@ public:
int CompareNoCase (const char *other) const { return stricmp (Chars, other); }
protected:
struct StringHeader
{
#ifndef NOPOOLS
FString *Owner; // FString this char array belongs to
#endif
size_t Len; // Length of FString, excluding terminating null
};
struct Pool;
struct PoolGroup
{
#ifndef NOPOOLS
~PoolGroup ();
Pool *Pools;
Pool *FindPool (char *chars) const;
static StringHeader *GetHeader (char *chars);
#endif
char *Alloc (FString *owner, size_t len);
char *Realloc (FString *owner, char *chars, size_t newlen);
void Free (char *chars);
};
const FStringData *Data() const { return (FStringData *)Chars - 1; }
FStringData *Data() { return (FStringData *)Chars - 1; }
FString (size_t len);
StringHeader *GetHeader () const;
void AttachToOther (const FString &other);
void AllocBuffer (size_t len);
void ReallocBuffer (size_t newlen);
static int FormatHelper (void *data, const char *str, int len);
static void StrCopy (char *to, const char *from, size_t len);
static void StrCopy (char *to, const FString &from);
static PoolGroup Pond;
char *Chars;
#ifndef __GNUC__
template<> friend void CopyForTArray<FString> (FString &dst, FString &src)
{
// When a TArray is resized, we just need to update the Owner, because
// the old copy is going to go away very soon. No need to call the
// destructor, either, because full ownership is transferred to the
// new FString.
char *chars = src.Chars;
dst.Chars = chars;
if (chars != NULL)
{
((FString::StringHeader *)(chars - sizeof(FString::StringHeader)))->Owner = &dst;
}
}
#else
template<class FString> friend inline void CopyForTArray (FString &dst, FString &src);
#endif
static FNullStringData NullString;
private:
void *operator new (size_t size, FString *addr)
{
return addr;
}
void operator delete (void *, FString *)
{
}
friend struct FStringData;
};
#ifdef __GNUC__
template<> inline void CopyForTArray<FString> (FString &dst, FString &src)
{
// When a TArray is resized, we just need to update the Owner, because
// the old copy is going to go away very soon. No need to call the
// destructor, either, because full ownership is transferred to the
// new FString.
char *chars = src.Chars;
dst.Chars = chars;
if (chars != NULL)
{
((FString::StringHeader *)(chars - sizeof(FString::StringHeader)))->Owner = &dst;
}
}
#endif
namespace StringFormat
{
enum

View file

@ -1,448 +0,0 @@
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "zstring.h"
FString::PoolGroup FString::Pond;
#ifndef NOPOOLS
struct FString::Pool
{
// The pool's performance (and thus the FString 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 FString 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 (FString *owner, size_t len);
char *Realloc (char *chars, size_t newlen);
void Free (char *chars);
void MergeFreeBlocks (StringHeader *block);
void CollectGarbage ();
bool BigEnough (size_t len) const;
size_t RoundLen (size_t len) const;
Pool *Next;
size_t FreeSpace;
char *PoolData;
char *MaxAlloc;
StringHeader *NextAlloc;
};
// 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.
FString::PoolGroup::~PoolGroup ()
{
int count = 0;
Pool *pool = Pools, *next;
while (pool != NULL)
{
count++;
next = pool->Next;
delete pool;
pool = next;
}
Pools = NULL;
}
char *FString::PoolGroup::Alloc (FString *owner, size_t len)
{
char *mem;
Pool *pool, *best, **prev, **bestprev;
// If no pools, create one
if (Pools == NULL)
{
Pools = new FString::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 ();
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 FString, so create a new one
pool = new FString::Pool (len);
pool->Next = Pools;
Pools = pool;
mem = pool->Alloc (owner, len);
}
return mem;
}
char *FString::PoolGroup::Realloc (FString *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 FString::PoolGroup::Free (char *chars)
{
Pool *pool = FindPool (chars);
if (pool != NULL)
{
pool->Free (chars);
}
}
FString::Pool *FString::PoolGroup::FindPool (char *chars) const
{
Pool *pool = Pools;
while (pool != NULL)
{
if (pool->PoolData <= chars && pool->MaxAlloc > chars)
{
break;
}
pool = pool->Next;
}
return pool;
}
FString::StringHeader *FString::PoolGroup::GetHeader (char *chars)
{
return (StringHeader *)(chars - sizeof(StringHeader));
}
FString::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;
}
FString::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;
StringHeader *laststr;
for (str = (StringHeader *)PoolData; str < NextAlloc; )
{
if (str->Owner != NULL)
{
FString *owner = str->Owner;
// assert (owner->Chars == (char *)str + sizeof(StringHeader));
Free ((char *)str + sizeof(StringHeader));
owner->Chars = "";
}
laststr = str;
str = (StringHeader *)((char *)str + str->Len);
}
delete[] PoolData;
PoolData = NULL;
}
}
char *FString::Pool::Alloc (FString *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 *FString::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 FString, 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 FString
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 FString 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)
{
FString::StrCopy (newchars, chars, oldhead->Len);
Free (chars);
return newchars;
}
// There was not enough space
return NULL;
}
void FString::Pool::Free (char *chars)
{
#ifdef _DEBUG
for (StringHeader *str = (StringHeader *)PoolData; str < NextAlloc; )
{
if (str->Owner != NULL)
{
// assert (str->Owner->Chars == (char *)str + sizeof(StringHeader));
str = (StringHeader *)((char *)str + RoundLen(str->Len));
}
else
{
str = (StringHeader *)((char *)str + str->Len);
}
}
#endif
StringHeader *head = (StringHeader *)(chars - sizeof(StringHeader));
size_t truelen = RoundLen (head->Len);
FreeSpace += truelen;
head->Owner = NULL;
head->Len = truelen;
MergeFreeBlocks (head);
#ifdef _DEBUG
memset (head + 1, 0xCE, head->Len - sizeof(StringHeader));
#endif
#ifdef _DEBUG
for (StringHeader *str = (StringHeader *)PoolData; str < NextAlloc; )
{
if (str->Owner != NULL)
{
// assert (str->Owner->Chars == (char *)str + sizeof(StringHeader));
str = (StringHeader *)((char *)str + RoundLen(str->Len));
}
else
{
str = (StringHeader *)((char *)str + str->Len);
}
}
#endif
}
void FString::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)
{
NextAlloc = head;
head->Len = MaxAlloc - (char *)head;
}
else
{
head->Len = (char *)block - (char *)head;
}
}
bool FString::Pool::BigEnough (size_t len) const
{
return FreeSpace >= RoundLen (len);
}
size_t FString::Pool::RoundLen (size_t len) const
{
return (len + 1 + sizeof(StringHeader) + BLOCK_GRANULARITY - 1) & ~(BLOCK_GRANULARITY - 1);
}
void FString::Pool::CollectGarbage ()
{
// 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.
StringHeader *moveto, *movefrom;
moveto = movefrom = (StringHeader *)PoolData;
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;
}
#else
char *FString::PoolGroup::Alloc (FString *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 *FString::PoolGroup::Realloc (FString *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 FString::PoolGroup::Free (char *chars)
{
free (chars - sizeof(StringHeader));
}
#endif

View file

@ -1766,9 +1766,6 @@
<File
RelativePath=".\src\zstring.cpp">
</File>
<File
RelativePath=".\src\zstringpool.cpp">
</File>
<Filter
Name="Decorate++"
Filter="">