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 \ vectors.cpp \
name.cpp \ name.cpp \
zstring.cpp \ zstring.cpp \
zstringpool.cpp \
zstrformat.cpp \ zstrformat.cpp \
w_wad.cpp \ w_wad.cpp \
wi_stuff.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) May 14, 2006 (Changes by Graf Zahl)
- Added a show_obituaries option to disable obituaries without disabling - Added a show_obituaries option to disable obituaries without disabling
other more important message types which have lower priority for some 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->inuse = false;
bot->lastteam = keepTeam ? players[i].userinfo.team : TEAM_None; 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; playeringame[i] = false;
} }

View file

@ -509,7 +509,7 @@ void C_SetDefaultBindings ()
BOOL C_DoKey (event_t *ev) BOOL C_DoKey (event_t *ev)
{ {
char *binding = NULL; FString binding;
bool dclick; bool dclick;
int dclickspot; int dclickspot;
byte dclickmask; byte dclickmask;
@ -553,33 +553,27 @@ BOOL C_DoKey (event_t *ev)
} }
if (binding == NULL || *binding==0) if (binding.IsEmpty())
{ {
binding = Bindings[ev->data1]; binding = Bindings[ev->data1];
dclick = false; 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;
{ }
return false;
} char *copy = binding.LockBuffer();
binding[0] = '-';
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); AddCommandString (copy, dclick ? ev->data1 | KEY_DBLCLICKED : ev->data1);
if (ev->type == EV_KeyUp)
{
binding[0] = '+';
}
return true; return true;
} }
return false; 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; return (unsigned int)key < NUM_KEYS ? Bindings[key].GetChars() : NULL;
} }

View file

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

View file

