- Switched ACS world and global arrays to the TMap class instead of

TAutoGrowArray. A map can handle a wide range of key values better than
  an array.
- Added a templated associative array class, TMap, that uses Lua's "chained
  scatter table with Brent's variation" algorithm. I had considered using
  hash_map until I discovered that it's not standard STL, and there are two
  major but incompatible implementations of it. So I rolled my own, and Lua
  seemed like a good place to go to for an efficient algorithm, since it
  makes heavy use of tables.


SVN r513 (trunk)
This commit is contained in:
Randy Heit 2007-04-12 23:00:52 +00:00
parent a00bc159a1
commit f48ad6c4c3
6 changed files with 663 additions and 129 deletions

View file

@ -1,3 +1,16 @@
April 12, 2007
- Switched ACS world and global arrays to the TMap class instead of
TAutoGrowArray. A map can handle a wide range of key values better than
an array.
April 10, 2007
- Added a templated associative array class, TMap, that uses Lua's "chained
scatter table with Brent's variation" algorithm. I had considered using
hash_map until I discovered that it's not standard STL, and there are two
major but incompatible implementations of it. So I rolled my own, and Lua
seemed like a good place to go to for an efficient algorithm, since it
makes heavy use of tables.
March 28, 2007
- Fixed some const char * -> char * conversions flagged by GCC 4.2.0 in the
Win32-specific code.

View file

