hash_map.inl - Engine C API Reference

hash_map.inl
  1. namespace stingray_plugin_foundation {
  2. template <class K, class D, class H, class E>
  3. HashMap<K,D,H,E>::HashMap(Allocator &a) : _data(), _used(0), _buckets(0),
  4. _spill_unused(0), _spill_freelist(END_OF_FREELIST), _allocator(a)
  5. {
  6. }
  7. template <class K, class D, class H, class E>
  8. HashMap<K,D,H,E>::HashMap(unsigned buckets, unsigned spill, Allocator &a) :
  9. _data(), _used(0), _buckets(buckets), _spill_unused(spill), _spill_freelist(END_OF_FREELIST)
  10. , _allocator(a)
  11. {
  12. allocate_data(buckets + spill);
  13. for (unsigned i=0; i<_data.size; ++i)
  14. _data.marker[i] = UNUSED;
  15. }
  16. template <class K, class D, class H, class E>
  17. HashMap<K,D,H,E>::HashMap(const HashMap<K,D,H,E> &o) :
  18. _hash(o._hash), _equal(o._equal),
  19. _data(), _used(o._used), _buckets(o._buckets),
  20. _spill_unused(o._spill_unused), _spill_freelist(o._spill_freelist),
  21. _allocator(o._allocator)
  22. {
  23. allocate_data(o._data.size);
  24. for (unsigned i = 0; i < _data.size; ++i) {
  25. if (o._data.marker[i] != UNUSED && (o._data.marker[i] & 0x80000000) == 0) {
  26. construct(_data.value[i]);
  27. _data.value[i] = o._data.value[i];
  28. }
  29. _data.marker[i] = o._data.marker[i];
  30. }
  31. }
  32. template <class K, class D, class H, class E>
  33. void HashMap<K,D,H,E>::operator=(const HashMap<K,D,H,E> &o)
  34. {
  35. if (this == &o)
  36. return;
  37. clear();
  38. _hash = o._hash;
  39. _equal = o._equal;
  40. _allocator.deallocate(_data.marker);
  41. _data.marker = nullptr;
  42. allocate_data(o._data.size);
  43. for (unsigned i = 0; i < _data.size; ++i) {
  44. if (o._data.marker[i] != UNUSED && (o._data.marker[i] & 0x80000000) == 0) {
  45. construct(_data.value[i]);
  46. _data.value[i] = o._data.value[i];
  47. }
  48. _data.marker[i] = o._data.marker[i];
  49. }
  50. _used = o._used;
  51. _buckets = o._buckets;
  52. _spill_unused = o._spill_unused;
  53. _spill_freelist = o._spill_freelist;
  54. }
  55. template <class K, class D, class H, class E>
  56. HashMap<K, D, H, E>::~HashMap()
  57. {
  58. clear();
  59. _allocator.deallocate(_data.marker);
  60. }
  61. template <class K, class D, class H, class E>
  62. void HashMap<K,D,H,E>::swap(HashMap<K,D,H,E> &o)
  63. {
  64. XENSURE(&_allocator == &o._allocator);
  65. std::swap(_hash, o._hash);
  66. std::swap(_equal, o._equal);
  67. std::swap(_data.size, o._data.size);
  68. std::swap(_data.marker, o._data.marker);
  69. std::swap(_data.value, o._data.value);
  70. std::swap(_used, o._used);
  71. std::swap(_buckets, o._buckets);
  72. std::swap(_spill_unused, o._spill_unused);
  73. std::swap(_spill_freelist, o._spill_freelist);
  74. }
  75. template <class K, class D, class H, class E>
  76. typename HashMap<K,D,H,E>::iterator HashMap<K,D,H,E>::begin()
  77. {
  78. return iterator(*this, 0);
  79. }
  80. template <class K, class D, class H, class E>
  81. typename HashMap<K,D,H,E>::iterator HashMap<K,D,H,E>::end()
  82. {
  83. return iterator(*this, num_buckets());
  84. }
  85. template <class K, class D, class H, class E>
  86. typename HashMap<K,D,H,E>::const_iterator HashMap<K,D,H,E>::begin() const
  87. {
  88. return const_iterator(*this, 0);
  89. }
  90. template <class K, class D, class H, class E>
  91. typename HashMap<K,D,H,E>::const_iterator HashMap<K,D,H,E>::end() const
  92. {
  93. return const_iterator(*this, num_buckets());
  94. }
  95. template <class K, class D, class H, class E> template <class KEY_EQ>
  96. typename HashMap<K,D,H,E>::const_iterator HashMap<K,D,H,E>::find(const KEY_EQ &k) const{
  97. auto i = find_or_fail(k);
  98. return (i == END_OF_LIST) ? end() : const_iterator(*this, i);
  99. }
  100. template <class K, class D, class H, class E> template <class KEY_EQ>
  101. typename HashMap<K,D,H,E>::iterator HashMap<K,D,H,E>::find(const KEY_EQ &k){
  102. auto i = find_or_fail(k);
  103. return (i == END_OF_LIST) ? end() : iterator(*this, i);
  104. }
  105. template <class K, class D, class H, class E>
  106. Allocator & HashMap<K,D,H,E>::allocator() const
  107. {
  108. return _allocator;
  109. }
  110. template <class K, class D, class H, class E>
  111. unsigned HashMap<K,D,H,E>::size() const
  112. {
  113. return _used;
  114. }
  115. template <class K, class D, class H, class E>
  116. bool HashMap<K,D,H,E>::empty() const
  117. {
  118. return _used == 0;
  119. }
  120. template <class K, class D, class H, class E> template <class KEY_EQ>
  121. bool HashMap<K,D,H,E>::has(const KEY_EQ &k) const
  122. {
  123. return find_or_fail(k) != END_OF_LIST;
  124. }
  125. template <class K, class D, class H, class E> template <class KEY_EQ>
  126. typename HashMap<K,D,H,E>::data_type &HashMap<K,D,H,E>::operator[](const KEY_EQ &k)
  127. {
  128. if (full()) {
  129. unsigned i = find_or_fail(k);
  130. if (i != END_OF_LIST)
  131. return _data.value[i].second;
  132. grow();
  133. }
  134. return _data.value[find_or_make(k)].second;
  135. }
  136. template <class K, class D, class H, class E> template <class KEY_EQ>
  137. typename HashMap<K, D, H, E>::value_type &HashMap<K, D, H, E>::get_pair(const KEY_EQ &k)
  138. {
  139. if (full()) {
  140. unsigned i = find_or_fail(k);
  141. if (i != END_OF_LIST)
  142. return _data.value[i];
  143. grow();
  144. }
  145. return _data.value[find_or_make(k)];
  146. }
  147. template <class K, class D, class H, class E> template <class KEY_EQ>
  148. const typename HashMap<K,D,H,E>::data_type &HashMap<K,D,H,E>::operator[](const KEY_EQ &k) const
  149. {
  150. unsigned i = find_or_fail(k);
  151. XASSERT(i != END_OF_LIST, "key not in map");
  152. return _data.value[i].second;
  153. }
  154. template <class K, class D, class H, class E> template <class KEY_EQ>
  155. typename HashMap<K,D,H,E>::data_type &HashMap<K,D,H,E>::get(const KEY_EQ &k, data_type &def)
  156. {
  157. unsigned i = find_or_fail(k);
  158. if (i == END_OF_LIST)
  159. return def;
  160. else
  161. return _data.value[i].second;
  162. }
  163. template <class K, class D, class H, class E> template <class KEY_EQ>
  164. const typename HashMap<K,D,H,E>::data_type &HashMap<K,D,H,E>::get(const KEY_EQ &k, const data_type &def) const
  165. {
  166. unsigned i = find_or_fail(k);
  167. if (i == END_OF_LIST)
  168. return def;
  169. else
  170. return _data.value[i].second;
  171. }
  172. template <class K, class D, class H, class E>
  173. void HashMap<K,D,H,E>::clear()
  174. {
  175. _used = 0;
  176. _spill_freelist = END_OF_FREELIST;
  177. _spill_unused = _data.size - _buckets;
  178. for (unsigned i=0; i<_data.size; ++i) {
  179. if (bucket_valid(i))
  180. destruct(_data.value[i]);
  181. _data.marker[i] = UNUSED;
  182. }
  183. }
  184. template <class K, class D, class H, class E> template <class KEY_EQ, class DATA_EQ>
  185. void HashMap<K,D,H,E>::insert(const KEY_EQ &k, const DATA_EQ &v)
  186. {
  187. (*this)[k] = v;
  188. }
  189. template <class K, class D, class H, class E> template <class KEY_EQ>
  190. void HashMap<K,D,H,E>::erase(const KEY_EQ &k)
  191. {
  192. find_and_erase(k);
  193. }
  194. template <class K, class D, class H, class E>
  195. void HashMap<K,D,H,E>::rehash(unsigned new_buckets)
  196. {
  197. XENSURE(new_buckets >= _used);
  198. clear_freelist();
  199. unsigned spill = int(new_buckets * 0.37f + 1);
  200. unsigned old_size = _data.size;
  201. if (_data.size == 0) {
  202. allocate_data(new_buckets + spill);
  203. _buckets = new_buckets;
  204. _spill_unused = spill;
  205. for (unsigned i = 0; i<_data.size; ++i)
  206. _data.marker[i] = UNUSED;
  207. return;
  208. }
  209. this_type new_hash(new_buckets, spill, _allocator);
  210. for (unsigned o=0; o<old_size; ++o) {
  211. if (_data.marker[o] == UNUSED) {
  212. continue;
  213. }
  214. // Find/allocate entry
  215. auto &k = _data.value[o].first;
  216. auto &d = _data.value[o].second;
  217. unsigned i = new_hash.hash(k);
  218. if (new_hash._data.marker[i] == UNUSED) {
  219. new_hash._data.marker[i] = END_OF_LIST;
  220. ++new_hash._used;
  221. }
  222. else {
  223. // Walk the hash chain until the end and add a new entry
  224. while (new_hash._data.marker[i] != END_OF_LIST)
  225. i = new_hash._data.marker[i];
  226. unsigned j = new_hash.allocate_spill();
  227. new_hash._data.marker[i] = j;
  228. i = j;
  229. new_hash._data.marker[i] = END_OF_LIST;
  230. }
  231. memmove(&new_hash._data.value[i].first, &k, sizeof(K));
  232. memmove(&new_hash._data.value[i].second, &d, sizeof(D));
  233. _data.marker[o] = UNUSED;
  234. }
  235. swap(new_hash);
  236. }
  237. template <class K, class D, class H, class E>
  238. void HashMap<K,D,H,E>::reserve(unsigned items)
  239. {
  240. unsigned buckets = int(items * 1.37f);
  241. if (buckets < 26)
  242. buckets = 26;
  243. rehash(buckets);
  244. }
  245. template <class K, class D, class H, class E>
  246. unsigned HashMap<K,D,H,E>::num_buckets() const
  247. {
  248. return _data.size;
  249. }
  250. template <class K, class D, class H, class E>
  251. bool HashMap<K,D,H,E>::bucket_valid(unsigned i) const
  252. {
  253. return (_data.marker[i] & 0x80000000) == 0;
  254. }
  255. template <class K, class D, class H, class E>
  256. typename HashMap<K,D,H,E>::value_type &HashMap<K,D,H,E>::bucket_value(unsigned i)
  257. {
  258. return _data.value[i];
  259. }
  260. template <class K, class D, class H, class E>
  261. const typename HashMap<K,D,H,E>::value_type &HashMap<K,D,H,E>::bucket_value(unsigned i) const
  262. {
  263. return _data.value[i];
  264. }
  265. // Searches for `k` in the map. Returns the index of the entry if found and
  266. // END_OF_LIST if not found.
  267. template <class K, class D, class H, class E> template <class KEY_EQ>
  268. unsigned HashMap<K,D,H,E>::find_or_fail(const KEY_EQ &k) const
  269. {
  270. // Is hash empty?
  271. if (empty())
  272. return END_OF_LIST;
  273. unsigned i = hash(k);
  274. // Is primary slot unused?
  275. if (_data.marker[i] == UNUSED)
  276. return END_OF_LIST;
  277. // Walk the hash chain until key matches or end is reached.
  278. while (true) {
  279. if (i == END_OF_LIST || _equal(k, _data.value[i].first))
  280. return i;
  281. i = _data.marker[i];
  282. }
  283. }
  284. // Searches for `k` in the map. Returns the index if it is found. If it is
  285. // not found a new hash slot is create for `k` and its index is returned.
  286. template <class K, class D, class H, class E> template <class KEY_EQ>
  287. unsigned HashMap<K,D,H,E>::find_or_make(const KEY_EQ &k)
  288. {
  289. unsigned i = hash(k);
  290. // Is primary slot unused?
  291. if (_data.marker[i] == UNUSED) {
  292. ++_used;
  293. construct(_data.value[i]);
  294. _data.value[i].first = k;
  295. _data.marker[i] = END_OF_LIST;
  296. return i;
  297. }
  298. while (true) {
  299. // Walk the hash chain until found
  300. if (_equal(k, _data.value[i].first) )
  301. return i;
  302. // Or until end of hash chain, in which case we add a new entry
  303. // to the end.
  304. if (_data.marker[i] == END_OF_LIST) {
  305. unsigned j = allocate_spill();
  306. _data.marker[i] = j;
  307. i = j;
  308. construct(_data.value[i]);
  309. _data.value[i].first = k;
  310. _data.marker[i] = END_OF_LIST;
  311. return i;
  312. }
  313. i = _data.marker[i];
  314. }
  315. }
  316. // Searches for `k` in the map. If it is found, it is erased.
  317. template <class K, class D, class H, class E> template <class KEY_EQ>
  318. void HashMap<K,D,H,E>::find_and_erase(const KEY_EQ &k)
  319. {
  320. // early out to avoid division by zero in hash method if the map is empty.
  321. if (_buckets == 0)
  322. return;
  323. unsigned i = hash(k);
  324. // Is primary slot unused?
  325. if (_data.marker[i] == UNUSED)
  326. return;
  327. // Did the primary slot match?
  328. if (_equal(k, _data.value[i].first)) {
  329. // If there is no chain, just return the primary value
  330. if (_data.marker[i] == END_OF_LIST) {
  331. _data.marker[i] = UNUSED;
  332. destruct(_data.value[i]);
  333. --_used;
  334. // If there is a chain, bring in the first value from the
  335. // chain and mark it as free in the spillover region.
  336. } else {
  337. unsigned del = _data.marker[i];
  338. _data.marker[i] = _data.marker[del];
  339. _data.value[i] = _data.value[del];
  340. destruct(_data.value[del]);
  341. free_spill(del);
  342. }
  343. return;
  344. }
  345. // If not, search the hash chain for a match
  346. unsigned prev = i;
  347. while (true) {
  348. // If there is a match remove that value from the chain and
  349. // mark it as free in the spillover region.
  350. if (_equal(k, _data.value[i].first) ) {
  351. _data.marker[prev] = _data.marker[i];
  352. destruct(_data.value[i]);
  353. free_spill(i);
  354. return;
  355. }
  356. // If end of chain was reached without finding the key just return
  357. if (_data.marker[i] == END_OF_LIST)
  358. return;
  359. prev = i;
  360. i = _data.marker[i];
  361. }
  362. }
  363. template <class K, class D, class H, class E>
  364. bool HashMap<K,D,H,E>::full() const
  365. {
  366. // Hash map is full when spillover region is full
  367. return _spill_unused == 0 && _spill_freelist == END_OF_FREELIST;
  368. }
  369. template <class K, class D, class H, class E>
  370. void HashMap<K,D,H,E>::grow()
  371. {
  372. do {
  373. unsigned new_buckets = _buckets * 2 + 1;
  374. if (new_buckets < 19)
  375. new_buckets = 19;
  376. rehash(new_buckets);
  377. } while (full());
  378. }
  379. template <class K, class D, class H, class E> template <class KEY_EQ>
  380. unsigned HashMap<K,D,H,E>::hash(const KEY_EQ &k) const
  381. {
  382. XENSURE(_buckets > 0);
  383. return _hash(k) % _buckets;
  384. }
  385. // Allocate a slot in the spillover region and return it.
  386. template <class K, class D, class H, class E>
  387. unsigned HashMap<K,D,H,E>::allocate_spill()
  388. {
  389. ++_used;
  390. // If there spillover free list is non-empty, use its
  391. // first item.
  392. if (_spill_freelist != END_OF_FREELIST) {
  393. unsigned i = _spill_freelist & 0x7fffffff;
  394. _spill_freelist = _data.marker[i];
  395. return i;
  396. }
  397. // Return the first unused item from the spillover region.
  398. XENSURE(_spill_unused > 0);
  399. unsigned i = _data.size - _spill_unused;
  400. --_spill_unused;
  401. _data.marker[i] = UNUSED;
  402. return i;
  403. }
  404. template <class K, class D, class H, class E>
  405. void HashMap<K,D,H,E>::free_spill(unsigned i)
  406. {
  407. // Free a spillover item by inserting it in the freelist.
  408. --_used;
  409. _data.marker[i] = _spill_freelist;
  410. _spill_freelist = i | 0x80000000;
  411. }
  412. // Clears the spillover freelist (marks all items as unused).
  413. template <class K, class D, class H, class E>
  414. void HashMap<K,D,H,E>::clear_freelist()
  415. {
  416. while (_spill_freelist != END_OF_FREELIST) {
  417. unsigned i = _spill_freelist & 0x7fffffff;
  418. _spill_freelist = _data.marker[i];
  419. _data.marker[i] = UNUSED;
  420. }
  421. }
  422. template <class K, class D, class H, class E>
  423. void HashMap<K, D, H, E>::allocate_data(unsigned count)
  424. {
  425. XASSERT(_data.marker == nullptr, "Data must be deallocated/cleared before allocating new data");
  426. if (count == 0) {
  427. _data = Data();
  428. return;
  429. }
  430. auto marker_size = (((sizeof(unsigned) * count) + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*);
  431. auto value_size = sizeof(value_type) * count;
  432. auto data_size = marker_size + value_size;
  433. _data.marker = (unsigned*)_allocator.allocate(data_size);
  434. _data.value = (value_type*)&(((char*)_data.marker)[marker_size]);
  435. _data.size = count;
  436. }
  437. template<class K, class D, class H, class E>
  438. template <class STREAM> void HashMap<K,D,H,E>::serialize(STREAM &stream)
  439. {
  440. unsigned n = size();
  441. stream & n;
  442. if (stream.is_output()) {
  443. iterator it(this->begin());
  444. iterator end(this->end());
  445. for (; it != end; ++it)
  446. stream & (*it);
  447. } else {
  448. reserve(n);
  449. for (unsigned i=0; i<n; ++i) {
  450. value_type v(_allocator);
  451. stream & v;
  452. insert(v.first, v.second);
  453. }
  454. }
  455. }
  456. } // namespace stingray_plugin_foundation