diff --git a/default.cbd b/default.cbd index c8b17296a1..d3822cb131 100644 --- a/default.cbd +++ b/default.cbd @@ -270,7 +270,6 @@ ${COMPILER} "autostart.cpp \ vectors.cpp \ name.cpp \ zstring.cpp \ - zstringpool.cpp \ zstrformat.cpp \ w_wad.cpp \ wi_stuff.cpp \ diff --git a/docs/rh-log.txt b/docs/rh-log.txt index b9e62e97be..219c58bdb3 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -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 diff --git a/src/b_bot.cpp b/src/b_bot.cpp index b2ac6f6356..545cdf27c7 100644 --- a/src/b_bot.cpp +++ b/src/b_bot.cpp @@ -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; } diff --git a/src/c_bind.cpp b/src/c_bind.cpp index 9bc882229c..8f4bb05430 100644 --- a/src/c_bind.cpp +++ b/src/c_bind.cpp @@ -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; } diff --git a/src/c_bind.h b/src/c_bind.h index 0bef476ce1..98df0ef74b 100644 --- a/src/c_bind.h +++ b/src/c_bind.h @@ -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__ diff --git a/src/cmdlib.cpp b/src/cmdlib.cpp index 9da4dd7403..3541c4cce5 100644 --- a/src/cmdlib.cpp +++ b/src/cmdlib.cpp @@ -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)) { diff --git a/src/d_main.cpp b/src/d_main.cpp index 8e0d598fb7..d6c19ebbfd 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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; diff --git a/src/d_main.h b/src/d_main.h index 4cb493ed15..544936149b 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -77,6 +77,8 @@ enum EIWADType struct WadStuff { + WadStuff() : Type(IWAD_Doom2TNT) {} + FString Path; EIWADType Type; }; diff --git a/src/d_net.cpp b/src/d_net.cpp index f4eb03c7da..326b32ed9e 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -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) { diff --git a/src/d_player.h b/src/d_player.h index d6482d81fb..7c4027c1dd 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -155,6 +155,8 @@ enum class player_s { public: + player_s(); + void Serialize (FArchive &arc); void FixPointers (const DObject *obj, DObject *replacement); diff --git a/src/dobject.h b/src/dobject.h index 00c458d6c9..b150924b60 100644 --- a/src/dobject.h +++ b/src/dobject.h @@ -37,6 +37,7 @@ #include #include "tarray.h" #include "doomtype.h" +#include "m_alloc.h" struct PClass; diff --git a/src/g_game.cpp b/src/g_game.cpp index 8a816d9c4b..cac5b768a4 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -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) { diff --git a/src/g_game.h b/src/g_game.h index 31eac02a97..60a87eb660 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -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); diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 9981010ff6..afbd81909a 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -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; diff --git a/src/p_user.cpp b/src/p_user.cpp index 76a4c690b6..e90e017656 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -67,6 +67,102 @@ static TArray 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 diff --git a/src/r_data.cpp b/src/r_data.cpp index 004c6c4a83..a287ac7cb7 100644 --- a/src/r_data.cpp +++ b/src/r_data.cpp @@ -93,7 +93,7 @@ byte** warpedflats; int* flatwarpedwhen; - +static TArray 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) diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index d7a9a64f4e..4ebed76ab0 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -81,13 +81,6 @@ struct FRandomSoundList WORD NumSounds; }; -template<> -void CopyForTArray (FRandomSoundList &dst, FRandomSoundList &src) -{ - dst = src; -} - - struct FPlayerClassLookup { char Name[MAX_SNDNAME+1]; diff --git a/src/sound/music_midi_timidity.cpp b/src/sound/music_midi_timidity.cpp index b6c274b7e3..96374b4773 100644 --- a/src/sound/music_midi_timidity.cpp +++ b/src/sound/music_midi_timidity.cpp @@ -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; diff --git a/src/tarray.h b/src/tarray.h index 1e69f39589..a4cf7375d4 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -38,37 +38,9 @@ #include #include #include - #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 -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 -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 -void ConstructEmptyInTArray (T *dst) -{ - ::new((void*)dst) T; -} - template 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) diff --git a/src/v_palette.cpp b/src/v_palette.cpp index 5da61aa6a7..a925ef9ea3 100644 --- a/src/v_palette.cpp +++ b/src/v_palette.cpp @@ -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; diff --git a/src/win32/i_system.cpp b/src/win32/i_system.cpp index 5dc54032b9..116fe11c8a 100644 --- a/src/win32/i_system.cpp +++ b/src/win32/i_system.cpp @@ -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 diff --git a/src/zstring.cpp b/src/zstring.cpp index 5137ab3441..18e7bd2a8a 100644 --- a/src/zstring.cpp +++ b/src/zstring.cpp @@ -1,39 +1,74 @@ #include #include #include +#include // 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(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 + +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; +} diff --git a/src/zstring.h b/src/zstring.h index 95273ff4e1..be08dfd210 100644 --- a/src/zstring.h +++ b/src/zstring.h @@ -4,16 +4,71 @@ #include #include #include +#include #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 &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 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 &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 diff --git a/src/zstringpool.cpp b/src/zstringpool.cpp deleted file mode 100644 index 6238b1cfd2..0000000000 --- a/src/zstringpool.cpp +++ /dev/null @@ -1,448 +0,0 @@ -#include -#include -#include - -#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 diff --git a/zdoom.vcproj b/zdoom.vcproj index f6a95439e6..d5484ad1ec 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1766,9 +1766,6 @@ - -