simpleHashMap.I 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. // Filename: simpleHashMap.I
  2. // Created by: drose (19Jul07)
  3. //
  4. ////////////////////////////////////////////////////////////////////
  5. //
  6. // PANDA 3D SOFTWARE
  7. // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
  8. //
  9. // All use of this software is subject to the terms of the Panda 3d
  10. // Software license. You should have received a copy of this license
  11. // along with this source code; you will also find a current copy of
  12. // the license at http://etc.cmu.edu/panda3d/docs/license/ .
  13. //
  14. // To contact the maintainers of this program write to
  15. // [email protected] .
  16. //
  17. ////////////////////////////////////////////////////////////////////
  18. ////////////////////////////////////////////////////////////////////
  19. // Function: SimpleHashMap::Constructor
  20. // Access: Public
  21. // Description:
  22. ////////////////////////////////////////////////////////////////////
  23. template<class Key, class Value, class Compare>
  24. INLINE SimpleHashMap<Key, Value, Compare>::
  25. SimpleHashMap(const Compare &comp) :
  26. _table(NULL),
  27. _deleted_chain(NULL),
  28. _table_size(0),
  29. _num_entries(0),
  30. _comp(comp)
  31. {
  32. }
  33. ////////////////////////////////////////////////////////////////////
  34. // Function: SimpleHashMap::Destructor
  35. // Access: Public
  36. // Description:
  37. ////////////////////////////////////////////////////////////////////
  38. template<class Key, class Value, class Compare>
  39. INLINE SimpleHashMap<Key, Value, Compare>::
  40. ~SimpleHashMap() {
  41. clear();
  42. }
  43. ////////////////////////////////////////////////////////////////////
  44. // Function: SimpleHashMap::swap
  45. // Access: Public
  46. // Description: Quickly exchanges the contents of this map and the
  47. // other map.
  48. ////////////////////////////////////////////////////////////////////
  49. template<class Key, class Value, class Compare>
  50. INLINE void SimpleHashMap<Key, Value, Compare>::
  51. swap(SimpleHashMap<Key, Value, Compare> &other) {
  52. TableEntry *t0 = _table;
  53. _table = other._table;
  54. other._table = t0;
  55. DeletedBufferChain *t1 = _deleted_chain;
  56. _deleted_chain = other._deleted_chain;
  57. other._deleted_chain = t1;
  58. size_t t2 = _table_size;
  59. _table_size = other._table_size;
  60. other._table_size = t2;
  61. size_t t3 = _num_entries;
  62. _num_entries = other._num_entries;
  63. other._num_entries = t3;
  64. }
  65. ////////////////////////////////////////////////////////////////////
  66. // Function: SimpleHashMap::find
  67. // Access: Public
  68. // Description: Searches for the indicated key in the table. Returns
  69. // its index number if it is found, or -1 if it is not
  70. // present in the table.
  71. ////////////////////////////////////////////////////////////////////
  72. template<class Key, class Value, class Compare>
  73. int SimpleHashMap<Key, Value, Compare>::
  74. find(const Key &key) const {
  75. if (_table_size == 0) {
  76. // Special case: the table is empty.
  77. return -1;
  78. }
  79. size_t index = get_hash(key);
  80. if (!has_element(index)) {
  81. return -1;
  82. }
  83. if (is_element(index, key)) {
  84. return index;
  85. }
  86. // There was some other key at the hashed slot. That's a hash
  87. // conflict. Maybe our entry was recorded at a later slot position;
  88. // scan the subsequent positions until we find the entry or an
  89. // unused slot, indicating the end of the scan.
  90. size_t i = index;
  91. i = (i + 1) & (_table_size - 1);
  92. while (i != index && has_element(i)) {
  93. if (is_element(i, key)) {
  94. return i;
  95. }
  96. i = (i + 1) & (_table_size - 1);
  97. }
  98. // The key is not in the table.
  99. return -1;
  100. }
  101. ////////////////////////////////////////////////////////////////////
  102. // Function: SimpleHashMap::store
  103. // Access: Public
  104. // Description: Records the indicated key/data pair in the map. If
  105. // the key was already present, silently replaces it.
  106. // Returns a reference to the value in the map.
  107. ////////////////////////////////////////////////////////////////////
  108. template<class Key, class Value, class Compare>
  109. Value &SimpleHashMap<Key, Value, Compare>::
  110. store(const Key &key, const Value &data) {
  111. if (_table_size == 0) {
  112. // Special case: the first key in an empty table.
  113. nassertr(_num_entries == 0, _table[0]._data);
  114. new_table();
  115. size_t index = get_hash(key);
  116. store_new_element(index, key, data);
  117. ++_num_entries;
  118. return _table[index]._data;
  119. }
  120. size_t index = get_hash(key);
  121. if (!has_element(index)) {
  122. if (consider_expand_table()) {
  123. return store(key, data);
  124. }
  125. store_new_element(index, key, data);
  126. ++_num_entries;
  127. return _table[index]._data;
  128. }
  129. if (is_element(index, key)) {
  130. _table[index]._data = data;
  131. return _table[index]._data;
  132. }
  133. // There was some other key at the hashed slot. That's a hash
  134. // conflict. Record this entry at a later position.
  135. size_t i = index;
  136. i = (i + 1) & (_table_size - 1);
  137. while (i != index) {
  138. if (!has_element(i)) {
  139. if (consider_expand_table()) {
  140. return store(key, data);
  141. }
  142. store_new_element(i, key, data);
  143. ++_num_entries;
  144. return _table[i]._data;
  145. }
  146. if (is_element(i, key)) {
  147. _table[i]._data = data;
  148. return _table[i]._data;
  149. }
  150. i = (i + 1) & (_table_size - 1);
  151. }
  152. // Shouldn't get here unless _num_entries == _table_size, which
  153. // shouldn't be possible due to consider_expand_table().
  154. nassertr(false, _table[0]._data);
  155. return _table[0]._data; // To satisfy compiler
  156. }
  157. ////////////////////////////////////////////////////////////////////
  158. // Function: SimpleHashMap::remove
  159. // Access: Public
  160. // Description: Removes the indicated key and its associated data
  161. // from the table. Returns true if the key was removed,
  162. // false if it was not present.
  163. ////////////////////////////////////////////////////////////////////
  164. template<class Key, class Value, class Compare>
  165. INLINE bool SimpleHashMap<Key, Value, Compare>::
  166. remove(const Key &key) {
  167. int index = find(key);
  168. if (index == -1) {
  169. return false;
  170. }
  171. remove_element(index);
  172. return true;
  173. }
  174. ////////////////////////////////////////////////////////////////////
  175. // Function: SimpleHashMap::clear
  176. // Access: Public
  177. // Description: Completely empties the table.
  178. ////////////////////////////////////////////////////////////////////
  179. template<class Key, class Value, class Compare>
  180. void SimpleHashMap<Key, Value, Compare>::
  181. clear() {
  182. if (_table_size != 0) {
  183. for (size_t i = 0; i < _table_size; ++i) {
  184. if (has_element(i)) {
  185. clear_element(i);
  186. }
  187. }
  188. _deleted_chain->deallocate(_table, TypeHandle::none());
  189. _table = NULL;
  190. _deleted_chain = NULL;
  191. _table_size = 0;
  192. _num_entries = 0;
  193. }
  194. }
  195. ////////////////////////////////////////////////////////////////////
  196. // Function: SimpleHashMap::operator []
  197. // Access: Public
  198. // Description: Returns a modifiable reference to the data associated
  199. // with the indicated key, or creates a new data entry
  200. // and returns its reference.
  201. ////////////////////////////////////////////////////////////////////
  202. template<class Key, class Value, class Compare>
  203. INLINE Value &SimpleHashMap<Key, Value, Compare>::
  204. operator [] (const Key &key) {
  205. int index = find(key);
  206. if (index != -1) {
  207. return modify_data(index);
  208. }
  209. return store(key, Value());
  210. }
  211. ////////////////////////////////////////////////////////////////////
  212. // Function: SimpleHashMap::get_size
  213. // Access: Public
  214. // Description: Returns the total number of slots in the table.
  215. ////////////////////////////////////////////////////////////////////
  216. template<class Key, class Value, class Compare>
  217. INLINE int SimpleHashMap<Key, Value, Compare>::
  218. get_size() const {
  219. return _table_size;
  220. }
  221. ////////////////////////////////////////////////////////////////////
  222. // Function: SimpleHashMap::has_element
  223. // Access: Public
  224. // Description: Returns true if there is an element stored in the nth
  225. // slot, false otherwise.
  226. //
  227. // n should be in the range 0 <= n < get_size().
  228. ////////////////////////////////////////////////////////////////////
  229. template<class Key, class Value, class Compare>
  230. INLINE bool SimpleHashMap<Key, Value, Compare>::
  231. has_element(int n) const {
  232. nassertr(n >= 0 && n < (int)_table_size, false);
  233. return (get_exists_array()[n] != 0);
  234. }
  235. ////////////////////////////////////////////////////////////////////
  236. // Function: SimpleHashMap::get_key
  237. // Access: Public
  238. // Description: Returns the key in the nth slot of the table.
  239. //
  240. // It is an error to call this if there is nothing
  241. // stored in the nth slot (use has_element() to check
  242. // this first). n should be in the range 0 <= n <
  243. // get_size().
  244. ////////////////////////////////////////////////////////////////////
  245. template<class Key, class Value, class Compare>
  246. INLINE const Key &SimpleHashMap<Key, Value, Compare>::
  247. get_key(int n) const {
  248. nassertr(has_element(n), _table[n]._key);
  249. return _table[n]._key;
  250. }
  251. ////////////////////////////////////////////////////////////////////
  252. // Function: SimpleHashMap::get_data
  253. // Access: Public
  254. // Description: Returns the data in the nth slot of the table.
  255. //
  256. // It is an error to call this if there is nothing
  257. // stored in the nth slot (use has_element() to check
  258. // this first). n should be in the range 0 <= n <
  259. // get_size().
  260. ////////////////////////////////////////////////////////////////////
  261. template<class Key, class Value, class Compare>
  262. INLINE const Value &SimpleHashMap<Key, Value, Compare>::
  263. get_data(int n) const {
  264. nassertr(has_element(n), _table[n]._data);
  265. return _table[n]._data;
  266. }
  267. ////////////////////////////////////////////////////////////////////
  268. // Function: SimpleHashMap::modify_data
  269. // Access: Public
  270. // Description: Returns a modifiable reference to the data in the nth
  271. // slot of the table.
  272. //
  273. // It is an error to call this if there is nothing
  274. // stored in the nth slot (use has_element() to check
  275. // this first). n should be in the range 0 <= n <
  276. // get_size().
  277. ////////////////////////////////////////////////////////////////////
  278. template<class Key, class Value, class Compare>
  279. INLINE Value &SimpleHashMap<Key, Value, Compare>::
  280. modify_data(int n) {
  281. nassertr(has_element(n), _table[n]._data);
  282. return _table[n]._data;
  283. }
  284. ////////////////////////////////////////////////////////////////////
  285. // Function: SimpleHashMap::set_data
  286. // Access: Public
  287. // Description: Changes the data for the nth slot of the table.
  288. //
  289. // It is an error to call this if there is nothing
  290. // stored in the nth slot (use has_element() to check
  291. // this first). n should be in the range 0 <= n <
  292. // get_size().
  293. ////////////////////////////////////////////////////////////////////
  294. template<class Key, class Value, class Compare>
  295. INLINE void SimpleHashMap<Key, Value, Compare>::
  296. set_data(int n, const Value &data) {
  297. nassertv(has_element(n));
  298. _table[n]._data = data;
  299. }
  300. ////////////////////////////////////////////////////////////////////
  301. // Function: SimpleHashMap::remove_element
  302. // Access: Public
  303. // Description: Removes the nth slot from the table.
  304. //
  305. // It is an error to call this if there is nothing
  306. // stored in the nth slot (use has_element() to check
  307. // this first). n should be in the range 0 <= n <
  308. // get_size().
  309. ////////////////////////////////////////////////////////////////////
  310. template<class Key, class Value, class Compare>
  311. void SimpleHashMap<Key, Value, Compare>::
  312. remove_element(int n) {
  313. nassertv(has_element(n));
  314. clear_element(n);
  315. nassertv(_num_entries > 0);
  316. --_num_entries;
  317. // Now we have put a hole in the table. If there was a hash
  318. // conflict in the slot following this one, we have to move it down
  319. // to close the hole.
  320. size_t i = n;
  321. i = (i + 1) & (_table_size - 1);
  322. while (has_element(i)) {
  323. size_t wants_index = get_hash(_table[i]._key);
  324. if (wants_index != i) {
  325. // This one was a hash conflict; try to put it where it belongs.
  326. // We can't just put it in n, since maybe it belongs somewhere
  327. // after n.
  328. while (wants_index != i && has_element(wants_index)) {
  329. wants_index = (wants_index + 1) & (_table_size - 1);
  330. }
  331. if (wants_index != i) {
  332. store_new_element(wants_index, _table[i]._key, _table[i]._data);
  333. clear_element(i);
  334. }
  335. }
  336. // Continue until we encounter the next unused slot. Until we do,
  337. // we can't be sure we've found all of the potential hash
  338. // conflicts.
  339. i = (i + 1) & (_table_size - 1);
  340. }
  341. }
  342. ////////////////////////////////////////////////////////////////////
  343. // Function: SimpleHashMap::get_num_entries
  344. // Access: Public
  345. // Description: Returns the number of active entries in the table.
  346. // This is not necessarily related to the number of
  347. // slots in the table as reported by get_size(). Use
  348. // get_size() to iterate through all of the slots, not
  349. // get_num_entries().
  350. ////////////////////////////////////////////////////////////////////
  351. template<class Key, class Value, class Compare>
  352. INLINE int SimpleHashMap<Key, Value, Compare>::
  353. get_num_entries() const {
  354. return _num_entries;
  355. }
  356. ////////////////////////////////////////////////////////////////////
  357. // Function: SimpleHashMap::is_empty
  358. // Access: Public
  359. // Description: Returns true if the table is empty;
  360. // i.e. get_num_entries() == 0.
  361. ////////////////////////////////////////////////////////////////////
  362. template<class Key, class Value, class Compare>
  363. INLINE bool SimpleHashMap<Key, Value, Compare>::
  364. is_empty() const {
  365. return (_num_entries == 0);
  366. }
  367. ////////////////////////////////////////////////////////////////////
  368. // Function: SimpleHashMap::output
  369. // Access: Public
  370. // Description:
  371. ////////////////////////////////////////////////////////////////////
  372. template<class Key, class Value, class Compare>
  373. void SimpleHashMap<Key, Value, Compare>::
  374. output(ostream &out) const {
  375. out << "SimpleHashMap (" << _num_entries << " entries): [";
  376. for (size_t i = 0; i < _table_size; ++i) {
  377. if (!has_element(i)) {
  378. out << " *";
  379. } else {
  380. out << " " << _table[i]._key;
  381. size_t index = get_hash(_table[i]._key);
  382. if (index != i) {
  383. // This was misplaced as the result of a hash conflict.
  384. // Report how far off it is.
  385. out << "(" << ((_table_size + i - index) & (_table_size - 1)) << ")";
  386. }
  387. }
  388. }
  389. out << " ]";
  390. }
  391. ////////////////////////////////////////////////////////////////////
  392. // Function: SimpleHashMap::write
  393. // Access: Public
  394. // Description:
  395. ////////////////////////////////////////////////////////////////////
  396. template<class Key, class Value, class Compare>
  397. void SimpleHashMap<Key, Value, Compare>::
  398. write(ostream &out) const {
  399. output(out);
  400. out << "\n";
  401. }
  402. ////////////////////////////////////////////////////////////////////
  403. // Function: SimpleHashMap::validate
  404. // Access: Public
  405. // Description: Returns true if the internal table appears to be
  406. // consistent, false if there are some internal errors.
  407. ////////////////////////////////////////////////////////////////////
  408. template<class Key, class Value, class Compare>
  409. bool SimpleHashMap<Key, Value, Compare>::
  410. validate() const {
  411. size_t count = 0;
  412. for (size_t i = 0; i < _table_size; ++i) {
  413. if (has_element(i)) {
  414. ++count;
  415. size_t ideal_index = get_hash(_table[i]._key);
  416. size_t wants_index = ideal_index;
  417. while (wants_index != i && has_element(wants_index)) {
  418. wants_index = (wants_index + 1) & (_table_size - 1);
  419. }
  420. if (wants_index != i) {
  421. util_cat.error()
  422. << "SimpleHashMap is invalid: key " << _table[i]._key
  423. << " should be in slot " << wants_index << " instead of "
  424. << i << " (ideal is " << ideal_index << ")\n";
  425. write(util_cat.error(false));
  426. return false;
  427. }
  428. }
  429. }
  430. if (count != _num_entries) {
  431. util_cat.error()
  432. << "SimpleHashMap is invalid: reports " << _num_entries
  433. << " entries, actually has " << count << "\n";
  434. write(util_cat.error(false));
  435. return false;
  436. }
  437. return true;
  438. }
  439. ////////////////////////////////////////////////////////////////////
  440. // Function: SimpleHashMap::get_hash
  441. // Access: Private
  442. // Description: Computes an appropriate index number to store the
  443. // given pointer.
  444. ////////////////////////////////////////////////////////////////////
  445. template<class Key, class Value, class Compare>
  446. INLINE size_t SimpleHashMap<Key, Value, Compare>::
  447. get_hash(const Key &key) const {
  448. /*
  449. // We want a hash constant 0 < k < 1. This one is suggested by
  450. // Knuth:
  451. static const double hash_constant = (sqrt(5.0) - 1.0) / 2.0;
  452. double f = ((double)_comp(key) * hash_constant);
  453. f -= floor(f);
  454. return (size_t)floor(f * _table_size);
  455. */
  456. return ((_comp(key) * (size_t)9973) >> 8) & (_table_size - 1);
  457. }
  458. ////////////////////////////////////////////////////////////////////
  459. // Function: SimpleHashMap::is_element
  460. // Access: Private
  461. // Description: Returns true if element n matches key.
  462. ////////////////////////////////////////////////////////////////////
  463. template<class Key, class Value, class Compare>
  464. INLINE bool SimpleHashMap<Key, Value, Compare>::
  465. is_element(int n, const Key &key) const {
  466. nassertr(has_element(n), false);
  467. return _comp.is_equal(_table[n]._key, key);
  468. }
  469. ////////////////////////////////////////////////////////////////////
  470. // Function: SimpleHashMap::store_new_element
  471. // Access: Private
  472. // Description: Constructs a new TableEntry at position n, storing
  473. // the indicated key and value.
  474. ////////////////////////////////////////////////////////////////////
  475. template<class Key, class Value, class Compare>
  476. INLINE void SimpleHashMap<Key, Value, Compare>::
  477. store_new_element(int n, const Key &key, const Value &data) {
  478. new(&_table[n]) TableEntry(key, data);
  479. get_exists_array()[n] = true;
  480. }
  481. ////////////////////////////////////////////////////////////////////
  482. // Function: SimpleHashMap::clear_element
  483. // Access: Private
  484. // Description: Destructs the TableEntry at position n.
  485. ////////////////////////////////////////////////////////////////////
  486. template<class Key, class Value, class Compare>
  487. INLINE void SimpleHashMap<Key, Value, Compare>::
  488. clear_element(int n) {
  489. _table[n].~TableEntry();
  490. get_exists_array()[n] = false;
  491. }
  492. ////////////////////////////////////////////////////////////////////
  493. // Function: SimpleHashMap::get_exists_array
  494. // Access: Private
  495. // Description: Returns the beginning of the array of _table_size
  496. // unsigned chars that are the boolean flags for whether
  497. // each element exists (has been constructed) within the
  498. // table.
  499. ////////////////////////////////////////////////////////////////////
  500. template<class Key, class Value, class Compare>
  501. INLINE unsigned char *SimpleHashMap<Key, Value, Compare>::
  502. get_exists_array() const {
  503. return (unsigned char *)(_table + _table_size);
  504. }
  505. ////////////////////////////////////////////////////////////////////
  506. // Function: SimpleHashMap::new_table
  507. // Access: Private
  508. // Description: Allocates a brand new table.
  509. ////////////////////////////////////////////////////////////////////
  510. template<class Key, class Value, class Compare>
  511. void SimpleHashMap<Key, Value, Compare>::
  512. new_table() {
  513. nassertv(_table_size == 0 && _num_entries == 0);
  514. // Pick a good initial table size. For now, we make it as small as
  515. // possible. Maybe that's the right answer.
  516. _table_size = 2;
  517. // We allocate enough bytes for _table_size elements of TableEntry,
  518. // plus _table_size more bytes at the end (for the exists array).
  519. size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size;
  520. _deleted_chain = memory_hook->get_deleted_chain(alloc_size);
  521. _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
  522. memset(get_exists_array(), 0, _table_size);
  523. }
  524. ////////////////////////////////////////////////////////////////////
  525. // Function: SimpleHashMap::consider_expand_table
  526. // Access: Private
  527. // Description: Expands the table if it will need it (assuming one
  528. // more element is about to be added). Returns true if
  529. // expanded, false otherwise.
  530. ////////////////////////////////////////////////////////////////////
  531. template<class Key, class Value, class Compare>
  532. INLINE bool SimpleHashMap<Key, Value, Compare>::
  533. consider_expand_table() {
  534. if (_num_entries >= (_table_size >> 1)) {
  535. expand_table();
  536. return true;
  537. }
  538. return false;
  539. }
  540. ////////////////////////////////////////////////////////////////////
  541. // Function: SimpleHashMap::expand_table
  542. // Access: Private
  543. // Description: Doubles the size of the existing table.
  544. ////////////////////////////////////////////////////////////////////
  545. template<class Key, class Value, class Compare>
  546. void SimpleHashMap<Key, Value, Compare>::
  547. expand_table() {
  548. nassertv(_table_size != 0);
  549. SimpleHashMap<Key, Value, Compare> old_map(_comp);
  550. swap(old_map);
  551. _table_size = (old_map._table_size << 1);
  552. nassertv(_table == NULL);
  553. // We allocate enough bytes for _table_size elements of TableEntry,
  554. // plus _table_size more bytes at the end (for the exists array).
  555. size_t alloc_size = _table_size * sizeof(TableEntry) + _table_size;
  556. _deleted_chain = memory_hook->get_deleted_chain(alloc_size);
  557. _table = (TableEntry *)_deleted_chain->allocate(alloc_size, TypeHandle::none());
  558. memset(get_exists_array(), 0, _table_size);
  559. // Now copy the entries from the old table into the new table.
  560. for (size_t i = 0; i < old_map._table_size; ++i) {
  561. if (old_map.has_element(i)) {
  562. store(old_map._table[i]._key, old_map._table[i]._data);
  563. }
  564. }
  565. nassertv(old_map._num_entries == _num_entries);
  566. }