dictionary.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /****************************************************************************\
  19. * 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 *
  20. ******************************************************************************
  21. Project Name: Carpenter (The RedAlert ladder creator)
  22. File Name : dictionary.h
  23. Author : Neal Kettler
  24. Start Date : June 1, 1997
  25. Last Update : June 17, 1997
  26. This template file implements a hash dictionary. A hash dictionary is
  27. used to quickly match a value with a key. This works well for very
  28. large sets of data. A table is constructed that has some power of two
  29. number of pointers in it. Any value to be put in the table has a hashing
  30. function applied to the key. That key/value pair is then put in the
  31. linked list at the slot the hashing function specifies. If everything
  32. is working well, this is much faster than a linked list, but only if
  33. your hashing function is good.
  34. \****************************************************************************/
  35. #ifndef DICTIONARY_HEADER
  36. #define DICTIONARY_HEADER
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40. #include <assert.h>
  41. #include "wstypes.h"
  42. #include "wdebug.h"
  43. // Every entry in the hash dictionary must be an instance of the DNode
  44. // template. 'K' and 'V' denote Key and Value.
  45. template <class K,class V>
  46. class DNode
  47. {
  48. public:
  49. K key;
  50. V value;
  51. DNode<K,V> *hashNext;
  52. };
  53. template <class K,class V>
  54. class Dictionary
  55. {
  56. public:
  57. Dictionary(uint32 (* hashFn)(K &key));
  58. ~Dictionary();
  59. void clear(void);
  60. bit8 add(IN K &key,IN V &value);
  61. bit8 getValue(IN K &key, OUT V &value);
  62. void print(IN FILE *out) const;
  63. uint32 getSize(void) const;
  64. uint32 getEntries(void) const;
  65. bit8 contains(IN K &key);
  66. bit8 updateValue(IN K &key,IN V &value);
  67. bit8 remove(IN K &key,OUT V &value);
  68. bit8 remove(IN K &key);
  69. bit8 removeAny(OUT K &key,OUT V &value);
  70. bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) const;
  71. private:
  72. void shrink(void); // halve the number of slots
  73. void expand(void); // double the number of slots
  74. DNode<K,V> **table; // This stores the lists at each slot
  75. uint32 entries; // number of entries
  76. uint32 size; // size of table
  77. uint32 tableBits; // table is 2^tableBits big
  78. uint32 log2Size; // Junk variable
  79. bit8 keepSize; // If true don't shrink or expand
  80. uint32 (* hashFunc)(K &key); // User provided hash function
  81. uint32 keyHash(IN K &key); // This will reduce to correct range
  82. // See initilizer list of constructor for values
  83. const double SHRINK_THRESHOLD; // When table is this % full shrink it
  84. const double EXPAND_THRESHOLD; // When table is this % full grow it
  85. const int MIN_TABLE_SIZE; // must be a power of 2
  86. };
  87. //Create the empty hash dictionary
  88. template <class K,class V>
  89. Dictionary<K,V>::Dictionary(uint32 (* hashFn)(K &key)) :
  90. SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it
  91. EXPAND_THRESHOLD(0.80), // When table is 80% full grow it
  92. MIN_TABLE_SIZE(32) // must be a power of 2
  93. {
  94. log2Size=MIN_TABLE_SIZE;
  95. size=MIN_TABLE_SIZE;
  96. assert(size>=4);
  97. tableBits=0;
  98. while(log2Size) { tableBits++; log2Size>>=1; }
  99. tableBits--;
  100. size=1<<tableBits; //Just in case MIN_TABLE_SIZE wasn't a power of 2
  101. entries=0;
  102. keepSize=FALSE;
  103. //Table is a pointer to a list of pointers (the hash table)
  104. table=(DNode<K,V> **)new DNode<K,V>* [size];
  105. assert(table!=NULL);
  106. memset((void *)table,0,size*sizeof(void *));
  107. hashFunc=hashFn;
  108. }
  109. //Free all the memory...
  110. template <class K,class V>
  111. Dictionary<K,V>::~Dictionary()
  112. {
  113. clear(); // Remove the entries
  114. delete[](table); // And the table as well
  115. }
  116. // Remove all the entries and free the memory
  117. template <class K,class V>
  118. void Dictionary<K,V>::clear()
  119. {
  120. DNode<K,V> *temp,*del;
  121. uint32 i;
  122. //free all the data
  123. for (i=0; i<size; i++)
  124. {
  125. temp=table[i];
  126. while(temp!=NULL)
  127. {
  128. del=temp;
  129. temp=temp->hashNext;
  130. delete(del);
  131. }
  132. table[i]=NULL;
  133. }
  134. entries=0;
  135. while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE))
  136. shrink();
  137. }
  138. template <class K,class V>
  139. uint32 Dictionary<K,V>::keyHash(IN K &key)
  140. {
  141. uint32 retval=hashFunc(key);
  142. retval &= ((1<<tableBits)-1);
  143. assert(retval<getSize());
  144. return(retval);
  145. }
  146. template <class K,class V>
  147. void Dictionary<K,V>::print(IN FILE *out) const
  148. {
  149. DNode<K,V> *temp;
  150. uint32 i;
  151. fprintf(out,"--------------------\n");
  152. for (i=0; i<getSize(); i++)
  153. {
  154. temp=table[i];
  155. fprintf(out," |\n");
  156. fprintf(out,"[ ]");
  157. while (temp!=NULL)
  158. {
  159. fprintf(out,"--[ ]");
  160. temp=temp->hashNext;
  161. }
  162. fprintf(out,"\n");
  163. }
  164. fprintf(out,"--------------------\n");
  165. }
  166. //
  167. // Iterate through all the records. Index is for the table, offset specifies the
  168. // element in the linked list. Set both to 0 and continue calling till false
  169. // is returned.
  170. template <class K,class V>
  171. bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
  172. OUT V &value) const
  173. {
  174. DNode<K,V> *temp;
  175. // index out of range
  176. if ((index<0)||(index >= getSize()))
  177. return(FALSE);
  178. temp=table[index];
  179. while ((temp==NULL)&&((++index) < getSize()))
  180. {
  181. temp=table[index];
  182. offset=0;
  183. }
  184. if (temp==NULL) // no more slots with data
  185. return(FALSE);
  186. uint32 i=0;
  187. while ((temp!=NULL) && (i < offset))
  188. {
  189. temp=temp->hashNext;
  190. i++;
  191. }
  192. if (temp==NULL) // should never happen
  193. return(FALSE);
  194. value=temp->value;
  195. if (temp->hashNext==NULL)
  196. {
  197. index++;
  198. offset=0;
  199. }
  200. else
  201. offset++;
  202. return(TRUE);
  203. }
  204. // Return the current size of the hash table
  205. template <class K,class V>
  206. uint32 Dictionary<K,V>::getSize(void) const
  207. { return(size); }
  208. // Return the current number of entries in the table
  209. template <class K,class V>
  210. uint32 Dictionary<K,V>::getEntries(void) const
  211. { return(entries); }
  212. // Does the Dictionary contain the key?
  213. template <class K,class V>
  214. bit8 Dictionary<K,V>::contains(IN K &key)
  215. {
  216. int offset;
  217. DNode<K,V> *node;
  218. offset=keyHash(key);
  219. node=table[offset];
  220. if (node==NULL)
  221. { return(FALSE); } // can't find it
  222. while(node!=NULL)
  223. {
  224. if ((node->key)==key)
  225. { return(TRUE); }
  226. node=node->hashNext;
  227. }
  228. return(FALSE);
  229. }
  230. // Try and update the value of an already existing object
  231. template <class K,class V>
  232. bit8 Dictionary<K,V>::updateValue(IN K &key,IN V &value)
  233. {
  234. sint32 retval;
  235. retval=remove(key);
  236. if (retval==FALSE)
  237. return(FALSE);
  238. add(key,value);
  239. return(TRUE);
  240. }
  241. // Add to the dictionary (if key exists, value is updated with the new V)
  242. template <class K, class V>
  243. bit8 Dictionary<K,V>::add(IN K &key,IN V &value)
  244. {
  245. int offset;
  246. DNode<K,V> *node,*item,*temp;
  247. float percent;
  248. item=(DNode<K,V> *)new DNode<K,V>;
  249. assert(item!=NULL);
  250. #ifdef KEY_MEM_OPS
  251. memcpy(&(item->key),&key,sizeof(K));
  252. #else
  253. item->key=key;
  254. #endif
  255. #ifdef VALUE_MEM_OPS
  256. memcpy(&(item->value),&value,sizeof(V));
  257. #else
  258. item->value=value;
  259. #endif
  260. item->hashNext=NULL;
  261. //If key already exists, it will be overwritten
  262. remove(key);
  263. offset=keyHash(key);
  264. node=table[offset];
  265. if (node==NULL)
  266. { table[offset]=item; }
  267. else
  268. {
  269. temp=table[offset];
  270. table[offset]=item;
  271. item->hashNext=temp;
  272. }
  273. entries++;
  274. percent=(float)entries;
  275. percent/=(float)getSize();
  276. if (percent>= EXPAND_THRESHOLD ) expand();
  277. return(TRUE);
  278. }
  279. // Remove an item from the dictionary
  280. template <class K,class V>
  281. bit8 Dictionary<K,V>::remove(IN K &key,OUT V &value)
  282. {
  283. int offset;
  284. DNode<K,V> *node,*last,*temp;
  285. float percent;
  286. if (entries==0)
  287. return(FALSE);
  288. percent=(float)(entries-1);
  289. percent/=(float)getSize();
  290. offset=keyHash(key);
  291. node=table[offset];
  292. last=node;
  293. if (node==NULL) return(FALSE);
  294. //special case table points to thing to delete
  295. #ifdef KEY_MEM_OPS
  296. if (0==memcmp(&(node->key),&key,sizeof(K)))
  297. #else
  298. if ((node->key)==key)
  299. #endif
  300. {
  301. #ifdef VALUE_MEM_OPS
  302. memcpy(&value,&(node->value),sizeof(V));
  303. #else
  304. value=node->value;
  305. #endif
  306. temp=table[offset]->hashNext;
  307. delete(table[offset]);
  308. table[offset]=temp;
  309. entries--;
  310. if (percent <= SHRINK_THRESHOLD)
  311. shrink();
  312. return(TRUE);
  313. }
  314. node=node->hashNext;
  315. //Now the case if the thing to delete is not the first
  316. while (node!=NULL)
  317. {
  318. #ifdef KEY_MEM_OPS
  319. if (0==memcmp(&(node->key),&key,sizeof(K)))
  320. #else
  321. if (node->key==key)
  322. #endif
  323. {
  324. #ifdef VALUE_MEM_OPS
  325. memcpy(&value,&(node->value),sizeof(V));
  326. #else
  327. value=node->value;
  328. #endif
  329. last->hashNext=node->hashNext;
  330. entries--;
  331. delete(node);
  332. break;
  333. }
  334. last=node;
  335. node=node->hashNext;
  336. }
  337. if (percent <= SHRINK_THRESHOLD)
  338. shrink();
  339. return(TRUE);
  340. }
  341. template <class K,class V>
  342. bit8 Dictionary<K,V>::remove(IN K &key)
  343. {
  344. V temp;
  345. return(remove(key,temp));
  346. }
  347. // Remove some random K/V pair that's in the Dictionary
  348. template <class K,class V>
  349. bit8 Dictionary<K,V>::removeAny(OUT K &key,OUT V &value)
  350. {
  351. int offset;
  352. DNode<K,V> *node,*last,*temp;
  353. float percent;
  354. if (entries==0)
  355. return(FALSE);
  356. percent=(entries-1);
  357. percent/=(float)getSize();
  358. int i;
  359. offset=-1;
  360. for (i=0; i<(int)getSize(); i++)
  361. if (table[i]!=NULL)
  362. {
  363. offset=i;
  364. break;
  365. }
  366. if (offset==-1) // Nothing there
  367. return(FALSE);
  368. node=table[offset];
  369. last=node;
  370. #ifdef KEY_MEM_OPS
  371. memcpy(&key,&(node->key),sizeof(K));
  372. #else
  373. key=node->key;
  374. #endif
  375. #ifdef VALUE_MEM_OPS
  376. memcpy(&value,&(node->value),sizeof(V));
  377. #else
  378. value=node->value;
  379. #endif
  380. temp=table[offset]->hashNext;
  381. delete(table[offset]);
  382. table[offset]=temp;
  383. entries--;
  384. if (percent <= SHRINK_THRESHOLD)
  385. shrink();
  386. return(TRUE);
  387. }
  388. template <class K,class V>
  389. bit8 Dictionary<K,V>::getValue(IN K &key,OUT V &value)
  390. {
  391. int offset;
  392. DNode<K,V> *node;
  393. offset=keyHash(key);
  394. node=table[offset];
  395. if (node==NULL) return(FALSE);
  396. #ifdef KEY_MEM_OPS
  397. while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K))))
  398. #else
  399. while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't
  400. #endif // have to do oper !=
  401. { node=node->hashNext; }
  402. if (node==NULL)
  403. { return(FALSE); }
  404. #ifdef VALUE_MEM_OPS
  405. memcpy(&value,&(node->value),sizeof(V));
  406. #else
  407. value=(node->value);
  408. #endif
  409. return(TRUE);
  410. }
  411. //A note about Shrink and Expand: They are never necessary, they are
  412. //only here to improve performance of the hash table by reducing
  413. //the length of the linked list at each table entry.
  414. // Shrink the hash table by a factor of 2 (and relocate entries)
  415. template <class K,class V>
  416. void Dictionary<K,V>::shrink(void)
  417. {
  418. int i;
  419. int oldsize;
  420. uint32 offset;
  421. DNode<K,V> **oldtable,*temp,*first,*next;
  422. if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE))
  423. return;
  424. //fprintf(stderr,"Shrinking....\n");
  425. oldtable=table;
  426. oldsize=size;
  427. size/=2;
  428. tableBits--;
  429. table=(DNode<K,V> **)new DNode<K,V>*[size];
  430. assert(table!=NULL);
  431. memset((void *)table,0,size*sizeof(void *));
  432. for (i=0; i<oldsize; i++)
  433. {
  434. temp=oldtable[i];
  435. while (temp!=NULL)
  436. {
  437. offset=keyHash(temp->key);
  438. first=table[offset];
  439. table[offset]=temp;
  440. next=temp->hashNext;
  441. temp->hashNext=first;
  442. temp=next;
  443. }
  444. }
  445. delete[](oldtable);
  446. }
  447. template <class K,class V>
  448. void Dictionary<K,V>::expand(void)
  449. {
  450. int i;
  451. int oldsize;
  452. uint32 offset;
  453. DNode<K,V> **oldtable,*temp,*first,*next;
  454. if (keepSize==TRUE)
  455. return;
  456. //fprintf(stderr,"Expanding...\n");
  457. oldtable=table;
  458. oldsize=size;
  459. size*=2;
  460. tableBits++;
  461. table=(DNode<K,V> **)new DNode<K,V>* [size];
  462. assert(table!=NULL);
  463. memset((void *)table,0,size*sizeof(void *));
  464. for (i=0; i<oldsize; i++)
  465. {
  466. temp=oldtable[i];
  467. while (temp!=NULL)
  468. {
  469. offset=keyHash(temp->key);
  470. first=table[offset];
  471. table[offset]=temp;
  472. next=temp->hashNext;
  473. temp->hashNext=first;
  474. temp=next;
  475. }
  476. }
  477. delete[](oldtable);
  478. }
  479. #endif