@ -146,7 +146,7 @@ void DefaultExtension (char *path, const char *extension)
void DefaultExtension (FString &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)) while (src != &path[0] && !IsSeperator(*src))
{ {

View file

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

View file

@ -77,6 +77,8 @@ enum EIWADType
struct WadStuff struct WadStuff
{ {
WadStuff() : Type(IWAD_Doom2TNT) {}
FString Path; FString Path;
EIWADType Type; 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 // 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. // 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, '\\'); char *slash = strrchr (fileonly, '\\');
if (slash != NULL) if (slash != NULL)
{ {

View file

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

View file

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

View file

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

View file

@ -37,7 +37,7 @@ void G_DeferedPlayDemo (char* demo);
// Can be called by the startup code or M_Responder, // Can be called by the startup code or M_Responder,
// calls P_SetupLevel or W_EnterWorld. // calls P_SetupLevel or W_EnterWorld.
void G_LoadGame (char* name); void G_LoadGame (const char* name);
void G_DoLoadGame (void); 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 *tempPlayerUsed = new BYTE[numPlayers];
BYTE *playerUsed = new BYTE[MAXPLAYERS]; BYTE *playerUsed = new BYTE[MAXPLAYERS];
memset (playertemp, 0, numPlayers*sizeof(player_t));
for (i = 0; i < numPlayers; ++i) for (i = 0; i < numPlayers; ++i)
{ {
nametemp[i] = NULL; nametemp[i] = NULL;

View file

@ -67,6 +67,102 @@ static TArray<sector_t *> PredictionTouchingSectorsBackup;
BOOL onground; 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 // This function supplements the pointer cleanup in dobject.cpp, because
// player_s is not derived from DObject. (I tried it, and DestroyScan was // player_s is not derived from DObject. (I tried it, and DestroyScan was

View file

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

View file

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

View file

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

View file

@ -38,37 +38,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <new> #include <new>
#include "m_alloc.h" #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> template <class T>
class TArray class TArray
{ {
@ -140,7 +112,7 @@ public:
unsigned int Push (const T &item) unsigned int Push (const T &item)
{ {
Grow (1); Grow (1);
ConstructInTArray (&Array[Count], item); ::new((void*)&Array[Count]) T(item);
return Count++; return Count++;
} }
bool Pop (T &item) bool Pop (T &item)
@ -148,7 +120,7 @@ public:
if (Count > 0) if (Count > 0)
{ {
item = Array[--Count]; item = Array[--Count];
DoDelete (Count, Count); Array[Count].~T();
return true; return true;
} }
return false; return false;
@ -157,12 +129,9 @@ public:
{ {
if (index < Count) if (index < Count)
{ {
for (unsigned int i = index; i < Count - 1; ++i) Array[index].~T();
{ memmove (&Array[index], &Array[index+1], Count - index - 1);
Array[i] = Array[i+1];
}
Count--; Count--;
DoDelete (Count, Count);
} }
} }
// Inserts an item into the array, shifting elements as needed // 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 // Inserting somewhere past the end of the array, so we can
// just add it without moving things. // just add it without moving things.
Resize (index + 1); Resize (index + 1);
ConstructInTArray (&Array[index], item); ::new ((void *)&Array[index]) T(item);
} }
else else
{ {
// Inserting somewhere in the middle of the array, so make // Inserting somewhere in the middle of the array,
// room for it and shift old entries out of the way. // so make room for it
Resize (Count + 1); Resize (Count + 1);
// Now copy items from the index and onward // Now move items from the index and onward out of the way
for (unsigned int i = Count - 1; i-- > index; ) memmove (&Array[index+1], &Array[index], sizeof(T)*(Count - index - 1));
{
Array[i+1] = Array[i];
}
// Now put the new element in // And put the new element in
DoDelete (index, index); ::new ((void *)&Array[index]) T(item);
ConstructInTArray (&Array[index], item);
} }
} }
void ShrinkToFit () void ShrinkToFit ()
@ -233,7 +197,7 @@ public:
Grow (amount - Count); Grow (amount - Count);
for (unsigned int i = Count; i < amount; ++i) for (unsigned int i = Count; i < amount; ++i)
{ {
ConstructEmptyInTArray (&Array[i]); ::new((void *)&Array[i]) T;
} }
} }
else if (Count != amount) else if (Count != amount)
@ -293,13 +257,7 @@ private:
void DoResize () void DoResize ()
{ {
size_t allocsize = sizeof(T)*Most; size_t allocsize = sizeof(T)*Most;
T *newarray = (T *)M_Malloc (allocsize); Array = (T *)M_Realloc (Array, allocsize);
for (unsigned int i = 0; i < Count; ++i)
{
CopyForTArray (newarray[i], Array[i]);
}
free (Array);
Array = newarray;
} }
void DoDelete (unsigned int first, unsigned int last) 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 // Reverse the palette because BUILD used entry 255 as
// transparent, but we use 0 as transparent. // 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) 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] = (ipal[765-c] << 2) | (ipal[765-c] >> 4);
opal[c+1] = (ipal[766-c] << 2) | (ipal[766-c] >> 4); opal[c+1] = (ipal[766-c] << 2) | (ipal[766-c] >> 4);
opal[c+2] = (ipal[767-c] << 2) | (ipal[767-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] = ipal[765-c];
opal[c+1] = ipal[766-c]; opal[c+1] = ipal[766-c];
opal[c+2] = ipal[767-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; 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++) for (i = 0; i < NumWads; i++)
{ {
char work[256]; char work[256];
char *filepart = strrchr (WadList[i].Path, '/'); const char *filepart = strrchr (WadList[i].Path, '/');
if (filepart == NULL) if (filepart == NULL)
filepart = WadList[i].Path; filepart = WadList[i].Path;
else else

View file

@ -1,39 +1,74 @@
#include <stdlib.h> #include <stdlib.h>
#include <ctype.h> #include <ctype.h>
#include <string.h> #include <string.h>
#include <new> // for bad_alloc
#include "zstring.h" #include "zstring.h"
FNullStringData FString::NullString =
FString::FString (size_t len)
{ {
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) FString::FString (const char *copyStr)
{ {
size_t len = strlen (copyStr); if (copyStr == NULL || *copyStr == '\0')
Chars = Pond.Alloc (this, len); {
StrCopy (Chars, copyStr, len); 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) FString::FString (const char *copyStr, size_t len)
{ {
Chars = Pond.Alloc (this, len); AllocBuffer (len);
StrCopy (Chars, copyStr, len); StrCopy (Chars, copyStr, len);
} }
FString::FString (char oneChar) FString::FString (char oneChar)
{ {
Chars = Pond.Alloc (this, 1); if (oneChar == '\0')
Chars[0] = oneChar; {
Chars[1] = '\0'; NullString.RefCount++;
Chars = &NullString.Nothing[0];
}
else
{
AllocBuffer (1);
Chars[0] = oneChar;
Chars[1] = '\0';
}
} }
FString::FString (const FString &head, const FString &tail) FString::FString (const FString &head, const FString &tail)
{ {
size_t len1 = head.Len(); size_t len1 = head.Len();
size_t len2 = tail.Len(); size_t len2 = tail.Len();
Chars = Pond.Alloc (this, len1 + len2); AllocBuffer (len1 + len2);
StrCopy (Chars, head); StrCopy (Chars, head);
StrCopy (Chars + len1, tail); StrCopy (Chars + len1, tail);
} }
@ -42,7 +77,7 @@ FString::FString (const FString &head, const char *tail)
{ {
size_t len1 = head.Len(); size_t len1 = head.Len();
size_t len2 = strlen (tail); size_t len2 = strlen (tail);
Chars = Pond.Alloc (this, len1 + len2); AllocBuffer (len1 + len2);
StrCopy (Chars, head); StrCopy (Chars, head);
StrCopy (Chars + len1, tail, len2); StrCopy (Chars + len1, tail, len2);
} }
@ -50,7 +85,7 @@ FString::FString (const FString &head, const char *tail)
FString::FString (const FString &head, char tail) FString::FString (const FString &head, char tail)
{ {
size_t len1 = head.Len(); size_t len1 = head.Len();
Chars = Pond.Alloc (this, len1 + 1); AllocBuffer (len1 + 1);
StrCopy (Chars, head); StrCopy (Chars, head);
Chars[len1] = tail; Chars[len1] = tail;
Chars[len1+1] = '\0'; Chars[len1+1] = '\0';
@ -60,7 +95,7 @@ FString::FString (const char *head, const FString &tail)
{ {
size_t len1 = strlen (head); size_t len1 = strlen (head);
size_t len2 = tail.Len(); size_t len2 = tail.Len();
Chars = Pond.Alloc (this, len1 + len2); AllocBuffer (len1 + len2);
StrCopy (Chars, head, len1); StrCopy (Chars, head, len1);
StrCopy (Chars + len1, tail); StrCopy (Chars + len1, tail);
} }
@ -69,7 +104,7 @@ FString::FString (const char *head, const char *tail)
{ {
size_t len1 = strlen (head); size_t len1 = strlen (head);
size_t len2 = strlen (tail); size_t len2 = strlen (tail);
Chars = Pond.Alloc (this, len1 + len2); AllocBuffer (len1 + len2);
StrCopy (Chars, head, len1); StrCopy (Chars, head, len1);
StrCopy (Chars + len1, tail, len2); StrCopy (Chars + len1, tail, len2);
} }
@ -77,62 +112,76 @@ FString::FString (const char *head, const char *tail)
FString::FString (char head, const FString &tail) FString::FString (char head, const FString &tail)
{ {
size_t len2 = tail.Len(); size_t len2 = tail.Len();
Chars = Pond.Alloc (this, 1 + len2); AllocBuffer (1 + len2);
Chars[0] = head; Chars[0] = head;
StrCopy (Chars + 1, tail); StrCopy (Chars + 1, tail);
} }
FString::~FString () 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); Data()->RefCount = 1;
Chars = NULL;
} }
} }
FString &FString::operator = (const FString &other) FString &FString::operator = (const FString &other)
{ {
if (Chars != NULL) assert (Chars != NULL);
if (&other != this)
{ {
Pond.Free (Chars); int oldrefcount = Data()->RefCount < 0;
} Data()->Release();
if (other.Chars == NULL) AttachToOther(other);
{ if (oldrefcount < 0)
Chars = NULL; {
} LockBuffer();
else Data()->RefCount = oldrefcount;
{ }
Chars = Pond.Alloc (this, other.GetHeader()->Len);
StrCopy (Chars, other);
} }
return *this; return *this;
} }
FString &FString::operator = (const char *copyStr) FString &FString::operator = (const char *copyStr)
{ {
if (Chars != NULL) Data()->Release();
if (copyStr == NULL || *copyStr == '\0')
{ {
Pond.Free (Chars); NullString.RefCount++;
} Chars = &NullString.Nothing[0];
if (copyStr == NULL)
{
Chars = NULL;
} }
else else
{ {
size_t len = strlen (copyStr); size_t len = strlen (copyStr);
/* AllocBuffer (len);
if (len == 0) StrCopy (Chars, copyStr, len);
{
Chars = NULL;
}
else
*/
{
Chars = Pond.Alloc (this, len);
StrCopy (Chars, copyStr, len);
}
} }
return *this; return *this;
} }
@ -147,11 +196,8 @@ void FString::Format (const char *fmt, ...)
void FString::VFormat (const char *fmt, va_list arglist) void FString::VFormat (const char *fmt, va_list arglist)
{ {
if (Chars != NULL) Data()->Release();
{ Chars = (char *)(FStringData::Alloc(128) + 1);
Pond.Free (Chars);
}
Chars = NULL;
StringFormat::VWorker (FormatHelper, this, fmt, arglist); StringFormat::VWorker (FormatHelper, this, fmt, arglist);
} }
@ -159,8 +205,12 @@ int FString::FormatHelper (void *data, const char *cstr, int len)
{ {
FString *str = (FString *)data; FString *str = (FString *)data;
size_t len1 = str->Len(); 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); StrCopy (str->Chars + len1, cstr, len);
str->Data()->Len = (unsigned int)(len1 + len);
return len; return len;
} }
@ -193,7 +243,7 @@ FString &FString::operator += (const FString &tail)
{ {
size_t len1 = Len(); size_t len1 = Len();
size_t len2 = tail.Len(); size_t len2 = tail.Len();
Chars = Pond.Realloc (this, Chars, len1 + len2); ReallocBuffer (len1 + len2);
StrCopy (Chars + len1, tail); StrCopy (Chars + len1, tail);
return *this; return *this;
} }
@ -202,7 +252,7 @@ FString &FString::operator += (const char *tail)
{ {
size_t len1 = Len(); size_t len1 = Len();
size_t len2 = strlen(tail); size_t len2 = strlen(tail);
Chars = Pond.Realloc (this, Chars, len1 + len2); ReallocBuffer (len1 + len2);
StrCopy (Chars + len1, tail, len2); StrCopy (Chars + len1, tail, len2);
return *this; return *this;
} }
@ -210,17 +260,17 @@ FString &FString::operator += (const char *tail)
FString &FString::operator += (char tail) FString &FString::operator += (char tail)
{ {
size_t len1 = Len(); size_t len1 = Len();
Chars = Pond.Realloc (this, Chars, len1 + 1); ReallocBuffer (len1 + 1);
Chars[len1] = tail; Chars[len1] = tail;
Chars[len1+1] = '\0'; Chars[len1+1] = '\0';
return *this; 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'; Chars[newlen] = '\0';
} }
} }
@ -250,7 +300,7 @@ FString FString::Mid (size_t pos, size_t numChars) const
size_t len = Len(); size_t len = Len();
if (pos >= len) if (pos >= len)
{ {
return FString(""); return FString();
} }
if (pos + numChars > len) if (pos + numChars > len)
{ {
@ -402,24 +452,29 @@ long FString::LastIndexOfAny (const char *charset, long endIndex) const
void FString::ToUpper () void FString::ToUpper ()
{ {
LockBuffer();
size_t max = Len(); size_t max = Len();
for (size_t i = 0; i < max; ++i) for (size_t i = 0; i < max; ++i)
{ {
Chars[i] = toupper(Chars[i]); Chars[i] = toupper(Chars[i]);
} }
UnlockBuffer();
} }
void FString::ToLower () void FString::ToLower ()
{ {
LockBuffer();
size_t max = Len(); size_t max = Len();
for (size_t i = 0; i < max; ++i) for (size_t i = 0; i < max; ++i)
{ {
Chars[i] = tolower(Chars[i]); Chars[i] = tolower(Chars[i]);
} }
UnlockBuffer();
} }
void FString::SwapCase () void FString::SwapCase ()
{ {
LockBuffer();
size_t max = Len(); size_t max = Len();
for (size_t i = 0; i < max; ++i) for (size_t i = 0; i < max; ++i)
{ {
@ -432,6 +487,7 @@ void FString::SwapCase ()
Chars[i] = toupper(Chars[i]); Chars[i] = toupper(Chars[i]);
} }
} }
UnlockBuffer();
} }
void FString::StripLeft () void FString::StripLeft ()
@ -442,11 +498,21 @@ void FString::StripLeft ()
if (!isspace(Chars[i])) if (!isspace(Chars[i]))
break; 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) void FString::StripLeft (const FString &charset)
@ -462,11 +528,21 @@ void FString::StripLeft (const char *charset)
if (!strchr (charset, Chars[i])) if (!strchr (charset, Chars[i]))
break; 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 () void FString::StripRight ()
@ -477,8 +553,18 @@ void FString::StripRight ()
if (!isspace(Chars[i])) if (!isspace(Chars[i]))
break; break;
} }
Chars[i+1] = '\0'; if (Data()->RefCount <= 1)
Pond.Realloc (this, Chars, i+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) void FString::StripRight (const FString &charset)
@ -494,8 +580,18 @@ void FString::StripRight (const char *charset)
if (!strchr (charset, Chars[i])) if (!strchr (charset, Chars[i]))
break; break;
} }
Chars[i+1] = '\0'; if (Data()->RefCount <= 1)
Pond.Realloc (this, Chars, i+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 () void FString::StripLeftRight ()
@ -511,17 +607,27 @@ void FString::StripLeftRight ()
if (!isspace(Chars[j])) if (!isspace(Chars[j]))
break; 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) void FString::StripLeftRight (const FString &charset)
{ {
return StripLeft (charset.Chars); return StripLeftRight (charset.Chars);
} }
void FString::StripLeftRight (const char *charset) void FString::StripLeftRight (const char *charset)
@ -537,12 +643,22 @@ void FString::StripLeftRight (const char *charset)
if (!strchr (charset, Chars[j])) if (!strchr (charset, Chars[j]))
break; 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) 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; index = mylen;
} }
Pond.Realloc (this, Chars, mylen + instrlen); if (Data()->RefCount <= 1)
if (index < mylen)
{ {
ReallocBuffer (mylen + instrlen);
memmove (Chars + index + instrlen, Chars + index, (mylen - index + 1)*sizeof(char)); 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)); else
if (index == mylen)
{ {
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) void FString::ReplaceChars (char oldchar, char newchar)
{ {
size_t i, j; size_t i, j;
LockBuffer();
for (i = 0, j = Len(); i < j; ++i) for (i = 0, j = Len(); i < j; ++i)
{ {
if (Chars[i] == oldchar) if (Chars[i] == oldchar)
@ -584,11 +707,14 @@ void FString::ReplaceChars (char oldchar, char newchar)
Chars[i] = newchar; Chars[i] = newchar;
} }
} }
UnlockBuffer();
} }
void FString::ReplaceChars (const char *oldcharset, char newchar) void FString::ReplaceChars (const char *oldcharset, char newchar)
{ {
size_t i, j; size_t i, j;
LockBuffer();
for (i = 0, j = Len(); i < j; ++i) for (i = 0, j = Len(); i < j; ++i)
{ {
if (strchr (oldcharset, Chars[i]) != NULL) if (strchr (oldcharset, Chars[i]) != NULL)
@ -596,11 +722,14 @@ void FString::ReplaceChars (const char *oldcharset, char newchar)
Chars[i] = newchar; Chars[i] = newchar;
} }
} }
UnlockBuffer();
} }
void FString::StripChars (char killchar) void FString::StripChars (char killchar)
{ {
size_t read, write, mylen; size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; ++read) for (read = write = 0, mylen = Len(); read < mylen; ++read)
{ {
if (Chars[read] != killchar) if (Chars[read] != killchar)
@ -609,12 +738,15 @@ void FString::StripChars (char killchar)
} }
} }
Chars[write] = '\0'; Chars[write] = '\0';
Pond.Realloc (this, Chars, write); ReallocBuffer (write);
UnlockBuffer();
} }
void FString::StripChars (const char *killchars) void FString::StripChars (const char *killchars)
{ {
size_t read, write, mylen; size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; ++read) for (read = write = 0, mylen = Len(); read < mylen; ++read)
{ {
if (strchr (killchars, Chars[read]) == NULL) if (strchr (killchars, Chars[read]) == NULL)
@ -623,7 +755,8 @@ void FString::StripChars (const char *killchars)
} }
} }
Chars[write] = '\0'; Chars[write] = '\0';
Pond.Realloc (this, Chars, write); ReallocBuffer (write);
UnlockBuffer();
} }
void FString::MergeChars (char merger) void FString::MergeChars (char merger)
@ -634,6 +767,8 @@ void FString::MergeChars (char merger)
void FString::MergeChars (char merger, char newchar) void FString::MergeChars (char merger, char newchar)
{ {
size_t read, write, mylen; size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; ) for (read = write = 0, mylen = Len(); read < mylen; )
{ {
if (Chars[read] == merger) if (Chars[read] == merger)
@ -649,12 +784,15 @@ void FString::MergeChars (char merger, char newchar)
} }
} }
Chars[write] = '\0'; Chars[write] = '\0';
Pond.Realloc (this, Chars, write); ReallocBuffer (write);
UnlockBuffer();
} }
void FString::MergeChars (const char *charset, char newchar) void FString::MergeChars (const char *charset, char newchar)
{ {
size_t read, write, mylen; size_t read, write, mylen;
LockBuffer();
for (read = write = 0, mylen = Len(); read < mylen; ) for (read = write = 0, mylen = Len(); read < mylen; )
{ {
if (strchr (charset, Chars[read]) != NULL) if (strchr (charset, Chars[read]) != NULL)
@ -670,7 +808,8 @@ void FString::MergeChars (const char *charset, char newchar)
} }
} }
Chars[write] = '\0'; Chars[write] = '\0';
Pond.Realloc (this, Chars, write); ReallocBuffer (write);
UnlockBuffer();
} }
void FString::Substitute (const FString &oldstr, const FString &newstr) 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) void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrlen, size_t newstrlen)
{ {
LockBuffer();
for (size_t checkpt = 0; checkpt < Len(); ) for (size_t checkpt = 0; checkpt < Len(); )
{ {
char *match = strstr (Chars + checkpt, oldstr); 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; size_t matchpt = match - Chars;
if (oldstrlen != newstrlen) 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)); memmove (Chars + matchpt + newstrlen, Chars + matchpt + oldstrlen, (len + 1 - matchpt - oldstrlen)*sizeof(char));
} }
memcpy (Chars + matchpt, newstr, newstrlen); memcpy (Chars + matchpt, newstr, newstrlen);
@ -715,6 +855,7 @@ void FString::Substitute (const char *oldstr, const char *newstr, size_t oldstrl
break; break;
} }
} }
UnlockBuffer();
} }
bool FString::IsInt () const bool FString::IsInt () const
@ -842,12 +983,6 @@ double FString::ToDouble () const
return strtod (Chars, NULL); 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) void FString::StrCopy (char *to, const char *from, size_t len)
{ {
memcpy (to, from, len*sizeof(char)); memcpy (to, from, len*sizeof(char));
@ -858,3 +993,106 @@ void FString::StrCopy (char *to, const FString &from)
{ {
StrCopy (to, from.Chars, from.Len()); 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 <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <stddef.h>
#include "tarray.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 class FString
{ {
public: public:
FString () : Chars(NULL) {} FString () : Chars(&NullString.Nothing[0]) { NullString.RefCount++; }
// Copy constructors // Copy constructors
FString (const FString &other) { Chars = NULL; *this = other; } FString (const FString &other) { AttachToOther (other); }
FString (const char *copyStr); FString (const char *copyStr);
FString (const char *copyStr, size_t copyLen); FString (const char *copyStr, size_t copyLen);
FString (char oneChar); FString (char oneChar);
@ -28,12 +83,14 @@ public:
~FString (); ~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; } operator const char *() const { return Chars; }
char *GetChars() const { return Chars; } const char *GetChars() const { return Chars; }
char &operator[] (int index) { return Chars[index]; } const char &operator[] (int index) const { return Chars[index]; }
char &operator[] (size_t index) { return Chars[index]; } const char &operator[] (size_t index) const { return Chars[index]; }
FString &operator = (const FString &other); FString &operator = (const FString &other);
FString &operator = (const char *copyStr); FString &operator = (const char *copyStr);
@ -117,10 +174,10 @@ public:
unsigned long ToULong (int base=0) const; unsigned long ToULong (int base=0) const;
double ToDouble () 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; } 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 FString &other) const { return strcmp (Chars, other.Chars); }
int Compare (const char *other) const { return strcmp (Chars, other); } 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); } int CompareNoCase (const char *other) const { return stricmp (Chars, other); }
protected: protected:
struct StringHeader const FStringData *Data() const { return (FStringData *)Chars - 1; }
{ FStringData *Data() { return (FStringData *)Chars - 1; }
#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);
};
FString (size_t len); void AttachToOther (const FString &other);
StringHeader *GetHeader () const; void AllocBuffer (size_t len);
void ReallocBuffer (size_t newlen);
static int FormatHelper (void *data, const char *str, int len); 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 char *from, size_t len);
static void StrCopy (char *to, const FString &from); static void StrCopy (char *to, const FString &from);
static PoolGroup Pond;
char *Chars; char *Chars;
#ifndef __GNUC__ static FNullStringData NullString;
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
private: friend struct FStringData;
void *operator new (size_t size, FString *addr)
{
return addr;
}
void operator delete (void *, FString *)
{
}
}; };
#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 namespace StringFormat
{ {
enum 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 <File
RelativePath=".\src\zstring.cpp"> RelativePath=".\src\zstring.cpp">
</File> </File>
<File
RelativePath=".\src\zstringpool.cpp">
</File>
<Filter <Filter
Name="Decorate++" Name="Decorate++"
Filter=""> Filter="">