| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586 |
- /*
- ** 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/>.
- */
- /****************************************************************************\
- * 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: Carpenter (The RedAlert ladder creator)
- File Name : dictionary.h
- Author : Neal Kettler
- Start Date : June 1, 1997
- Last Update : June 17, 1997
- This template file implements a hash dictionary. A hash dictionary is
- used to quickly match a value with a key. This works well for very
- large sets of data. A table is constructed that has some power of two
- number of pointers in it. Any value to be put in the table has a hashing
- function applied to the key. That key/value pair is then put in the
- linked list at the slot the hashing function specifies. If everything
- is working well, this is much faster than a linked list, but only if
- your hashing function is good.
- \****************************************************************************/
- #ifndef DICTIONARY_HEADER
- #define DICTIONARY_HEADER
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <assert.h>
- #include "wstypes.h"
- #include "wdebug.h"
- // Every entry in the hash dictionary must be an instance of the DNode
- // template. 'K' and 'V' denote Key and Value.
- template <class K,class V>
- class DNode
- {
- public:
- K key;
- V value;
- DNode<K,V> *hashNext;
- };
- template <class K,class V>
- class Dictionary
- {
- public:
- Dictionary(uint32 (* hashFn)(K &key));
- ~Dictionary();
- void clear(void);
- bit8 add(IN K &key,IN V &value);
- bit8 getValue(IN K &key, OUT V &value);
- void print(IN FILE *out) const;
- uint32 getSize(void) const;
- uint32 getEntries(void) const;
- bit8 contains(IN K &key);
- bit8 updateValue(IN K &key,IN V &value);
- bit8 remove(IN K &key,OUT V &value);
- bit8 remove(IN K &key);
- bit8 removeAny(OUT K &key,OUT V &value);
- bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) const;
- private:
- void shrink(void); // halve the number of slots
- void expand(void); // double the number of slots
- DNode<K,V> **table; // This stores the lists at each slot
- uint32 entries; // number of entries
- uint32 size; // size of table
- uint32 tableBits; // table is 2^tableBits big
- uint32 log2Size; // Junk variable
- bit8 keepSize; // If true don't shrink or expand
- uint32 (* hashFunc)(K &key); // User provided hash function
- uint32 keyHash(IN K &key); // This will reduce to correct range
- // See initilizer list of constructor for values
- const double SHRINK_THRESHOLD; // When table is this % full shrink it
- const double EXPAND_THRESHOLD; // When table is this % full grow it
- const int MIN_TABLE_SIZE; // must be a power of 2
- };
- //Create the empty hash dictionary
- template <class K,class V>
- Dictionary<K,V>::Dictionary(uint32 (* hashFn)(K &key)) :
- SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it
- EXPAND_THRESHOLD(0.80), // When table is 80% full grow it
- MIN_TABLE_SIZE(32) // must be a power of 2
- {
- log2Size=MIN_TABLE_SIZE;
- size=MIN_TABLE_SIZE;
- assert(size>=4);
- tableBits=0;
- while(log2Size) { tableBits++; log2Size>>=1; }
- tableBits--;
- size=1<<tableBits; //Just in case MIN_TABLE_SIZE wasn't a power of 2
- entries=0;
- keepSize=FALSE;
- //Table is a pointer to a list of pointers (the hash table)
- table=(DNode<K,V> **)new DNode<K,V>* [size];
- assert(table!=NULL);
- memset((void *)table,0,size*sizeof(void *));
- hashFunc=hashFn;
- }
- //Free all the memory...
- template <class K,class V>
- Dictionary<K,V>::~Dictionary()
- {
- clear(); // Remove the entries
- delete[](table); // And the table as well
- }
- // Remove all the entries and free the memory
- template <class K,class V>
- void Dictionary<K,V>::clear()
- {
- DNode<K,V> *temp,*del;
- uint32 i;
- //free all the data
- for (i=0; i<size; i++)
- {
- temp=table[i];
- while(temp!=NULL)
- {
- del=temp;
- temp=temp->hashNext;
- delete(del);
- }
- table[i]=NULL;
- }
- entries=0;
- while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE))
- shrink();
- }
- template <class K,class V>
- uint32 Dictionary<K,V>::keyHash(IN K &key)
- {
- uint32 retval=hashFunc(key);
- retval &= ((1<<tableBits)-1);
- assert(retval<getSize());
- return(retval);
- }
- template <class K,class V>
- void Dictionary<K,V>::print(IN FILE *out) const
- {
- DNode<K,V> *temp;
- uint32 i;
- fprintf(out,"--------------------\n");
- for (i=0; i<getSize(); i++)
- {
- temp=table[i];
- fprintf(out," |\n");
- fprintf(out,"[ ]");
- while (temp!=NULL)
- {
- fprintf(out,"--[ ]");
- temp=temp->hashNext;
- }
- fprintf(out,"\n");
- }
- fprintf(out,"--------------------\n");
- }
- //
- // Iterate through all the records. Index is for the table, offset specifies the
- // element in the linked list. Set both to 0 and continue calling till false
- // is returned.
- template <class K,class V>
- bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
- OUT V &value) const
- {
- DNode<K,V> *temp;
- // index out of range
- if ((index<0)||(index >= getSize()))
- return(FALSE);
- temp=table[index];
- while ((temp==NULL)&&((++index) < getSize()))
- {
- temp=table[index];
- offset=0;
- }
- if (temp==NULL) // no more slots with data
- return(FALSE);
- uint32 i=0;
- while ((temp!=NULL) && (i < offset))
- {
- temp=temp->hashNext;
- i++;
- }
- if (temp==NULL) // should never happen
- return(FALSE);
- value=temp->value;
- if (temp->hashNext==NULL)
- {
- index++;
- offset=0;
- }
- else
- offset++;
- return(TRUE);
- }
- // Return the current size of the hash table
- template <class K,class V>
- uint32 Dictionary<K,V>::getSize(void) const
- { return(size); }
- // Return the current number of entries in the table
- template <class K,class V>
- uint32 Dictionary<K,V>::getEntries(void) const
- { return(entries); }
- // Does the Dictionary contain the key?
- template <class K,class V>
- bit8 Dictionary<K,V>::contains(IN K &key)
- {
- int offset;
- DNode<K,V> *node;
- offset=keyHash(key);
- node=table[offset];
- if (node==NULL)
- { return(FALSE); } // can't find it
- while(node!=NULL)
- {
- if ((node->key)==key)
- { return(TRUE); }
- node=node->hashNext;
- }
- return(FALSE);
- }
- // Try and update the value of an already existing object
- template <class K,class V>
- bit8 Dictionary<K,V>::updateValue(IN K &key,IN V &value)
- {
- sint32 retval;
- retval=remove(key);
- if (retval==FALSE)
- return(FALSE);
- add(key,value);
- return(TRUE);
- }
- // Add to the dictionary (if key exists, value is updated with the new V)
- template <class K, class V>
- bit8 Dictionary<K,V>::add(IN K &key,IN V &value)
- {
- int offset;
- DNode<K,V> *node,*item,*temp;
- float percent;
- item=(DNode<K,V> *)new DNode<K,V>;
- assert(item!=NULL);
- #ifdef KEY_MEM_OPS
- memcpy(&(item->key),&key,sizeof(K));
- #else
- item->key=key;
- #endif
- #ifdef VALUE_MEM_OPS
- memcpy(&(item->value),&value,sizeof(V));
- #else
- item->value=value;
- #endif
- item->hashNext=NULL;
- //If key already exists, it will be overwritten
- remove(key);
- offset=keyHash(key);
-
- node=table[offset];
- if (node==NULL)
- { table[offset]=item; }
- else
- {
- temp=table[offset];
- table[offset]=item;
- item->hashNext=temp;
- }
- entries++;
- percent=(float)entries;
- percent/=(float)getSize();
- if (percent>= EXPAND_THRESHOLD ) expand();
- return(TRUE);
- }
- // Remove an item from the dictionary
- template <class K,class V>
- bit8 Dictionary<K,V>::remove(IN K &key,OUT V &value)
- {
- int offset;
- DNode<K,V> *node,*last,*temp;
- float percent;
- if (entries==0)
- return(FALSE);
- percent=(float)(entries-1);
- percent/=(float)getSize();
- offset=keyHash(key);
- node=table[offset];
- last=node;
- if (node==NULL) return(FALSE);
- //special case table points to thing to delete
- #ifdef KEY_MEM_OPS
- if (0==memcmp(&(node->key),&key,sizeof(K)))
- #else
- if ((node->key)==key)
- #endif
- {
- #ifdef VALUE_MEM_OPS
- memcpy(&value,&(node->value),sizeof(V));
- #else
- value=node->value;
- #endif
- temp=table[offset]->hashNext;
- delete(table[offset]);
- table[offset]=temp;
- entries--;
- if (percent <= SHRINK_THRESHOLD)
- shrink();
- return(TRUE);
- }
- node=node->hashNext;
- //Now the case if the thing to delete is not the first
- while (node!=NULL)
- {
- #ifdef KEY_MEM_OPS
- if (0==memcmp(&(node->key),&key,sizeof(K)))
- #else
- if (node->key==key)
- #endif
- {
- #ifdef VALUE_MEM_OPS
- memcpy(&value,&(node->value),sizeof(V));
- #else
- value=node->value;
- #endif
- last->hashNext=node->hashNext;
- entries--;
- delete(node);
- break;
- }
- last=node;
- node=node->hashNext;
- }
- if (percent <= SHRINK_THRESHOLD)
- shrink();
- return(TRUE);
- }
- template <class K,class V>
- bit8 Dictionary<K,V>::remove(IN K &key)
- {
- V temp;
- return(remove(key,temp));
- }
- // Remove some random K/V pair that's in the Dictionary
- template <class K,class V>
- bit8 Dictionary<K,V>::removeAny(OUT K &key,OUT V &value)
- {
- int offset;
- DNode<K,V> *node,*last,*temp;
- float percent;
- if (entries==0)
- return(FALSE);
- percent=(entries-1);
- percent/=(float)getSize();
- int i;
- offset=-1;
- for (i=0; i<(int)getSize(); i++)
- if (table[i]!=NULL)
- {
- offset=i;
- break;
- }
- if (offset==-1) // Nothing there
- return(FALSE);
- node=table[offset];
- last=node;
- #ifdef KEY_MEM_OPS
- memcpy(&key,&(node->key),sizeof(K));
- #else
- key=node->key;
- #endif
- #ifdef VALUE_MEM_OPS
- memcpy(&value,&(node->value),sizeof(V));
- #else
- value=node->value;
- #endif
- temp=table[offset]->hashNext;
- delete(table[offset]);
- table[offset]=temp;
- entries--;
- if (percent <= SHRINK_THRESHOLD)
- shrink();
- return(TRUE);
- }
- template <class K,class V>
- bit8 Dictionary<K,V>::getValue(IN K &key,OUT V &value)
- {
- int offset;
- DNode<K,V> *node;
- offset=keyHash(key);
- node=table[offset];
- if (node==NULL) return(FALSE);
- #ifdef KEY_MEM_OPS
- while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K))))
- #else
- while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't
- #endif // have to do oper !=
- { node=node->hashNext; }
- if (node==NULL)
- { return(FALSE); }
- #ifdef VALUE_MEM_OPS
- memcpy(&value,&(node->value),sizeof(V));
- #else
- value=(node->value);
- #endif
- return(TRUE);
- }
- //A note about Shrink and Expand: They are never necessary, they are
- //only here to improve performance of the hash table by reducing
- //the length of the linked list at each table entry.
- // Shrink the hash table by a factor of 2 (and relocate entries)
- template <class K,class V>
- void Dictionary<K,V>::shrink(void)
- {
- int i;
- int oldsize;
- uint32 offset;
- DNode<K,V> **oldtable,*temp,*first,*next;
- if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE))
- return;
- //fprintf(stderr,"Shrinking....\n");
- oldtable=table;
- oldsize=size;
- size/=2;
- tableBits--;
- table=(DNode<K,V> **)new DNode<K,V>*[size];
- assert(table!=NULL);
- memset((void *)table,0,size*sizeof(void *));
- for (i=0; i<oldsize; i++)
- {
- temp=oldtable[i];
- while (temp!=NULL)
- {
- offset=keyHash(temp->key);
- first=table[offset];
- table[offset]=temp;
- next=temp->hashNext;
- temp->hashNext=first;
- temp=next;
- }
- }
- delete[](oldtable);
- }
- template <class K,class V>
- void Dictionary<K,V>::expand(void)
- {
- int i;
- int oldsize;
- uint32 offset;
- DNode<K,V> **oldtable,*temp,*first,*next;
- if (keepSize==TRUE)
- return;
- //fprintf(stderr,"Expanding...\n");
- oldtable=table;
- oldsize=size;
- size*=2;
- tableBits++;
- table=(DNode<K,V> **)new DNode<K,V>* [size];
- assert(table!=NULL);
- memset((void *)table,0,size*sizeof(void *));
- for (i=0; i<oldsize; i++)
- {
- temp=oldtable[i];
- while (temp!=NULL)
- {
- offset=keyHash(temp->key);
- first=table[offset];
- table[offset]=temp;
- next=temp->hashNext;
- temp->hashNext=first;
- temp=next;
- }
- }
- delete[](oldtable);
- }
- #endif
|