| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /* $Header: /Commando/Code/wwlib/hashlist.h 13 4/30/01 3:05p Joe_b $ */
- /***********************************************************************************************
- *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
- ***********************************************************************************************
- * *
- * Project Name : G *
- * *
- * $Archive:: /Commando/Code/wwlib/hashlist.h $*
- * *
- * Creator::Scott K. Bowen - 5/4/99 *
- * *
- * $Author:: Joe_b $*
- * *
- * $Modtime:: 4/30/01 2:41p $*
- * *
- * $Revision:: 13 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * HashListClass::Find -- Find record in hash table. *
- * HashListClass::Remove -- Remove record from hash table. *
- * HashListClass::Add -- Add record to hash list. *
- * HashListClass::Add -- Add record to list, list creates node for user. *
- * HashListClass::Move_To -- Move nodes from one list to another. *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #if defined(_MSC_VER)
- #pragma once
- #endif
- #ifndef HASHLIST_H
- #define HASHLIST_H
- #pragma warning (push)
- #pragma warning (disable: 4786)
-
- #include "listnode.h"
- #include <memory.h>
- #ifndef NULL
- #define NULL (0L)
- #endif
- #define A_LARGE_PRIME_NUMBER 257
- // HashListClass<> and HashNodeClass<>:
- // The purpose of these templates is to allow a user to insert records into
- // a hash table that also contains a double linked list that includes every
- // record in the hash table. The functionality of the hash list mirrors
- // very closely that of the DataNode<> and List<> templates. The differences
- // between them are:
- // -User calls Add(?) to add to list instead of Add_Head() or others.
- // -A key must be set to hash off of.
- //
- // Additional features of the HashList<> over List<> are:
- // -Quick searching as long as key's are well hashed..
- // -Node can inherit user class so additional data can be put in node.
- // For example a timestamp to be set by user. Default is
- // HashDefaluatUserClass that has no data.
- // -If class T is actually a class (not a pointer), user may get a ptr
- // to class with Get_Ptr() instead of Get() that returns an INSTANCE
- // of the class.
- // -Flags to tell user if record was just created or record was just put
- // into a list (user must clear flags if desired.)
- // -HashListClass<> can create and destroy nodes so that the nodes do not
- // need to be put as members in classes. This allows an object to be
- // put into an infinite (subject to memory constraints) lists.
- // -User may specify number of hash node slots (default is A_LARGE_PRIME_NUMBER).
- // You can typedef these templates with the the following:
- // typedef HashListClass<TestHashClass *> HashTableClass;
- // typedef HashNodeClass<TestHashClass *> HashTableNodeClass;
- // or if using a UserClass
- // typedef HashListClass<TestHashClass *, 4, UserClass> HashTableClass;
- // typedef HashNodeClass<TestHashClass *, UserClass> HashTableNodeClass;
- template<class T, class U> class HashNodeClass;
- template<class T, class U> class HashNodeFriendClass;
- template<class T, class U, int NumHashValues> class HashListClass;
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // This is a default class that is stored in HashNodeClass. It's purpose is
- // to allow the user to override it with his/her own class so that additional
- // data may be stored in the node about the object.
- // Since the object might be in various lists, this data is specific to
- // the list that it in.
- class HashDefaultUserClass {};
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
-
- template<class T, class U = class HashDefaultUserClass>
- class HashNodeClass : public DataNode<HashNodeClass<T,U> *>, public U
- {
- public:
- // Creation when key and record are known.
- HashNodeClass(T record, unsigned key) :
- Flags(0),
- Record(record),
- Key(key)
- {
- DataNode<HashNodeClass<T,U> *>::Set(this);
- Flag.NewRecord = true;
- Flag.KeySet = true;
- }
-
- // Creation when key and record are to be set later.
- // node may not be put into list until the key is set.
- HashNodeClass() :
- Flags(0),
- Key(-1)
- {
- DataNode<HashNodeClass<T,U> *>::Set(this);
- Flag.NewRecord = true;
- }
- virtual ~HashNodeClass() {
- // Assert is raised when user tries to delete HashNodeClass still in a list
- // NOTE: This might be raised because user expected node to clean
- // itself up when deleted. Currently, this feature is not available
- // because it would require an additional pointer to the HashListClass
- // that the node is a member of.
- assert(!Flag.InList);
- }
-
- // Get next and prev record given a node.
- // A node may be retrieved from: HashListClass::Find();
- HashNodeClass<T,U> *Next() {
- return((HashNodeClass<T,U> *) DataNode<HashNodeClass<T,U> *>::Next());
- }
- HashNodeClass<T,U> *Prev() {
- return((HashNodeClass<T,U> *) DataNode<HashNodeClass<T,U> *>::Prev());
- }
-
- // User may use these if he desires not to have to check for valid.
- HashNodeClass<T,U> *Next_Valid() {
- HashNodeClass<T,U> *next = Next();
- if (next && next->Is_Valid()) {
- return(next);
- }
- return(NULL);
- }
- HashNodeClass<T,U> *Prev_Valid() {
- HashNodeClass<T,U> *prev = Prev();
- if (prev && prev->Is_Valid()) {
- return(prev);
- }
- return(NULL);
- }
-
- // Get record that is in hash table.
- T Get_Record() {return(Record);}
- T Get_Record_Ptr() {return(&Record);}
- // Overload DataNode::Get() to get the record we are pointing to so
- // that it behaves how people expect List to operate.
- // To Get HashNodeClass - it is this.
- T Get() {return(Record);}
-
- // Get a pointer to Record to avoid a copy construction of Record if
- // it is an instance of a class that does not want to be copied all
- // over the place.
- T *Get_Ptr() {return(&Record);}
- // To mirror usage of DataNode().
- void Set(T record) {Record = record;}
-
- void Set_Key(unsigned key) {
- // Can't change key once in a list, we would never find it.
- assert(!Flag.InList);
- Flag.KeySet = true;
- Key = key;
- }
-
- // Get key of record for has table.
- unsigned Get_Key() {
- assert(Flag.KeySet);
- return (Key);
- }
-
- // Enable user to know if a record has just been added to the list.
- // It stays set until user clears it - not required.
- int Is_New_Record() {return(Flag.NewRecord);}
- void Clear_New_Record() {Flag.NewRecord = false;}
- // Each time object gets put into a list NewInList is set.
- // Stays set until user clears - not required.
- int Is_New_In_List() {return(Flag.NewInList);}
- void Clear_New_In_List() {Flag.NewInList = false;}
-
- bool Is_In_List() {return(Flag.InList);}
- bool Is_Key_Set() {return(Flag.KeySet);}
- protected:
- // Record that we are keeping in hash table.
- // This is commonly a pointer to the actual record.
- T Record;
-
- // Key that user gives to identify record.
- // WARNING: Duplicate keys are undefined behavior.
- unsigned Key;
-
- // Unioned flags so Flags can be initialized to 0.
- union {
- struct {
- // When record collides in hash list, these ?InTable
- // tell when to stop looking for object in hash entry.
- unsigned FirstInTable:1;
- unsigned LastInTable:1;
-
- // This is set when record is first created. It is up to user
- // to clear if he uses flag.
- unsigned NewRecord:1;
-
- // This is set when record is first put in list. It is up to user
- // to clear if he uses flag.
- unsigned NewInList:1;
-
- // Certain functions may not happen if node is InList (in hash table)
- // These include delete, Set_Key(), and possibly others.
- unsigned InList:1;
-
- // This is set so HashListClass knows if it should delete the
- // node when removed from it's list.
- unsigned ListCreated:1;
-
- // Make sure someone does not insert a node into a list
- // without setting it's key.
- unsigned KeySet:1;
- } Flag;
- unsigned Flags;
- };
- private:
- // Not something the casual user can call.
- void Unlink(void) {
- DataNode<HashNodeClass<T,U> *>::Unlink();
- }
-
- friend class HashNodeFriendClass<T,U>;
- };
-
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- // This class is created so that HashListClass can access HashNodeClass.
- // HashListClass cannot access directly because it is impossible
- // (I could not figure out how) to tell HashNodeClass to be a friend
- // to all creations of HashListClass<T,N> since HashNodeClass
- // does not need the NumHashValues parameter.
- #pragma warning(push)
- #pragma warning(disable : 4786) // identifier was truncated to 255 chars in debug information.
-
- template<class T, class U>
- class HashNodeFriendClass
- {
- protected:
- void Set_In_List(HashNodeClass<T,U> *ptr) {ptr->Flag.InList = true; ptr->Flag.NewInList = true;}
- void Clear_In_List(HashNodeClass<T,U> *ptr) {ptr->Flag.InList = false; ptr->Flag.NewInList = false;}
-
- bool Is_First(HashNodeClass<T,U> *ptr) {return(ptr->Flag.FirstInTable);}
- bool Is_Last(HashNodeClass<T,U> *ptr) {return(ptr->Flag.LastInTable); }
- void Set_First(HashNodeClass<T,U> *ptr) {ptr->Flag.FirstInTable = true; }
- void Set_Last(HashNodeClass<T,U> *ptr) {ptr->Flag.LastInTable = true; }
- void Clear_First(HashNodeClass<T,U> *ptr) {ptr->Flag.FirstInTable = false;}
- void Clear_Last(HashNodeClass<T,U> *ptr) {ptr->Flag.LastInTable = false; }
-
- void Set_List_Created(HashNodeClass<T,U> *ptr) {ptr->Flag.ListCreated = true; }
- void Clear_List_Created(HashNodeClass<T,U> *ptr) {ptr->Flag.ListCreated = false; }
- bool Is_List_Created(HashNodeClass<T,U> *ptr) {return(ptr->Flag.ListCreated);}
-
- void Unlink(HashNodeClass<T,U> *ptr) {ptr->Unlink();}
- };
- #pragma warning(pop)
- // The sole purpose of using NumHashValues as a template is to make it easier to
- // view in a debugger. If it were an allocated array of pointers, the debugger does
- // not easily show each element in the array.
- template<class T, class U = class HashDefaultUserClass, int NumHashValues = A_LARGE_PRIME_NUMBER>
- class HashListClass : protected HashNodeFriendClass<T,U>
- {
- public:
- // Creation and destruction.
- HashListClass() :
- List(),NumRecords(0), UsedValues(0)
- {
- memset(HashTable, 0, sizeof(HashTable));
- }
- ~HashListClass() {Delete();}
-
- // Get first node in list so user can itterate through it.
- // CAUTION: Do not destroy pointer returned or itterated though -
- // you must call the Remove function on it.
- // An assert() will come up if you try.
- HashNodeClass<T,U> *First() {
- return((HashNodeClass<T,U> *)List.First());
- }
- HashNodeClass<T,U> *First_Valid() {
- return((HashNodeClass<T,U> *)List.First_Valid());
- }
- HashNodeClass<T,U> *Last() {
- return((HashNodeClass<T,U> *)List.Last());
- }
- HashNodeClass<T,U> *Last_Valid() {
- return((HashNodeClass<T,U> *)List.Last_Valid());
- }
- // Add a record to hash creating new HashNodeClass.
- HashNodeClass<T,U> *Add(T record, unsigned key);
-
- // Add an existing node not in a list to this list.
- // This allows closer behavior to List class.
- HashNodeClass<T,U> *Add(HashNodeClass<T,U> *node);
-
- // Search for record based off key in hash table.
- HashNodeClass<T,U> *Find(unsigned key);
-
- // Use this remove if you know the node * already. It is quicker.
- void Remove(HashNodeClass<T,U> *node);
-
- // Use this remove if you need to do a find to get the node.
- // OK if key does not exist in table.
- bool Remove(unsigned key);
-
- // Removes all nodes in list.
- void Delete(void) {while (First()->Is_Valid()) Remove(First());}
-
- // Move contents of one list to another (this becomes empty).
- void Move_To(HashListClass<T,U> *newlist);
-
- // So we always do it the same way.
- unsigned Hash_Value(unsigned key) {
- return(unsigned)(key % NumHashValues);
- }
-
- // Total number of records in hash table.
- int Num_Records() {
- return(NumRecords);
- }
- // how many hash values are used.
- // The closer this number is to NumRecords, the
- // better hashing going on.
- int Num_Used_Values() {
- return(UsedValues);
- }
-
- protected:
- // This list can be walked from start to end of all objects in the list.
- // User can use Get_First() to get first (valid) record in linked list.
- List<DataNode<T> *> List;
- // Points to first record in List that has the right 'value'.
- // Must check LaastInTable to see if end of records in has list.
- HashNodeClass<T,U> *HashTable[NumHashValues];
-
- // Number of records we have in class.
- int NumRecords;
- int UsedValues;
-
- };
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////////////////////////////////////////////////////////////////////////////////////////
- /***********************************************************************************************
- * HashListClass::Add -- Add record to hash list. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 05/26/1999 SKB : Created. *
- *=============================================================================================*/
- template <class T, class U, int NumHashValues>
- HashNodeClass<T,U> *HashListClass<T, U, NumHashValues>::Add(HashNodeClass<T,U> *node)
- {
- // Get our index into the hash table.
- int hashidx = Hash_Value(node->Get_Key());
- assert(hashidx < NumHashValues);
- // Tell system we are being put in the list (and make sure we weren't already in one).
- assert(!node->Is_In_List());
- Set_In_List(node);
-
- // Get first hit in table.
- HashNodeClass<T,U> *first = HashTable[hashidx];
- if (first) {
- // This will add the new node right after the first node in the list.
- first->Link(node);
-
- // If we are the first one to collide, then we will be at end of list.
- // otherwise, we are at the end.
- if (Is_Last(first)) {
- Clear_Last(first);
- Set_Last(node);
- }
- } else {
- // We are first hit at hash index. Put ourselves at start of
- List.Add_Head(node);
- Set_First(node);
- Set_Last(node);
- HashTable[hashidx] = node;
- UsedValues++;
- }
- NumRecords++;
- return (node);
- }
-
- /***********************************************************************************************
- * HashListClass::Add -- Add record to list, list creates node for user. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 06/03/1999 SKB : Created. *
- *=============================================================================================*/
- template <class T, class U, int NumHashValues>
- HashNodeClass<T,U> *HashListClass<T, U, NumHashValues>::Add(T record, unsigned key)
- {
- // Create new node so we can add our record.
- HashNodeClass<T,U> *node = new HashNodeClass<T,U>(record, key);
- Set_List_Created(node);
-
- // Now let the other add do all the work.
- Add(node);
- return (node);
- }
- /***********************************************************************************************
- * HashListClass::Find -- Find record in hash table. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 05/26/1999 SKB : Created. *
- *=============================================================================================*/
- template <class T, class U, int NumHashValues>
- HashNodeClass<T,U> *HashListClass<T, U, NumHashValues>::Find(unsigned key)
- {
- int hashidx = Hash_Value(key);
- assert(hashidx < NumHashValues);
-
- HashNodeClass<T,U> *cur = HashTable[hashidx];
- if (cur) for(;;) {
- // Have we found our match?
- if (cur->Get_Key() == key) {
- return(cur);
- }
-
- // We have to find record before the end of the list.
- if (Is_Last(cur)) {
- break;
- }
-
- // Continue on because we appear to have some collisions.
- cur = cur->Next();
-
- // The end of a list should always be marked with Is_Last().
- assert(cur);
- assert(cur->Is_Valid());
- }
- return(NULL);
-
- }
- /***********************************************************************************************
- * HashListClass::Remove_Node -- Remove node from hash table. *
- * Protected function for usage by Remove() routines. *
- * *
- * INPUT: *
- * HashNodeClass<T,U> *node - node to remove. *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 06/02/1999 SKB : Created. *
- * 01/19/2000 SKB : Remove the node with Unlink. *
- *=============================================================================================*/
- template <class T, class U, int NumHashValues>
- void HashListClass<T, U, NumHashValues>::Remove(HashNodeClass<T,U> *node)
- {
- assert(node);
- assert(node->Is_In_List());
-
- // See if entry is first in list for hash value.
- if (Is_First(node)) {
-
- int hashidx = Hash_Value(node->Get_Key());
- assert(HashTable[hashidx]);
- // SKB: 2/20/01 - clear incase inserted in new list later.
- Clear_First(node);
- // is it only one in hash index?
- if (Is_Last(node)) {
- // SKB: 2/20/01 - clear incase inserted in new list later.
- Clear_Last(node);
- HashTable[hashidx] = NULL;
- UsedValues--;
- } else {
- HashTable[hashidx] = node->Next_Valid();
- Set_First(HashTable[hashidx]);
- }
- } else if (Is_Last(node)) {
- // SKB: 2/20/01 - clear incase inserted in new list later.
- Clear_Last(node);
- // There is one before us because it failed above.
- Set_Last(node->Prev());
- }
-
-
- // All done with you. Set flag to avoid assert.
- Clear_In_List(node);
-
- // Delete node if it was created by the this class instead of by the user.
- if (Is_List_Created(node)) {
- delete node;
- } else {
- // Remove it from the link list at least so it can be put in another.
- Unlink(node);
- }
- NumRecords--;
- }
- /***********************************************************************************************
- * HashListClass::Remove -- Remove record from hash table. *
- * *
- * INPUT: *
- * unsigned key - key to find node by. *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 05/26/1999 SKB : Created. *
- *=============================================================================================*/
- template <class T, class U, int NumHashValues>
- bool HashListClass<T, U, NumHashValues>::Remove(unsigned key)
- {
- int hashidx = Hash_Value(key);
- assert(hashidx < NumHashValues);
-
- HashNodeClass<T,U> *node = Find(key);
- if (node) {
- Remove(node);
- return(true);
- }
- return(false);
- }
-
- /***********************************************************************************************
- * HashListClass::Move_To -- Move nodes from one list to another. *
- * *
- * INPUT: *
- * *
- * OUTPUT: *
- * *
- * WARNINGS: *
- * *
- * HISTORY: *
- * 06/04/1999 SKB : Created. *
- *=============================================================================================*/
- template <class T, class U, int NumHashValues>
- void HashListClass<T, U, NumHashValues>::Move_To(HashListClass<T,U> *newlist)
- {
- HashNodeClass<T,U> *node = First_Valid();
- while (node) {
- HashNodeClass<T,U> *next = node->Next_Valid();
-
- // If list created node, clear that fact for a moment so it
- // will not get deleted in the remove.
- if (Is_List_Created(node)) {
- Clear_List_Created(node);
- Remove(node);
- newlist->Add(node);
- Set_List_Created(node);
- } else {
- Remove(node);
- newlist->Add(node);
- }
- node = next;
- }
- }
- #pragma warning (pop)
- #endif // HASHLIST_H
-
|