forked from Mirrors/openclonk
Hold C4Strings in a hashtable instead of a linked list
I didn't measure wether this helps performance, but I plan to use the generic hashtable for function lists and object fields, too.stable-5.2
parent
1fe837cc34
commit
6274cf7a5e
|
@ -24,11 +24,15 @@ class C4StringTable;
|
|||
|
||||
class C4String
|
||||
{
|
||||
C4String(C4StringTable *pTable);
|
||||
C4String(StdStrBuf strString, C4StringTable *pTable);
|
||||
explicit C4String(StdStrBuf strString);
|
||||
explicit C4String(const char *strString);
|
||||
|
||||
StdCopyStrBuf Data; // string data
|
||||
int iRefCnt; // reference count on string (by C4Value)
|
||||
|
||||
friend class C4StringTable;
|
||||
public:
|
||||
virtual ~C4String();
|
||||
~C4String();
|
||||
|
||||
// increment/decrement reference count on this string
|
||||
void IncRef();
|
||||
|
@ -36,38 +40,139 @@ public:
|
|||
|
||||
const char * GetCStr() const { return Data.getData(); }
|
||||
StdStrBuf GetData() const { return Data.getRef(); }
|
||||
StdCopyStrBuf Data; // string data
|
||||
int iRefCnt; // reference count on string (by C4Value)
|
||||
bool Hold; // string stays hold when RefCnt reaches 0 (for in-script strings)
|
||||
|
||||
int iEnumID;
|
||||
|
||||
C4String *Next, *Prev; // double-linked list
|
||||
|
||||
C4StringTable *pTable; // owning table
|
||||
|
||||
void Reg(C4StringTable *pTable);
|
||||
void UnReg();
|
||||
unsigned int Hash;
|
||||
};
|
||||
|
||||
template<typename T> class C4Set
|
||||
{
|
||||
unsigned int Capacity;
|
||||
unsigned int Size;
|
||||
T * Table;
|
||||
void AddInternal(T e)
|
||||
{
|
||||
unsigned int h = Hash(e);
|
||||
T * p = &Table[h % Capacity];
|
||||
while (*p && *p != e)
|
||||
{
|
||||
p = &Table[++h % Capacity];
|
||||
}
|
||||
*p = e;
|
||||
}
|
||||
public:
|
||||
template<typename H> static unsigned int Hash(H);
|
||||
template<typename H> static bool Equals(T, H);
|
||||
static bool Equals(T a, T b) { return a == b; }
|
||||
// FIXME: Profile for initial size
|
||||
C4Set(): Capacity(16), Size(0), Table(new T[Capacity])
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
~C4Set()
|
||||
{
|
||||
delete[] Table;
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
for (unsigned int i = 0; i < Capacity; ++i)
|
||||
Table[i] = 0;
|
||||
}
|
||||
template<typename H> T Get(H e)
|
||||
{
|
||||
unsigned int h = Hash(e);
|
||||
T r = Table[h % Capacity];
|
||||
while (r && !Equals(r, e))
|
||||
{
|
||||
r = Table[++h % Capacity];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
unsigned int GetSize() { return Size; }
|
||||
void Add(T e)
|
||||
{
|
||||
// FIXME: Profile for load factor
|
||||
if (Size > Capacity / 2)
|
||||
{
|
||||
unsigned int OCapacity = Capacity;
|
||||
Capacity *= 2;
|
||||
T * OTable = Table;
|
||||
Table = new T[Capacity];
|
||||
Clear();
|
||||
//memset(Table, 0, sizeof (T *) * NCapacity);
|
||||
for (unsigned int i = 0; i < OCapacity; ++i)
|
||||
{
|
||||
if (OTable[i])
|
||||
AddInternal(OTable[i]);
|
||||
}
|
||||
delete [] OTable;
|
||||
}
|
||||
AddInternal(e);
|
||||
++Size;
|
||||
}
|
||||
template<typename H> void Remove(H e)
|
||||
{
|
||||
unsigned int h = Hash(e);
|
||||
T * r = &Table[h % Capacity];
|
||||
while (*r && !Equals(*r, e))
|
||||
{
|
||||
r = &Table[++h % Capacity];
|
||||
}
|
||||
assert(*r);
|
||||
*r = 0;
|
||||
--Size;
|
||||
// Move entries which might have collided with e
|
||||
while (*(r = &Table[++h % Capacity]))
|
||||
{
|
||||
T m = *r;
|
||||
*r = 0;
|
||||
AddInternal(m);
|
||||
}
|
||||
}
|
||||
T const * First() const { return Next(Table - 1); }
|
||||
T const * Next(T const * p) const
|
||||
{
|
||||
while (++p != &Table[Capacity])
|
||||
{
|
||||
if (*p) return p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<> template<>
|
||||
inline unsigned int C4Set<C4String *>::Hash<const C4String *>(const C4String * e)
|
||||
{
|
||||
return e->Hash;
|
||||
}
|
||||
template<> template<>
|
||||
inline unsigned int C4Set<C4String *>::Hash<C4String *>(C4String * e)
|
||||
{
|
||||
return e->Hash;
|
||||
}
|
||||
|
||||
// There is only one Stringtable in Game.ScriptEngine
|
||||
class C4StringTable
|
||||
{
|
||||
public:
|
||||
C4StringTable();
|
||||
friend class C4AulScriptEngine;
|
||||
public:
|
||||
virtual ~C4StringTable();
|
||||
|
||||
void Clear();
|
||||
|
||||
C4String *RegString(StdStrBuf String);
|
||||
C4String *RegString(const char * s) { return RegString(StdStrBuf(s)); }
|
||||
// Find existing C4String
|
||||
C4String *FindString(const char *strString);
|
||||
// Check wether the pointer is a C4String
|
||||
C4String *FindString(C4String *pString);
|
||||
// Get a string from the Strings.txt
|
||||
C4String *FindString(int iEnumID);
|
||||
|
||||
bool Load(C4Group& ParentGroup);
|
||||
|
||||
C4String *First, *Last; // string list
|
||||
C4Set<C4String *> Set;
|
||||
std::vector<C4String *> Stringstxt;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -795,7 +795,6 @@ C4AulTokenType C4AulParseState::GetNextToken(char *pToken, long int *pInt, HoldS
|
|||
// reg string (if not already done so)
|
||||
C4String *pString;
|
||||
pString = a->Engine->Strings.RegString(StdStrBuf(StrBuff,static_cast<long>(pStrPos - StrBuff)));
|
||||
if (HoldStrings == Hold) pString->Hold = 1;
|
||||
// return pointer on string object
|
||||
*pInt = (long) pString;
|
||||
return ATT_STRING;
|
||||
|
|
|
@ -2280,7 +2280,7 @@ void C4Command::CompileFunc(StdCompiler *pComp)
|
|||
if(pComp->isDecompiler())
|
||||
{
|
||||
if(Text)
|
||||
TextBuf.Ref(Text->Data);
|
||||
TextBuf.Ref(Text->GetData());
|
||||
else
|
||||
TextBuf.Ref("0");
|
||||
}
|
||||
|
|
|
@ -2961,7 +2961,7 @@ static const int32_t CSPF_FixedAttributes = 1<<0,
|
|||
static bool FnCreateScriptPlayer(C4AulContext *cthr, C4String *szName, long dwColor, long idTeam, long dwFlags, C4ID idExtra)
|
||||
{
|
||||
// safety
|
||||
if (!szName || !szName->Data.getLength()) return false;
|
||||
if (!szName || !szName->GetData().getLength()) return false;
|
||||
// this script command puts a new script player info into the list
|
||||
// the actual join will be delayed and synchronized via queue
|
||||
// processed by control host only - clients/replay/etc. will perform the join via queue
|
||||
|
@ -3910,7 +3910,7 @@ static C4Value FnGetLength(C4AulContext *cthr, C4Value *pPars)
|
|||
return C4VInt(pArray->GetSize());
|
||||
C4String * pStr = pPars->getStr();
|
||||
if (pStr)
|
||||
return C4VInt(pStr->Data.getLength());
|
||||
return C4VInt(pStr->GetData().getLength());
|
||||
throw new C4AulExecError(cthr->Obj, "func \"GetLength\" par 0 cannot be converted to string or array");
|
||||
}
|
||||
|
||||
|
@ -6295,7 +6295,7 @@ static bool FnPauseGame(C4AulContext *ctx, bool fToggle)
|
|||
|
||||
static bool FnSetNextMission(C4AulContext *ctx, C4String *szNextMission, C4String *szNextMissionText, C4String *szNextMissionDesc)
|
||||
{
|
||||
if (!szNextMission || !szNextMission->Data.getLength())
|
||||
if (!szNextMission || !szNextMission->GetData().getLength())
|
||||
{
|
||||
// param empty: clear next mission
|
||||
Game.NextMission.Clear();
|
||||
|
@ -6304,10 +6304,10 @@ static bool FnSetNextMission(C4AulContext *ctx, C4String *szNextMission, C4Strin
|
|||
else
|
||||
{
|
||||
// set next mission, button and button desc if given
|
||||
Game.NextMission.Copy(szNextMission->Data);
|
||||
Game.NextMission.Copy(szNextMission->GetData());
|
||||
if (szNextMissionText && szNextMissionText->GetCStr())
|
||||
{
|
||||
Game.NextMissionText.Copy(szNextMissionText->Data);
|
||||
Game.NextMissionText.Copy(szNextMissionText->GetData());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -6315,7 +6315,7 @@ static bool FnSetNextMission(C4AulContext *ctx, C4String *szNextMission, C4Strin
|
|||
}
|
||||
if (szNextMissionDesc && szNextMissionDesc->GetCStr())
|
||||
{
|
||||
Game.NextMissionDesc.Copy(szNextMissionDesc->Data);
|
||||
Game.NextMissionDesc.Copy(szNextMissionDesc->GetData());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -23,31 +23,43 @@
|
|||
#include <C4Group.h>
|
||||
#include <C4Components.h>
|
||||
#include <C4Aul.h>
|
||||
#include <C4Game.h>
|
||||
#endif
|
||||
|
||||
// *** C4Set
|
||||
template<> template<>
|
||||
unsigned int C4Set<C4String *>::Hash<const char *>(const char * s)
|
||||
{
|
||||
// Fowler/Noll/Vo hash
|
||||
unsigned int h = 2166136261u;
|
||||
while (*s)
|
||||
h = (h ^ *(s++)) * 16777619;
|
||||
return h;
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
bool C4Set<C4String *>::Equals<const char *>(C4String * a, const char * b)
|
||||
{
|
||||
return a->GetData() == b;
|
||||
}
|
||||
|
||||
// *** C4String
|
||||
|
||||
C4String::C4String(C4StringTable *pnTable)
|
||||
: Data(NULL), iRefCnt(0), Hold(false), iEnumID(-1), pTable(NULL)
|
||||
{
|
||||
// reg
|
||||
Reg(pnTable);
|
||||
}
|
||||
|
||||
C4String::C4String(StdStrBuf strString, C4StringTable *pnTable)
|
||||
: iRefCnt(0), Hold(false), iEnumID(-1), pTable(NULL)
|
||||
C4String::C4String(StdStrBuf strString)
|
||||
: iRefCnt(0)
|
||||
{
|
||||
// take string
|
||||
Data.Take(strString);
|
||||
Hash = C4Set<C4String*>::Hash(Data.getData());
|
||||
// reg
|
||||
Reg(pnTable);
|
||||
Game.ScriptEngine.Strings.Set.Add(this);
|
||||
}
|
||||
|
||||
C4String::~C4String()
|
||||
{
|
||||
// unreg
|
||||
iRefCnt = 1;
|
||||
if(pTable) UnReg();
|
||||
Game.ScriptEngine.Strings.Set.Remove(this);
|
||||
}
|
||||
|
||||
void C4String::IncRef()
|
||||
|
@ -58,46 +70,6 @@ void C4String::IncRef()
|
|||
void C4String::DecRef()
|
||||
{
|
||||
--iRefCnt;
|
||||
|
||||
// delete if ref cnt is 0 and the Hold-Flag isn't set
|
||||
if(iRefCnt <= 0 && !Hold)
|
||||
delete this;
|
||||
}
|
||||
|
||||
void C4String::Reg(C4StringTable *pnTable)
|
||||
{
|
||||
if(pTable) UnReg();
|
||||
|
||||
// add string to tail of table
|
||||
Prev = pnTable->Last;
|
||||
Next = NULL;
|
||||
|
||||
if(Prev)
|
||||
Prev->Next = this;
|
||||
else
|
||||
pnTable->First = this;
|
||||
pnTable->Last = this;
|
||||
|
||||
pTable = pnTable;
|
||||
}
|
||||
|
||||
void C4String::UnReg()
|
||||
{
|
||||
if(!pTable) return;
|
||||
|
||||
if(Next)
|
||||
Next->Prev = Prev;
|
||||
else
|
||||
pTable->Last = Prev;
|
||||
if(Prev)
|
||||
Prev->Next = Next;
|
||||
else
|
||||
pTable->First = Next;
|
||||
|
||||
pTable = NULL;
|
||||
|
||||
// delete hold flag if table is lost and check for delete
|
||||
Hold = false;
|
||||
if(iRefCnt <= 0)
|
||||
delete this;
|
||||
}
|
||||
|
@ -105,33 +77,20 @@ void C4String::UnReg()
|
|||
// *** C4StringTable
|
||||
|
||||
C4StringTable::C4StringTable()
|
||||
: First(NULL), Last(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
C4StringTable::~C4StringTable()
|
||||
{
|
||||
// unreg all remaining strings
|
||||
// (hold strings will delete themselves)
|
||||
while(First) First->UnReg();
|
||||
Clear();
|
||||
assert(!Set.GetSize());
|
||||
}
|
||||
|
||||
void C4StringTable::Clear()
|
||||
{
|
||||
bool bContinue;
|
||||
do
|
||||
{
|
||||
bContinue = false;
|
||||
// find string to delete / unreg
|
||||
for(C4String *pAct = First; pAct; pAct = pAct->Next)
|
||||
if(pAct->Hold)
|
||||
{
|
||||
pAct->UnReg();
|
||||
bContinue = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while(bContinue);
|
||||
for (unsigned int i = 0; i < Stringstxt.size(); ++i)
|
||||
Stringstxt[i]->DecRef();
|
||||
Stringstxt.clear();
|
||||
}
|
||||
|
||||
C4String *C4StringTable::RegString(StdStrBuf String)
|
||||
|
@ -140,35 +99,31 @@ C4String *C4StringTable::RegString(StdStrBuf String)
|
|||
if (s)
|
||||
return s;
|
||||
else
|
||||
return new C4String(String, this);
|
||||
return new C4String(String);
|
||||
}
|
||||
|
||||
C4String *C4StringTable::FindString(const char *strString)
|
||||
{
|
||||
for(C4String *pAct = First; pAct; pAct = pAct->Next)
|
||||
if(SEqual(pAct->Data.getData(), strString))
|
||||
return pAct;
|
||||
return NULL;
|
||||
return Set.Get(strString);
|
||||
}
|
||||
|
||||
C4String *C4StringTable::FindString(C4String *pString)
|
||||
{
|
||||
for(C4String *pAct = First; pAct; pAct = pAct->Next)
|
||||
if(pAct == pString)
|
||||
return pAct;
|
||||
return NULL;
|
||||
for (C4String * const * i = Set.First(); i; i = Set.Next(i))
|
||||
if (*i == pString)
|
||||
return pString;
|
||||
}
|
||||
|
||||
C4String *C4StringTable::FindString(int iEnumID)
|
||||
{
|
||||
for(C4String *pAct = First; pAct; pAct = pAct->Next)
|
||||
if(pAct->iEnumID == iEnumID)
|
||||
return pAct;
|
||||
if (iEnumID >= 0 && iEnumID < int(Stringstxt.size()))
|
||||
return Stringstxt[iEnumID];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool C4StringTable::Load(C4Group& ParentGroup)
|
||||
{
|
||||
Clear();
|
||||
// read data
|
||||
char *pData;
|
||||
if(!ParentGroup.LoadEntry(C4CFN_Strings, &pData, NULL, 1))
|
||||
|
@ -180,9 +135,9 @@ bool C4StringTable::Load(C4Group& ParentGroup)
|
|||
SReplaceChar(strBuf, 0x0D, 0x00);
|
||||
// add string to list
|
||||
C4String *pnString;
|
||||
if(!(pnString = FindString(strBuf)))
|
||||
pnString = RegString(StdStrBuf(strBuf));
|
||||
pnString->iEnumID = i;
|
||||
pnString = RegString(StdStrBuf(strBuf));
|
||||
pnString->IncRef();
|
||||
Stringstxt.push_back(pnString);
|
||||
}
|
||||
// delete data
|
||||
delete[] pData;
|
||||
|
|
|
@ -261,6 +261,7 @@ C4V_Type C4Value::GuessType()
|
|||
// object?
|
||||
if (Game.Objects.ObjectNumber(Data.Obj))
|
||||
{
|
||||
assert(false);
|
||||
Type = C4V_C4Object;
|
||||
// With the type now known, the destructor will clean up the reference
|
||||
// which only works if the reference is added first
|
||||
|
@ -271,6 +272,7 @@ C4V_Type C4Value::GuessType()
|
|||
// string?
|
||||
if (Game.ScriptEngine.Strings.FindString(Data.Str))
|
||||
{
|
||||
assert(false);
|
||||
Type = C4V_String;
|
||||
// see above
|
||||
AddDataRef();
|
||||
|
@ -797,7 +799,7 @@ bool C4Value::operator == (const C4Value& Value2) const
|
|||
case C4V_C4Object:
|
||||
return Data == Value2.Data && Type == Value2.Type;
|
||||
case C4V_String:
|
||||
return Type == Value2.Type && Data.Str->Data == Value2.Data.Str->Data;
|
||||
return Type == Value2.Type && Data.Str == Value2.Data.Str;
|
||||
case C4V_Array:
|
||||
return Type == Value2.Type && *(Data.Array) == *(Value2.Data.Array);
|
||||
default:
|
||||
|
|
Loading…
Reference in New Issue