@ -1553,6 +1553,33 @@ bool G_CheckSaveGameWads (PNGHandle *png, bool printwarn)
return true;
}
static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id)
{
size_t i, j;
for (i = 0; i < count; ++i)
{
if (vars[i] != 0)
break;
}
if (i < count)
{
// Find last non-zero var. Anything beyond the last stored variable
// will be zeroed at load time.
for (j = count-1; j > i; --j)
{
if (vars[j] != 0)
break;
}
FPNGChunkArchive arc (file, id);
for (i = 0; i <= j; ++i)
{
DWORD var = vars[i];
arc << var;
}
}
}
static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id)
{
size_t len = M_FindPNGChunk (png, id);
@ -1578,7 +1605,45 @@ static void ReadVars (PNGHandle *png, SDWORD *vars, size_t count, DWORD id)
}
}
static void ReadArrayVars (PNGHandle *png, TArray<SDWORD> *vars, size_t count, DWORD id)
static void WriteArrayVars (FILE *file, FWorldGlobalArray *vars, unsigned int count, DWORD id)
{
unsigned int i, j;
// Find the first non-empty array.
for (i = 0; i < count; ++i)
{
if (vars[i].CountUsed() != 0)
break;
}
if (i < count)
{
// Find last non-empty array. Anything beyond the last stored array
// will be emptied at load time.
for (j = count-1; j > i; --j)
{
if (vars[j].CountUsed() != 0)
break;
}
FPNGChunkArchive arc (file, id);
arc.WriteCount (i);
arc.WriteCount (j);
for (; i <= j; ++i)
{
const SDWORD *key;
SDWORD *val;
arc.WriteCount (vars[i].CountUsed());
FWorldGlobalArrayIterator it(vars[i]);
while (it.NextPair (key, val))
{
arc.WriteCount (*key);
arc.WriteCount (*val);
}
}
}
}
static void ReadArrayVars (PNGHandle *png, FWorldGlobalArray *vars, size_t count, DWORD id)
{
size_t len = M_FindPNGChunk (png, id);
unsigned int i, k;
@ -1591,7 +1656,6 @@ static void ReadArrayVars (PNGHandle *png, TArray<SDWORD> *vars, size_t count, D
if (len != 0)
{
DWORD max, size;
DWORD var;
FPNGChunkArchive arc (png->File->GetFile(), id, len);
i = arc.ReadCount ();
@ -1600,14 +1664,12 @@ static void ReadArrayVars (PNGHandle *png, TArray<SDWORD> *vars, size_t count, D
for (; i <= max; ++i)
{
size = arc.ReadCount ();
if (size > 0)
{
vars[i].Resize (size);
}
for (k = 0; k < size; ++k)
{
arc << var;
vars[i][k] = var;
SDWORD key, val;
key = arc.ReadCount();
val = arc.ReadCount();
vars[i].Insert (key, val);
}
}
png->File->ResetFilePtr();
@ -1916,67 +1978,6 @@ static void PutSavePic (FILE *file, int width, int height)
}
}
static void WriteVars (FILE *file, SDWORD *vars, size_t count, DWORD id)
{
size_t i, j;
for (i = 0; i < count; ++i)
{
if (vars[i] != 0)
break;
}
if (i < count)
{
// Find last non-zero var. Anything beyond the last stored variable
// will be zeroed at load time.
for (j = count-1; j > i; --j)
{
if (vars[j] != 0)
break;
}
FPNGChunkArchive arc (file, id);
for (i = 0; i <= j; ++i)
{
DWORD var = vars[i];
arc << var;
}
}
}
static void WriteArrayVars (FILE *file, TArray<SDWORD> *vars, unsigned int count, DWORD id)
{
unsigned int i, j, k;
SDWORD val;
for (i = 0; i < count; ++i)
{
if (vars[i].Size() != 0)
break;
}
if (i < count)
{
// Find last non-empty array. Anything beyond the last stored array
// will be emptied at load time.
for (j = count-1; j > i; --j)
{
if (vars[j].Size() != 0)
break;
}
FPNGChunkArchive arc (file, id);
arc.WriteCount (i);
arc.WriteCount (j);
for (; i <= j; ++i)
{
arc.WriteCount (vars[i].Size());
for (k = 0; k < vars[i].Size(); ++k)
{
val = vars[i][k];
arc << val;
}
}
}
}
void G_DoSaveGame (bool okForQuicksave)
{
if (demoplayback)

View file

@ -113,11 +113,11 @@ int starttime;
// ACS variables with world scope
SDWORD ACS_WorldVars[NUM_WORLDVARS];
TAutoGrowArray<SDWORD> ACS_WorldArrays[NUM_WORLDVARS];
FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS];
// ACS variables with global scope
SDWORD ACS_GlobalVars[NUM_GLOBALVARS];
TAutoGrowArray<SDWORD> ACS_GlobalArrays[NUM_GLOBALVARS];
FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
extern bool netdemo;
extern FString BackupSaveName;

View file

@ -286,8 +286,19 @@ extern TArray<level_info_t> wadlevelinfos;
extern SDWORD ACS_WorldVars[NUM_WORLDVARS];
extern SDWORD ACS_GlobalVars[NUM_GLOBALVARS];
extern TAutoGrowArray<SDWORD> ACS_WorldArrays[NUM_WORLDVARS];
extern TAutoGrowArray<SDWORD> ACS_GlobalArrays[NUM_GLOBALVARS];
struct InitIntToZero
{
void Init(int &v)
{
v = 0;
}
};
typedef TMap<SDWORD, SDWORD, THashTraits<SDWORD>, InitIntToZero> FWorldGlobalArray;
typedef TMapIterator<SDWORD, SDWORD, FWorldGlobalArray> FWorldGlobalArrayIterator;
extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS];
extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS];
extern bool savegamerestore;

View file

