aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.hh5
-rw-r--r--src/strings.cc207
-rw-r--r--src/stubs.hh5
3 files changed, 212 insertions, 5 deletions
diff --git a/src/common.hh b/src/common.hh
index b981b2b..33fd694 100644
--- a/src/common.hh
+++ b/src/common.hh
@@ -117,6 +117,11 @@ void ExitSHMExtern(void);
// strings.cc
// =============================================================================
+const char *AddStaticString(const char *String);
+uint32 AddDynamicString(const char *String);
+const char *GetDynamicString(uint32 Number);
+void DeleteDynamicString(uint32 Number);
+void CleanupDynamicStrings(void);
void InitStrings(void);
void ExitStrings(void);
diff --git a/src/strings.cc b/src/strings.cc
index 478ad69..41fdc44 100644
--- a/src/strings.cc
+++ b/src/strings.cc
@@ -1,4 +1,211 @@
#include "common.hh"
+#include "containers.hh"
+
+struct TStaticStringTableBlock {
+ int TotalTextLength;
+ char Text[65536];
+};
+
+struct TDynamicStringTableBlock {
+ int FreeEntries;
+ int TotalTextLength;
+ bool Dirty;
+ uint8 EntryType[256];
+ uint16 StringOffset[256];
+ char Text[32768];
+};
+
+constexpr int StaticBlockSize = NARRAY(TStaticStringTableBlock::Text);
+constexpr int DynamicBlockSize = NARRAY(TDynamicStringTableBlock::Text);
+constexpr int DynamicBlockEntries = NARRAY(TDynamicStringTableBlock::EntryType);
+
+enum : uint8 {
+ DYNAMIC_STRING_FREE = 0,
+ DYNAMIC_STRING_ALLOCATED = 1,
+ DYNAMIC_STRING_DELETED = 2,
+};
+
+static list<TStaticStringTableBlock> StaticStringTable;
+static list<TDynamicStringTableBlock> DynamicStringTable;
+
+const char *AddStaticString(const char *String){
+ int StringLen = (int)strlen(String);
+ if((StringLen + 1) > StaticBlockSize){
+ error("AddStaticString: String zu lang (%d).\n", StringLen);
+ return NULL;
+ }
+
+ if(StringLen == 0){
+ return "";
+ }
+
+ listnode<TStaticStringTableBlock> *Node = StaticStringTable.firstNode;
+ while(Node != NULL){
+ if((Node->data.TotalTextLength + StringLen + 1) <= StaticBlockSize){
+ break;
+ }
+ Node = Node->next;
+ }
+
+ if(Node == NULL){
+ Node = StaticStringTable.append();
+ memset(&Node->data, 0, sizeof(TStaticStringTableBlock));
+ }
+
+ TStaticStringTableBlock *Block = &Node->data;
+ char *Result = &Block->Text[Block->TotalTextLength];
+ memcpy(Result, String, StringLen + 1);
+ Block->TotalTextLength += StringLen + 1;
+ return Result;
+}
+
+uint32 AddDynamicString(const char *String){
+ int StringLen = (int)strlen(String);
+ if((StringLen + 1) > DynamicBlockSize){
+ error("AddDynamicString: String zu lang (%d)\n", StringLen);
+ return 0;
+ }
+
+ if(StringLen == 0){
+ return 0;
+ }
+
+ int BlockIndex = 0;
+ listnode<TDynamicStringTableBlock> *Node = DynamicStringTable.firstNode;
+ while(Node != NULL){
+ if(Node->data.FreeEntries > 0){
+ if((Node->data.TotalTextLength + StringLen + 1) <= DynamicBlockSize){
+ break;
+ }
+ }
+ Node = Node->next;
+ BlockIndex += 1;
+ }
+
+ if(Node == NULL){
+ Node = DynamicStringTable.append();
+ memset(&Node->data, 0, sizeof(TDynamicStringTableBlock));
+ Node->data.FreeEntries = DynamicBlockEntries;
+ }
+
+ TDynamicStringTableBlock *Block = &Node->data;
+ int EntryIndex = -1;
+ for(int i = 0; i < DynamicBlockEntries; i += 1){
+ if(Block->EntryType[i] == DYNAMIC_STRING_FREE){
+ EntryIndex = i;
+ break;
+ }
+ }
+
+ if(EntryIndex == -1){
+ error("AddDynamicString: Keinen freien Platz gefunden.\n");
+ return 0;
+ }
+
+ int StringOffset = Block->TotalTextLength;
+ Block->FreeEntries -= 1;
+ Block->TotalTextLength += StringLen + 1;
+ Block->EntryType[EntryIndex] = DYNAMIC_STRING_ALLOCATED;
+ Block->StringOffset[EntryIndex] = (uint16)StringOffset;
+ memcpy(&Block->Text[StringOffset], String, StringLen + 1);
+
+ return (uint32)(1 + BlockIndex * DynamicBlockEntries + EntryIndex);
+}
+
+const char *GetDynamicString(uint32 Number){
+ if(Number == 0){
+ return NULL;
+ }
+
+ int BlockIndex = (int)(Number - 1) / DynamicBlockEntries;
+ int EntryIndex = (int)(Number - 1) % DynamicBlockEntries;
+ listnode<TDynamicStringTableBlock> *Node = DynamicStringTable.firstNode;
+ while(BlockIndex > 0 && Node != NULL){
+ Node = Node->next;
+ BlockIndex -= 1;
+ }
+
+ if(Node == NULL){
+ error("GetDynamicString: Block für String %u existiert nicht\n", Number);
+ return NULL;
+ }
+
+ TDynamicStringTableBlock *Block = &Node->data;
+ if(Block->EntryType[EntryIndex] != DYNAMIC_STRING_ALLOCATED){
+ error("GetDynamicString: Eintrag für String %u existiert nicht\n", Number);
+ return NULL;
+ }
+
+ int StringOffset = (int)Block->StringOffset[EntryIndex];
+ return &Block->Text[StringOffset];
+}
+
+void DeleteDynamicString(uint32 Number){
+ if(Number == 0){
+ return;
+ }
+
+ int BlockIndex = (int)(Number - 1) / DynamicBlockEntries;
+ int EntryIndex = (int)(Number - 1) % DynamicBlockEntries;
+ listnode<TDynamicStringTableBlock> *Node = DynamicStringTable.firstNode;
+ while(BlockIndex > 0 && Node != NULL){
+ Node = Node->next;
+ BlockIndex -= 1;
+ }
+
+ if(Node == NULL){
+ error("DeleteDynamicString: Block für String %u existiert nicht\n", Number);
+ return;
+ }
+
+ TDynamicStringTableBlock *Block = &Node->data;
+ if(Block->EntryType[EntryIndex] != DYNAMIC_STRING_ALLOCATED){
+ error("DeleteDynamicString: Eintrag für String %u existiert nicht\n", Number);
+ return;
+ }
+
+ Block->EntryType[EntryIndex] = DYNAMIC_STRING_DELETED;
+ Block->Dirty = true;
+}
+
+void CleanupDynamicStrings(void){
+ // IMPORTANT(fusion): The way we manage dynamic strings also mean pointers
+ // returned from `GetDynamicString` aren't stable.
+ for(listnode<TDynamicStringTableBlock> *Node = DynamicStringTable.firstNode;
+ Node != NULL;
+ Node = Node->next){
+ TDynamicStringTableBlock *Block = &Node->data;
+ if(!Block->Dirty){
+ continue;
+ }
+
+ for(int i = 0; i < DynamicBlockEntries; i += 1){
+ if(Block->EntryType[i] == DYNAMIC_STRING_DELETED){
+ int StringOffset = Block->StringOffset[i];
+ char *String = &Block->Text[StringOffset];
+ int StringLen = (int)strlen(String);
+ int StringEnd = StringOffset + StringLen + 1;
+ Block->EntryType[i] = DYNAMIC_STRING_FREE;
+
+ if(StringEnd > DynamicBlockSize){
+ error("CleanupDynamicStrings: Stringende fehlt\n");
+ continue;
+ }
+
+ if(StringEnd < DynamicBlockSize){
+ memmove(String, String + StringEnd, DynamicBlockSize - StringEnd);
+ }
+
+ for(int j = 0; j < DynamicBlockEntries; j += 1){
+ if(Block->EntryType[j] != DYNAMIC_STRING_FREE
+ && Block->StringOffset[j] > StringOffset){
+ Block->StringOffset[j] -= StringLen + 1;
+ }
+ }
+ }
+ }
+ }
+}
void InitStrings(void){
// no-op
diff --git a/src/stubs.hh b/src/stubs.hh
index 316a2fe..372ca92 100644
--- a/src/stubs.hh
+++ b/src/stubs.hh
@@ -15,18 +15,13 @@ typedef void TRefreshSectorFunction(int SectorX, int SectorY, int SectorZ, const
typedef void TSendMailsFunction(TPlayerData *PlayerData);
extern void AbortWriter(void);
-extern uint32 AddDynamicString(const char *String);
-extern const char *AddStaticString(const char *String);
extern void AnnounceChangedCreature(uint32 CreatureID, int Type);
extern void BroadcastMessage(int Mode, const char *Text, ...) ATTR_PRINTF(2, 3);
extern void Change(Object Obj, ObjectType NewType, uint32 Value);
extern bool CheckRight(uint32 CreatureID, RIGHT Right);
extern void CleanHouseField(int x, int y, int z);
-extern void CleanupDynamicStrings(void);
extern void CreatePlayerList(bool Online);
-extern void DeleteDynamicString(uint32 Number);
extern TCreature *GetCreature(uint32 CreatureID);
-extern const char *GetDynamicString(uint32 Number);
extern TConnection *GetFirstConnection(void);
extern TConnection *GetNextConnection(void);
extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3);