From f48ad6c4c3c6991461f603e8d2d1223ef3a2759d Mon Sep 17 00:00:00 2001 From: Randy Heit Date: Thu, 12 Apr 2007 23:00:52 +0000 Subject: [PATCH] - 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) --- docs/rh-log.txt | 13 ++ src/g_game.cpp | 139 ++++++------- src/g_level.cpp | 4 +- src/g_level.h | 15 +- src/p_acs.cpp | 82 +++----- src/tarray.h | 539 +++++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 663 insertions(+), 129 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 671ad9c4d8..69c3d47d3e 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -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. diff --git a/src/g_game.cpp b/src/g_game.cpp index e39003006f..5f75d8d42a 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -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 *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 *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 *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 *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) diff --git a/src/g_level.cpp b/src/g_level.cpp index 960313c83b..7aad65403f 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -113,11 +113,11 @@ int starttime; // ACS variables with world scope SDWORD ACS_WorldVars[NUM_WORLDVARS]; -TAutoGrowArray ACS_WorldArrays[NUM_WORLDVARS]; +FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; // ACS variables with global scope SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; -TAutoGrowArray ACS_GlobalArrays[NUM_GLOBALVARS]; +FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; extern bool netdemo; extern FString BackupSaveName; diff --git a/src/g_level.h b/src/g_level.h index ec5f73a7e0..9ca4bbfbac 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -286,8 +286,19 @@ extern TArray wadlevelinfos; extern SDWORD ACS_WorldVars[NUM_WORLDVARS]; extern SDWORD ACS_GlobalVars[NUM_GLOBALVARS]; -extern TAutoGrowArray ACS_WorldArrays[NUM_WORLDVARS]; -extern TAutoGrowArray ACS_GlobalArrays[NUM_GLOBALVARS]; + +struct InitIntToZero +{ + void Init(int &v) + { + v = 0; + } +}; +typedef TMap, InitIntToZero> FWorldGlobalArray; +typedef TMapIterator FWorldGlobalArrayIterator; + +extern FWorldGlobalArray ACS_WorldArrays[NUM_WORLDVARS]; +extern FWorldGlobalArray ACS_GlobalArrays[NUM_GLOBALVARS]; extern bool savegamerestore; diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 4a10d447d4..223898ac1e 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -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++; } diff --git a/src/tarray.h b/src/tarray.h index 21cb4d38b1..6dd97ee0e9 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -41,6 +41,8 @@ #include "m_alloc.h" +// TArray ------------------------------------------------------------------- + template 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 TAutoGrowArray : public TArray @@ -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 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 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 ValueTraits=TValueTraits > +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 friend class TMapIterator; + template 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 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 ⤅ + hash_t Position; +}; + +// TMapConstIterator -------------------------------------------------------- +// Exactly the same as TMapIterator, but it works with a const TMap. + +template > +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 ⤅ + hash_t Position; +}; + #endif //__TARRAY_H__