dictionary.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. /*
  2. ** Command & Conquer Renegade(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. // Every entry in the hash dictionary must be an instance of the DNode
  43. // template. 'K' and 'V' denote Key and Value.
  44. template <class K,class V>
  45. class DNode
  46. {
  47. public:
  48. K key;
  49. V value;
  50. DNode<K,V> *hashNext;
  51. };
  52. template <class K,class V>
  53. class Dictionary
  54. {
  55. public:
  56. ////////////////Dictionary(uint32 (* hashFn)(K &key));
  57. // Note: I had to put this inside the class definition because VC5 sucks butt
  58. //Create the empty hash dictionary
  59. Dictionary(uint32 (*hashFn)(const K &key)) :
  60. SHRINK_THRESHOLD(0.20), // When table is only 20% full shrink it
  61. EXPAND_THRESHOLD(0.80), // When table is 80% full grow it
  62. MIN_TABLE_SIZE(128) // must be a power of 2
  63. {
  64. log2Size=MIN_TABLE_SIZE;
  65. size=MIN_TABLE_SIZE;
  66. assert(size>=4);
  67. tableBits=0;
  68. while(log2Size) { tableBits++; log2Size>>=1; }
  69. tableBits--;
  70. size=1<<tableBits; //Just in case MIN_TABLE_SIZE wasn't a power of 2
  71. entries=0;
  72. keepSize=FALSE;
  73. //Table is a pointer to a list of pointers (the hash table)
  74. table=(DNode<K,V> **)new DNode<K,V>* [size];
  75. assert(table!=NULL);
  76. memset((void *)table,0,size*sizeof(void *));
  77. hashFunc=hashFn;
  78. }
  79. ~Dictionary();
  80. void clear(void);
  81. bit8 add(IN K &key,IN V &value);
  82. bool getValue(IN K &key, OUT V &value) RO;
  83. bool getPointer(IN K &key, OUT V **value) RO; // ptr to internal storage (Careful!)
  84. void print(FILE *out) RO;
  85. uint32 getSize(void) RO;
  86. uint32 getEntries(void) RO;
  87. bit8 contains(IN K &key) RO;
  88. bit8 updateValue(IN K &key,IN V &value);
  89. bit8 remove(IN K &key,OUT V &value);
  90. bit8 remove(IN K &key);
  91. bit8 removeAny(OUT K &key,OUT V &value);
  92. bit8 iterate(INOUT int &index,INOUT int &offset, OUT V &value) RO;
  93. bit8 iterate(INOUT int &index,INOUT int &offset, OUT K &key, OUT V &value) RO;
  94. Dictionary<K,V> &operator=(Dictionary<K,V> &other);
  95. private:
  96. void shrink(void); // halve the number of slots
  97. void expand(void); // double the number of slots
  98. DNode<K,V> **table; // This stores the lists at each slot
  99. uint32 entries; // number of entries
  100. uint32 size; // size of table
  101. uint32 tableBits; // table is 2^tableBits big
  102. uint32 log2Size; // Junk variable
  103. bit8 keepSize; // If true don't shrink or expand
  104. uint32 (* hashFunc)(IN K &key); // User provided hash function
  105. uint32 keyHash(IN K &key) RO; // This will reduce to correct range
  106. // See initilizer list of constructor for values
  107. const double SHRINK_THRESHOLD; // When table is this % full shrink it
  108. const double EXPAND_THRESHOLD; // When table is this % full grow it
  109. const int MIN_TABLE_SIZE; // must be a power of 2
  110. };
  111. //Free all the memory...
  112. template <class K,class V>
  113. Dictionary<K,V>::~Dictionary()
  114. {
  115. clear(); // Remove the entries
  116. delete[](table); // And the table as well
  117. }
  118. // Remove all the entries and free the memory
  119. template <class K,class V>
  120. void Dictionary<K,V>::clear()
  121. {
  122. DNode<K,V> *temp,*del;
  123. uint32 i;
  124. //free all the data
  125. for (i=0; i<size; i++)
  126. {
  127. temp=table[i];
  128. while(temp!=NULL)
  129. {
  130. del=temp;
  131. temp=temp->hashNext;
  132. delete(del);
  133. }
  134. table[i]=NULL;
  135. }
  136. entries=0;
  137. while ((getSize()>(uint32)MIN_TABLE_SIZE)&&(keepSize==FALSE))
  138. shrink();
  139. }
  140. template <class K,class V>
  141. uint32 Dictionary<K,V>::keyHash(IN K &key) RO
  142. {
  143. uint32 retval=hashFunc(key);
  144. retval &= ((1<<tableBits)-1);
  145. assert(retval<getSize());
  146. return(retval);
  147. }
  148. template <class K,class V>
  149. void Dictionary<K,V>::print(FILE *out) RO
  150. {
  151. DNode<K,V> *temp;
  152. uint32 i;
  153. fprintf(out,"--------------------\n");
  154. for (i=0; i<getSize(); i++)
  155. {
  156. temp=table[i];
  157. fprintf(out," |\n");
  158. fprintf(out,"[ ]");
  159. while (temp!=NULL)
  160. {
  161. fprintf(out,"--[ ]");
  162. temp=temp->hashNext;
  163. }
  164. fprintf(out,"\n");
  165. }
  166. fprintf(out,"--------------------\n");
  167. }
  168. template <class K, class V>
  169. Dictionary<K,V> &Dictionary<K,V>::operator=(Dictionary<K,V> &other)
  170. {
  171. _ASSERTE(0);
  172. }
  173. //
  174. // Iterate through all the records. Index is for the table, offset specifies the
  175. // element in the linked list. Set both to 0 and continue calling till false
  176. // is returned.
  177. template <class K,class V>
  178. bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
  179. OUT V &value) RO
  180. {
  181. DNode<K,V> *temp;
  182. // index out of range
  183. if ((index<0)||(index >= (int)getSize()))
  184. return(FALSE);
  185. temp=table[index];
  186. while ((temp==NULL)&&((++index) < (int)getSize()))
  187. {
  188. temp=table[index];
  189. offset=0;
  190. }
  191. if (temp==NULL) // no more slots with data
  192. return(FALSE);
  193. uint32 i=0;
  194. while ((temp!=NULL) && ((int)i < offset))
  195. {
  196. temp=temp->hashNext;
  197. i++;
  198. }
  199. if (temp==NULL) // should never happen
  200. return(FALSE);
  201. value=temp->value;
  202. if (temp->hashNext==NULL)
  203. {
  204. index++;
  205. offset=0;
  206. }
  207. else
  208. offset++;
  209. return(TRUE);
  210. }
  211. //
  212. // Iterate through all the records. Index is for the table, offset specifies the
  213. // element in the linked list. Set both to 0 and continue calling till false
  214. // is returned.
  215. template <class K,class V>
  216. bit8 Dictionary<K,V>::iterate(INOUT int &index,INOUT int &offset,
  217. OUT K &key, OUT V &value) RO
  218. {
  219. DNode<K,V> *temp;
  220. // index out of range
  221. if ((index<0)||(index >= (int)getSize()))
  222. return(FALSE);
  223. temp=table[index];
  224. while ((temp==NULL)&&((++index) < (int)getSize()))
  225. {
  226. temp=table[index];
  227. offset=0;
  228. }
  229. if (temp==NULL) // no more slots with data
  230. return(FALSE);
  231. uint32 i=0;
  232. while ((temp!=NULL) && ((int)i < offset))
  233. {
  234. temp=temp->hashNext;
  235. i++;
  236. }
  237. if (temp==NULL) // should never happen
  238. return(FALSE);
  239. value=temp->value;
  240. key=temp->key;
  241. if (temp->hashNext==NULL)
  242. {
  243. index++;
  244. offset=0;
  245. }
  246. else
  247. offset++;
  248. return(TRUE);
  249. }
  250. // Return the current size of the hash table
  251. template <class K,class V>
  252. uint32 Dictionary<K,V>::getSize(void) RO
  253. { return(size); }
  254. // Return the current number of entries in the table
  255. template <class K,class V>
  256. uint32 Dictionary<K,V>::getEntries(void) RO
  257. { return(entries); }
  258. // Does the Dictionary contain the key?
  259. template <class K,class V>
  260. bit8 Dictionary<K,V>::contains(IN K &key) RO
  261. {
  262. int offset;
  263. DNode<K,V> *node;
  264. offset=keyHash(key);
  265. node=table[offset];
  266. if (node==NULL)
  267. { return(FALSE); } // can't find it
  268. while(node!=NULL)
  269. {
  270. if ((node->key)==key)
  271. { return(TRUE); }
  272. node=node->hashNext;
  273. }
  274. return(FALSE);
  275. }
  276. // Try and update the value of an already existing object
  277. template <class K,class V>
  278. bit8 Dictionary<K,V>::updateValue(IN K &key,IN V &value)
  279. {
  280. sint32 retval;
  281. retval=remove(key);
  282. if (retval==FALSE)
  283. return(FALSE);
  284. add(key,value);
  285. return(TRUE);
  286. }
  287. // Add to the dictionary (if key exists, value is updated with the new V)
  288. template <class K, class V>
  289. bit8 Dictionary<K,V>::add(IN K &key,IN V &value)
  290. {
  291. int offset;
  292. DNode<K,V> *node,*item,*temp;
  293. float percent;
  294. item=(DNode<K,V> *)new DNode<K,V>;
  295. assert(item!=NULL);
  296. #ifdef KEY_MEM_OPS
  297. memcpy(&(item->key),&key,sizeof(K));
  298. #else
  299. item->key=key;
  300. #endif
  301. #ifdef VALUE_MEM_OPS
  302. memcpy(&(item->value),&value,sizeof(V));
  303. #else
  304. item->value=value;
  305. #endif
  306. item->hashNext=NULL;
  307. //If key already exists, it will be overwritten
  308. remove(key); // Hopefully this will be false...
  309. offset=keyHash(key);
  310. node=table[offset];
  311. if (node==NULL)
  312. { table[offset]=item; }
  313. else
  314. {
  315. temp=table[offset];
  316. table[offset]=item;
  317. item->hashNext=temp;
  318. }
  319. entries++;
  320. percent=(float)entries;
  321. percent/=(float)getSize();
  322. if (percent>= EXPAND_THRESHOLD ) expand();
  323. return(TRUE);
  324. }
  325. // Remove an item from the dictionary
  326. template <class K,class V>
  327. bit8 Dictionary<K,V>::remove(IN K &key,OUT V &value)
  328. {
  329. int offset;
  330. DNode<K,V> *node,*last,*temp;
  331. float percent;
  332. if (entries==0)
  333. return(FALSE);
  334. percent=(float)(entries-1);
  335. percent/=(float)getSize();
  336. offset=keyHash(key);
  337. node=table[offset];
  338. last=node;
  339. if (node==NULL)
  340. return(FALSE);
  341. //special case table points to thing to delete
  342. #ifdef KEY_MEM_OPS
  343. if (0==memcmp(&(node->key),&key,sizeof(K)))
  344. #else
  345. if ((node->key)==key)
  346. #endif
  347. {
  348. #ifdef VALUE_MEM_OPS
  349. memcpy(&value,&(node->value),sizeof(V));
  350. #else
  351. value=node->value;
  352. #endif
  353. temp=table[offset]->hashNext;
  354. delete(table[offset]);
  355. table[offset]=temp;
  356. entries--;
  357. if (percent <= SHRINK_THRESHOLD)
  358. shrink();
  359. return(TRUE);
  360. }
  361. node=node->hashNext;
  362. bit8 retval=FALSE; // wow, didn't add this for years... (DOH!)
  363. //Now the case if the thing to delete is not the first
  364. while (node!=NULL)
  365. {
  366. #ifdef KEY_MEM_OPS
  367. if (0==memcmp(&(node->key),&key,sizeof(K)))
  368. #else
  369. if (node->key==key)
  370. #endif
  371. {
  372. #ifdef VALUE_MEM_OPS
  373. memcpy(&value,&(node->value),sizeof(V));
  374. #else
  375. value=node->value;
  376. #endif
  377. last->hashNext=node->hashNext;
  378. entries--;
  379. delete(node);
  380. retval=TRUE; // yes, we deleted something
  381. break;
  382. }
  383. last=node;
  384. node=node->hashNext;
  385. }
  386. if (percent <= SHRINK_THRESHOLD)
  387. shrink();
  388. return(retval);
  389. }
  390. template <class K,class V>
  391. bit8 Dictionary<K,V>::remove(IN K &key)
  392. {
  393. V temp;
  394. return(remove(key,temp));
  395. }
  396. // Remove some random K/V pair that's in the Dictionary
  397. template <class K,class V>
  398. bit8 Dictionary<K,V>::removeAny(OUT K &key,OUT V &value)
  399. {
  400. int offset;
  401. DNode<K,V> *node,*last,*temp;
  402. float percent;
  403. if (entries==0)
  404. return(FALSE);
  405. percent=(entries-1);
  406. percent/=(float)getSize();
  407. int i;
  408. offset=-1;
  409. for (i=0; i<(int)getSize(); i++)
  410. if (table[i]!=NULL)
  411. {
  412. offset=i;
  413. break;
  414. }
  415. if (offset==-1) // Nothing there
  416. return(FALSE);
  417. node=table[offset];
  418. last=node;
  419. #ifdef KEY_MEM_OPS
  420. memcpy(&key,&(node->key),sizeof(K));
  421. #else
  422. key=node->key;
  423. #endif
  424. #ifdef VALUE_MEM_OPS
  425. memcpy(&value,&(node->value),sizeof(V));
  426. #else
  427. value=node->value;
  428. #endif
  429. temp=table[offset]->hashNext;
  430. delete(table[offset]);
  431. table[offset]=temp;
  432. entries--;
  433. if (percent <= SHRINK_THRESHOLD)
  434. shrink();
  435. return(TRUE);
  436. }
  437. template <class K,class V>
  438. bool Dictionary<K,V>::getValue(IN K &key,OUT V &value) RO
  439. {
  440. V *valptr=NULL;
  441. bool retval=getPointer(key,&valptr);
  442. if (retval && valptr)
  443. {
  444. #ifdef VALUE_MEM_OPS
  445. assert(0);
  446. #else
  447. value=*valptr;
  448. #endif
  449. }
  450. return(retval);
  451. }
  452. // Try and avoid this since you're getting a pointer to the internally
  453. // managed data!
  454. template <class K,class V>
  455. bool Dictionary<K,V>::getPointer(IN K &key,OUT V **valptr) RO
  456. {
  457. int offset;
  458. DNode<K,V> *node;
  459. if (entries==0)
  460. return(FALSE);
  461. offset=keyHash(key);
  462. node=table[offset];
  463. if (node==NULL)
  464. return(FALSE);
  465. #ifdef KEY_MEM_OPS
  466. while ((node!=NULL)&&(memcmp(&(node->key),&key,sizeof(K))))
  467. #else
  468. while ((node!=NULL)&&( ! ((node->key)==key)) ) // odd syntax so you don't
  469. #endif // have to do oper !=
  470. { node=node->hashNext; }
  471. if (node==NULL)
  472. { return(FALSE); }
  473. *valptr=&(node->value);
  474. return(TRUE);
  475. }
  476. //A note about Shrink and Expand: They are never necessary, they are
  477. //only here to improve performance of the hash table by reducing
  478. //the length of the linked list at each table entry.
  479. // Shrink the hash table by a factor of 2 (and relocate entries)
  480. template <class K,class V>
  481. void Dictionary<K,V>::shrink(void)
  482. {
  483. int i;
  484. int oldsize;
  485. uint32 offset;
  486. DNode<K,V> **oldtable,*temp,*first,*next;
  487. if ((size<=(uint32)MIN_TABLE_SIZE)||(keepSize==TRUE))
  488. return;
  489. //fprintf(stderr,"Shrinking....\n");
  490. oldtable=table;
  491. oldsize=size;
  492. size/=2;
  493. tableBits--;
  494. table=(DNode<K,V> **)new DNode<K,V>*[size];
  495. assert(table!=NULL);
  496. memset((void *)table,0,size*sizeof(void *));
  497. for (i=0; i<oldsize; i++)
  498. {
  499. temp=oldtable[i];
  500. while (temp!=NULL)
  501. {
  502. offset=keyHash(temp->key);
  503. first=table[offset];
  504. table[offset]=temp;
  505. next=temp->hashNext;
  506. temp->hashNext=first;
  507. temp=next;
  508. }
  509. }
  510. delete[](oldtable);
  511. }
  512. template <class K,class V>
  513. void Dictionary<K,V>::expand(void)
  514. {
  515. int i;
  516. int oldsize;
  517. uint32 offset;
  518. DNode<K,V> **oldtable,*temp,*first,*next;
  519. if (keepSize==TRUE)
  520. return;
  521. //fprintf(stderr,"Expanding...\n");
  522. oldtable=table;
  523. oldsize=size;
  524. size*=2;
  525. tableBits++;
  526. table=(DNode<K,V> **)new DNode<K,V>* [size];
  527. assert(table!=NULL);
  528. memset((void *)table,0,size*sizeof(void *));
  529. for (i=0; i<oldsize; i++)
  530. {
  531. temp=oldtable[i];
  532. while (temp!=NULL)
  533. {
  534. offset=keyHash(temp->key);
  535. first=table[offset];
  536. table[offset]=temp;
  537. next=temp->hashNext;
  538. temp->hashNext=first;
  539. temp=next;
  540. }
  541. }
  542. delete[](oldtable);
  543. }
  544. #endif