@ -2709,12 +2709,12 @@ int DLevelScript::RunScript ()
break;
case PCD_ASSIGNWORLDARRAY:
ACS_WorldArrays[NEXTBYTE].SetVal (STACK(2), STACK(1));
ACS_WorldArrays[NEXTBYTE][STACK(2)] = STACK(1);
sp -= 2;
break;
case PCD_ASSIGNGLOBALARRAY:
ACS_GlobalArrays[NEXTBYTE].SetVal (STACK(2), STACK(1));
ACS_GlobalArrays[NEXTBYTE][STACK(2)] = STACK(1);
sp -= 2;
break;
@ -2739,11 +2739,11 @@ int DLevelScript::RunScript ()
break;
case PCD_PUSHWORLDARRAY:
STACK(1) = ACS_WorldArrays[NEXTBYTE].GetVal (STACK(1));
STACK(1) = ACS_WorldArrays[NEXTBYTE][STACK(1)];
break;
case PCD_PUSHGLOBALARRAY:
STACK(1) = ACS_GlobalArrays[NEXTBYTE].GetVal (STACK(1));
STACK(1) = ACS_GlobalArrays[NEXTBYTE][STACK(1)];
break;
case PCD_ADDSCRIPTVAR:
@ -2778,8 +2778,7 @@ int DLevelScript::RunScript ()
case PCD_ADDWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) + STACK(1));
ACS_WorldArrays[a][STACK(2)] += STACK(1);
sp -= 2;
}
break;
@ -2787,8 +2786,7 @@ int DLevelScript::RunScript ()
case PCD_ADDGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) + STACK(1));
ACS_GlobalArrays[a][STACK(2)] += STACK(1);
sp -= 2;
}
break;
@ -2825,8 +2823,7 @@ int DLevelScript::RunScript ()
case PCD_SUBWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) - STACK(1));
ACS_WorldArrays[a][STACK(2)] -= STACK(1);
sp -= 2;
}
break;
@ -2834,8 +2831,7 @@ int DLevelScript::RunScript ()
case PCD_SUBGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) - STACK(1));
ACS_GlobalArrays[a][STACK(2)] -= STACK(1);
sp -= 2;
}
break;
@ -2872,8 +2868,7 @@ int DLevelScript::RunScript ()
case PCD_MULWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) * STACK(1));
ACS_WorldArrays[a][STACK(2)] *= STACK(1);
sp -= 2;
}
break;
@ -2881,8 +2876,7 @@ int DLevelScript::RunScript ()
case PCD_MULGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) * STACK(1));
ACS_GlobalArrays[a][STACK(2)] *= STACK(1);
sp -= 2;
}
break;
@ -2957,8 +2951,7 @@ int DLevelScript::RunScript ()
else
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) / STACK(1));
ACS_WorldArrays[a][STACK(2)] /= STACK(1);
sp -= 2;
}
break;
@ -2971,8 +2964,7 @@ int DLevelScript::RunScript ()
else
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) / STACK(1));
ACS_GlobalArrays[a][STACK(2)] /= STACK(1);
sp -= 2;
}
break;
@ -3047,8 +3039,7 @@ int DLevelScript::RunScript ()
else
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) % STACK(1));
ACS_WorldArrays[a][STACK(2)] %= STACK(1);
sp -= 2;
}
break;
@ -3061,8 +3052,7 @@ int DLevelScript::RunScript ()
else
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) % STACK(1));
ACS_GlobalArrays[a][STACK(2)] %= STACK(1);
sp -= 2;
}
break;
@ -3100,8 +3090,7 @@ int DLevelScript::RunScript ()
case PCD_ANDWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) & STACK(1));
ACS_WorldArrays[a][STACK(2)] &= STACK(1);
sp -= 2;
}
break;
@ -3109,8 +3098,7 @@ int DLevelScript::RunScript ()
case PCD_ANDGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) & STACK(1));
ACS_GlobalArrays[a][STACK(2)] &= STACK(1);
sp -= 2;
}
break;
@ -3147,8 +3135,7 @@ int DLevelScript::RunScript ()
case PCD_EORWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) ^ STACK(1));
ACS_WorldArrays[a][STACK(2)] ^= STACK(1);
sp -= 2;
}
break;
@ -3156,8 +3143,7 @@ int DLevelScript::RunScript ()
case PCD_EORGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) ^ STACK(1));
ACS_GlobalArrays[a][STACK(2)] ^= STACK(1);
sp -= 2;
}
break;
@ -3194,8 +3180,7 @@ int DLevelScript::RunScript ()
case PCD_ORWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) | STACK(1));
ACS_WorldArrays[a][STACK(2)] |= STACK(1);
sp -= 2;
}
break;
@ -3204,7 +3189,7 @@ int DLevelScript::RunScript ()
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) | STACK(1));
ACS_GlobalArrays[a][STACK(2)] |= STACK(1);
sp -= 2;
}
break;
@ -3241,8 +3226,7 @@ int DLevelScript::RunScript ()
case PCD_LSWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) << STACK(1));
ACS_WorldArrays[a][STACK(2)] <<= STACK(1);
sp -= 2;
}
break;
@ -3250,8 +3234,7 @@ int DLevelScript::RunScript ()
case PCD_LSGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) << STACK(1));
ACS_GlobalArrays[a][STACK(2)] <<= STACK(1);
sp -= 2;
}
break;
@ -3288,8 +3271,7 @@ int DLevelScript::RunScript ()
case PCD_RSWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) >> STACK(1));
ACS_WorldArrays[a][STACK(2)] >>= STACK(1);
sp -= 2;
}
break;
@ -3297,8 +3279,7 @@ int DLevelScript::RunScript ()
case PCD_RSGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(2);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) >> STACK(1));
ACS_GlobalArrays[a][STACK(2)] >>= STACK(1);
sp -= 2;
}
break;
@ -3332,8 +3313,7 @@ int DLevelScript::RunScript ()
case PCD_INCWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(1);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) + 1);
ACS_WorldArrays[a][STACK(1)] += 1;
sp--;
}
break;
@ -3341,8 +3321,7 @@ int DLevelScript::RunScript ()
case PCD_INCGLOBALARRAY:
{
int a = NEXTBYTE;
int i = STACK(1);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) + 1);
ACS_GlobalArrays[a][STACK(1)] += 1;
sp--;
}
break;
@ -3375,8 +3354,7 @@ int DLevelScript::RunScript ()
case PCD_DECWORLDARRAY:
{
int a = NEXTBYTE;
int i = STACK(1);
ACS_WorldArrays[a].SetVal (i, ACS_WorldArrays[a].GetVal (i) - 1);
ACS_WorldArrays[a][STACK(1)] -= 1;
sp--;
}
break;
@ -3385,7 +3363,7 @@ int DLevelScript::RunScript ()
{
int a = NEXTBYTE;
int i = STACK(1);
ACS_GlobalArrays[a].SetVal (i, ACS_GlobalArrays[a].GetVal (i) - 1);
ACS_GlobalArrays[a][STACK(1)] -= 1;
sp--;
}
break;
@ -3732,7 +3710,7 @@ int DLevelScript::RunScript ()
int a = STACK(1);
int offset = STACK(2);
int c;
while((c = ACS_WorldArrays[a].GetVal (offset)) != '\0') {
while((c = ACS_WorldArrays[a][offset]) != '\0') {
work += (char)c;
offset++;
}
@ -3746,7 +3724,7 @@ int DLevelScript::RunScript ()
int a = STACK(1);
int offset = STACK(2);
int c;
while((c = ACS_GlobalArrays[a].GetVal (offset)) != '\0') {
while((c = ACS_GlobalArrays[a][offset]) != '\0') {
work += (char)c;
offset++;
}

View file

@ -41,6 +41,8 @@
#include "m_alloc.h"
// TArray -------------------------------------------------------------------
template <class T>
class TArray
{
@ -251,7 +253,7 @@ private:
Array = (T *)M_Malloc (sizeof(T)*Most);
for (unsigned int i = 0; i < Count; ++i)
{
Array[i] = other.Array[i];
::new(&Array[i]) T(other.Array[i]);
}
}
else
@ -276,9 +278,10 @@ private:
}
};
// An array with accessors that automatically grow the
// array as needed. But can still be used as a normal
// TArray if needed. Used by ACS world and global arrays.
// TAutoGrowArray -----------------------------------------------------------
// An array with accessors that automatically grow the array as needed.
// It can still be used as a normal TArray if needed. ACS uses this for
// world and global arrays.
template <class T>
class TAutoGrowArray : public TArray<T>
@ -304,4 +307,532 @@ public:
}
};
// TMap ---------------------------------------------------------------------
// An associative array, similar in concept to the STL extension
// class hash_map. It is implemented using Lua's table algorithm:
/*
** Hash uses a mix of chained scatter table with Brent's variation.
** A main invariant of these tables is that, if an element is not
** in its main position (i.e. the `original' position that its hash gives
** to it), then the colliding element is in its own main position.
** Hence even when the load factor reaches 100%, performance remains good.
*/
/******************************************************************************
* Copyright (C) 1994-2006 Lua.org, PUC-Rio. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
typedef unsigned int hash_t;
template<class KT> struct THashTraits
{
// Returns the hash value for a key.
hash_t Hash(const KT key) { return hash_t(key); }
// Compares two keys, returning zero if they are the same.
int Compare(const KT left, const KT right) { return left != right; }
};
template<class VT> struct TValueTraits
{
// Initializes a value for TMap. If a regular consructor isn't
// good enough, you can override it.
void Init(VT &value)
{
::new(&value) VT;
}
};
template<class KT, class VT, class HashTraits=THashTraits<KT>, class ValueTraits=TValueTraits<VT> >
class TMap
{
public:
TMap() { NumUsed = 0; SetNodeVector(1); }
TMap(hash_t size) { NumUsed = 0; SetNodeVector(size); }
~TMap() { ClearNodeVector(); }
TMap(const TMap &o)
{
NumUsed = 0;
SetNodeVector(o.CountUsed());
CopyNodes(o.Nodes, o.Size);
}
TMap &operator= (const TMap &o)
{
NumUsed = 0;
ClearNodeVector();
SetNodeVector(o.CountUsed());
CopyNodes(o.Nodes, o.Size);
return *this;
}
//=======================================================================
//
// Clear
//
// Empties out the table and resizes it with room for count entries.
//
//=======================================================================
void Clear(hash_t count=1)
{
ClearNodeVector();
SetNodeVector(count);
}
//=======================================================================
//
// CountUsed
//
// Returns the number of entries in use in the table.
//
//=======================================================================
hash_t CountUsed() const
{
#ifdef _DEBUG
hash_t used = 0;
hash_t ct = Size;
for (Node *n = Nodes; ct-- > 0; ++n)
{
if (!n->IsNil())
{
++used;
}
}
assert (used == NumUsed);
#endif
return NumUsed;
}
//=======================================================================
//
// operator[]
//
// Returns a reference to the value associated with a particular key,
// creating the pair if the key isn't already in the table.
//
//=======================================================================
VT &operator[] (const KT key)
{
return GetNode(key)->Value;
}
const VT &operator[] (const KT key) const
{
return GetNode(key)->Value;
}
//=======================================================================
//
// CheckKey
//
// Returns a pointer to the value associated with a particular key, or
// NULL if the key isn't in the table.
//
//=======================================================================
VT *CheckKey (const KT key)
{
Node *n = FindKey(key);
return n != NULL ? &n->Value : NULL;
}
const VT *CheckKey (const KT key) const
{
Node *n = FindKey(key);
return n != NULL ? &n->Value : NULL;
}
//=======================================================================
//
// Insert
//
// Adds a key/value pair to the table if key isn't in the table, or
// replaces the value for the existing pair if the key is in the table.
//
// This is functionally equivalent to (*this)[key] = value; but can be
// slightly faster if the pair needs to be created because it doesn't run
// the constructor on the value part twice.
//
//=======================================================================
VT &Insert(const KT key, const VT &value)
{
Node *n = FindKey(key);
if (n != NULL)
{
n->Value = value;
}
else
{
n = NewKey(key);
::new(&n->Value) VT(value);
}
return n->Value;
}
//=======================================================================
//
// Remove
//
// Removes the key/value pair for a particular key if it is in the table.
//
//=======================================================================
void Remove(const KT key)
{
DelKey(key);
}
protected:
template<class KTa, class VTa, class MTa> friend class TMapIterator;
template<class KTb, class VTb, class MTb> friend class TMapConstIterator;
struct Node
{
Node *Next;
KT Key;
VT Value;
void SetNil()
{
Next = (Node *)1;
}
bool IsNil() const
{
return Next == (Node *)1;
}
};
/* This is used instead of memcpy, because Node is likely to be small,
* such that the time spent calling a function would eclipse the time
* spent copying. */
struct NodeSizedStruct { unsigned char Pads[sizeof(Node)]; };
Node *Nodes;
Node *LastFree; /* any free position is before this position */
hash_t Size; /* must be a power of 2 */
hash_t NumUsed;
const Node *MainPosition(const KT k) const
{
HashTraits Traits;
return &Nodes[Traits.Hash(k) & (Size - 1)];
}
Node *MainPosition(const KT k)
{
HashTraits Traits;
return &Nodes[Traits.Hash(k) & (Size - 1)];
}
void SetNodeVector(hash_t size)
{
// Round size up to nearest power of 2
for (Size = 1; Size < size; Size <<= 1)
{ }
Nodes = (Node *)M_Malloc(Size * sizeof(Node));
LastFree = &Nodes[Size]; /* all positions are free */
for (hash_t i = 0; i < Size; ++i)
{
Nodes[i].SetNil();
}
}
void ClearNodeVector()
{
for (hash_t i = 0; i < Size; ++i)
{
if (!Nodes[i].IsNil())
{
Nodes[i].~Node();
}
}
free(Nodes);
Nodes = NULL;
Size = 0;
LastFree = NULL;
NumUsed = 0;
}
void Resize(hash_t nhsize)
{
hash_t i, oldhsize = Size;
Node *nold = Nodes;
/* create new hash part with appropriate size */
SetNodeVector(nhsize);
/* re-insert elements from hash part */
NumUsed = 0;
for (i = 0; i < oldhsize; ++i)
{
if (!nold[i].IsNil())
{
Node *n = NewKey(nold[i].Key);
::new(&n->Value) VT(nold[i].Value);
nold[i].~Node();
}
}
free(nold);
}
void Rehash()
{
Resize (Size << 1);
}
Node *GetFreePos()
{
while (LastFree-- > Nodes)
{
if (LastFree->IsNil())
{
return LastFree;
}
}
return NULL; /* could not find a free place */
}
/*
** Inserts a new key into a hash table; first, check whether key's main
** position is free. If not, check whether colliding node is in its main
** position or not: if it is not, move colliding node to an empty place and
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
**
** The Value field is left unconstructed.
*/
Node *NewKey(const KT key)
{
Node *mp = MainPosition(key);
if (!mp->IsNil())
{
Node *othern;
Node *n = GetFreePos(); /* get a free place */
if (n == NULL) /* cannot find a free place? */
{
Rehash(); /* grow table */
return NewKey(key); /* re-insert key into grown table */
}
othern = MainPosition(mp->Key);
if (othern != mp) /* is colliding node out of its main position? */
{ /* yes; move colliding node into free position */
while (othern->Next != mp) /* find previous */
{
othern = othern->Next;
}
othern->Next = n; /* redo the chain with 'n' in place of 'mp' */
CopyNode(n, mp); /* copy colliding node into free pos. (mp->Next also goes) */
mp->Next = NULL; /* now 'mp' is free */
}
else /* colliding node is in its own main position */
{ /* new node will go into free position */
n->Next = mp->Next; /* chain new position */
mp->Next = n;
mp = n;
}
}
else
{
mp->Next = NULL;
}
++NumUsed;
::new(&mp->Key) KT(key);
return mp;
}
void DelKey(const KT key)
{
Node *mp = MainPosition(key), **mpp;
HashTraits Traits;
if (!mp->IsNil() && !Traits.Compare(mp->Key, key)) /* the key is in its main position */
{
if (mp->Next != NULL) /* move next node to its main position */
{
Node *n = mp->Next;
mp->~Node(); /* deconstruct old node */
CopyNode(mp, n); /* copy next node */
n->SetNil(); /* next node is now nil */
}
else
{
mp->~Node();
mp->SetNil(); /* there is no chain, so main position is nil */
}
--NumUsed;
}
else /* the key is either not present or not in its main position */
{
for (mpp = &mp->Next, mp = *mpp; mp != NULL && Traits.Compare(mp->Key, key); mpp = &mp->Next, mp = *mpp)
{ } /* look for the key */
if (mp != NULL) /* found it */
{
*mpp = mp->Next; /* rechain so this node is skipped */
mp->~Node();
mp->SetNil(); /* because this node is now nil */
--NumUsed;
}
}
}
Node *FindKey(const KT key)
{
HashTraits Traits;
Node *n = MainPosition(key);
while (n != NULL && !n->IsNil() && Traits.Compare(n->Key, key))
{
n = n->Next;
}
return n == NULL || n->IsNil() ? NULL : n;
}
Node *GetNode(const KT key)
{
Node *n = FindKey(key);
if (n != NULL)
{
return n;
}
n = NewKey(key);
ValueTraits traits;
traits.Init(n->Value);
return n;
}
/* Perform a bit-wise copy of the node. Used when relocating a node in the table. */
void CopyNode(Node *dst, const Node *src)
{
*(NodeSizedStruct *)dst = *(const NodeSizedStruct *)src;
}
/* Copy all nodes in the node vector to this table. */
void CopyNodes(const Node *nodes, hash_t numnodes)
{
for (; numnodes-- > 0; ++nodes)
{
if (!nodes->IsNil())
{
Node *n = NewKey(nodes->Key);
::new(&n->Value) VT(nodes->Value);
}
}
}
};
// TMapIterator -------------------------------------------------------------
// A class to iterate over all the pairs in a TMap.
template<class KT, class VT, class MapType=TMap<KT,VT> >
class TMapIterator
{
public:
TMapIterator(MapType &map)
: Map(map), Position(0)
{
}
//=======================================================================
//
// NextPair
//
// Returns false if there are no more entries in the table. Otherwise, it
// returns true, and key and value are filled with pointers to the
// respective parts of the next pair in the table.
//
//=======================================================================
bool NextPair(const KT *&key, VT *&value)
{
if (Position >= Map.Size)
{
return false;
}
do
{
if (!Map.Nodes[Position].IsNil())
{
key = &Map.Nodes[Position].Key;
value = &Map.Nodes[Position].Value;
Position += 1;
return true;
}
} while (++Position < Map.Size);
return false;
}
//=======================================================================
//
// Reset
//
// Restarts the iteration so you can do it all over again.
//
//=======================================================================
void Reset()
{
Position = 0;
}
protected:
MapType &Map;
hash_t Position;
};
// TMapConstIterator --------------------------------------------------------
// Exactly the same as TMapIterator, but it works with a const TMap.
template<class KT, class VT, class MapType=TMap<KT,VT> >
class TMapConstIterator
{
public:
TMapConstIterator(const MapType &map)
: Map(map), Position(0)
{
}
bool NextPair(const KT *&key, const VT *&value)
{
if (Position >= Map.Size)
{
return false;
}
do
{
if (!Map.Nodes[Position].IsNil())
{
key = &Map.Nodes[Position].Key;
value = &Map.Nodes[Position].Value;
Position += 1;
return true;
}
} while (++Position < Map.Size);
return false;
}
protected:
const MapType &Map;
hash_t Position;
};
#endif //__TARRAY_H__