hash_set.inl - Engine C API Reference

hash_set.inl
  1. #include "assert.h"
  2. namespace stingray_plugin_foundation {
  3. template<class K, class H, class E>
  4. HashSet<K,H,E>::HashSet(Allocator &a) : _data(), _used(0), _buckets(0),
  5. _spill_unused(0), _spill_freelist(END_OF_FREELIST), _allocator(a)
  6. {}
  7. template<class K, class H, class E>
  8. HashSet<K,H,E>::HashSet(unsigned buckets, unsigned spill, Allocator &a) : _data(),
  9. _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 H, class E>
  17. HashSet<K,H,E>::HashSet(const HashSet<K,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.key[i], IS_ALLOCATOR_AWARE_TYPE(K)());
  27. _data.key[i] = o._data.key[i];
  28. }
  29. _data.marker[i] = o._data.marker[i];
  30. }
  31. }
  32. template<class K, class H, class E>
  33. void HashSet<K,H,E>::operator=(const HashSet<K,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.key[i], IS_ALLOCATOR_AWARE_TYPE(K)());
  46. _data.key[i] = o._data.key[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 H, class E>
  56. HashSet<K, H, E>::~HashSet()
  57. {
  58. clear();
  59. _allocator.deallocate(_data.marker);
  60. }
  61. template <class K, class H, class E>
  62. void HashSet<K,H,E>::reserve(unsigned items)
  63. {
  64. unsigned buckets = int(items * 1.37f);
  65. if (buckets < int(19*1.37f))
  66. buckets = int(19*1.37f);
  67. rehash(buckets);
  68. }
  69. template<class K, class H, class E>
  70. void HashSet<K,H,E>::swap(HashSet<K,H,E> &o)
  71. {
  72. XENSURE(&_allocator == &o._allocator);
  73. std::swap(_hash, o._hash);
  74. std::swap(_equal, o._equal);
  75. std::swap(_data.size, o._data.size);
  76. std::swap(_data.marker, o._data.marker);
  77. std::swap(_data.key, o._data.key);
  78. std::swap(_used, o._used);
  79. std::swap(_buckets, o._buckets);
  80. std::swap(_spill_unused, o._spill_unused);
  81. std::swap(_spill_freelist, o._spill_freelist);
  82. }
  83. template<class K, class H, class E>
  84. Allocator & HashSet<K,H,E>::allocator() const
  85. {
  86. return _allocator;
  87. }
  88. template<class K, class H, class E>
  89. unsigned HashSet<K,H,E>::size() const
  90. {
  91. return _used;
  92. }
  93. template<class K, class H, class E>
  94. bool HashSet<K,H,E>::empty() const
  95. {
  96. return _used == 0;
  97. }
  98. template<class K, class H, class E> template <class KEY_EQ>
  99. unsigned HashSet<K,H,E>::find_or_fail(const KEY_EQ &k) const
  100. {
  101. if (empty())
  102. return END_OF_LIST;
  103. unsigned i = hash(k);
  104. if (_data.marker[i] == UNUSED)
  105. return END_OF_LIST;
  106. while (true) {
  107. if (i == END_OF_LIST || _equal(k, _data.key[i]))
  108. return i;
  109. i = _data.marker[i];
  110. }
  111. }
  112. template<class K, class H, class E> template <class KEY_EQ>
  113. unsigned HashSet<K,H,E>::find_or_make(const KEY_EQ &k)
  114. {
  115. unsigned i = hash(k);
  116. if (_data.marker[i] == UNUSED) {
  117. ++_used;
  118. construct(_data.key[i], IS_ALLOCATOR_AWARE_TYPE(K)());
  119. _data.key[i] = k;
  120. _data.marker[i] = END_OF_LIST;
  121. return i;
  122. }
  123. while (true) {
  124. if (_equal(k, _data.key[i]) )
  125. return i;
  126. if (_data.marker[i] == END_OF_LIST) {
  127. unsigned j = allocate_spill();
  128. _data.marker[i] = j;
  129. i = j;
  130. construct(_data.key[i], IS_ALLOCATOR_AWARE_TYPE(K)());
  131. _data.key[i] = k;
  132. _data.marker[i] = END_OF_LIST;
  133. return j;
  134. } else
  135. i = _data.marker[i];
  136. }
  137. }
  138. template<class K, class H, class E> template <class KEY_EQ>
  139. void HashSet<K,H,E>::find_and_erase(const KEY_EQ &k)
  140. {
  141. unsigned i = hash(k);
  142. if (_data.marker[i] == UNUSED)
  143. return;
  144. if (_equal(k, _data.key[i])) {
  145. if (_data.marker[i] == END_OF_LIST) {
  146. _data.marker[i] = UNUSED;
  147. destruct(_data.key[i]);
  148. --_used;
  149. } else {
  150. unsigned del = _data.marker[i];
  151. _data.marker[i] = _data.marker[del];
  152. _data.key[i] = _data.key[del];
  153. destruct(_data.key[del]);
  154. free_spill(del);
  155. }
  156. return;
  157. }
  158. unsigned prev = i;
  159. while (true) {
  160. if (_equal(k, _data.key[i]) ) {
  161. _data.marker[prev] = _data.marker[i];
  162. destruct(_data.key[i]);
  163. free_spill(i);
  164. return;
  165. }
  166. if (_data.marker[i] == END_OF_LIST)
  167. return;
  168. prev = i;
  169. i = _data.marker[i];
  170. }
  171. }
  172. template<class K, class H, class E> template <class KEY_EQ>
  173. bool HashSet<K,H,E>::has(const KEY_EQ &k) const
  174. {
  175. return find_or_fail(k) != END_OF_LIST;
  176. }
  177. template<class K, class H, class E>
  178. void HashSet<K,H,E>::clear()
  179. {
  180. _used = 0;
  181. _spill_freelist = END_OF_FREELIST;
  182. _spill_unused = _data.size - _buckets;
  183. for (unsigned i = 0; i<_data.size; ++i) {
  184. if (bucket_valid(i))
  185. destruct(_data.key[i]);
  186. _data.marker[i] = UNUSED;
  187. }
  188. }
  189. template<class K, class H, class E> template <class KEY_EQ>
  190. void HashSet<K,H,E>::insert(const KEY_EQ &k)
  191. {
  192. if (full()) {
  193. unsigned i = find_or_fail(k);
  194. if (i != END_OF_LIST)
  195. return;
  196. grow();
  197. }
  198. find_or_make(k);
  199. }
  200. template<class K, class H, class E> template <class KEY_EQ>
  201. void HashSet<K,H,E>::erase(const KEY_EQ &k)
  202. {
  203. find_and_erase(k);
  204. }
  205. template<class K, class H, class E>
  206. void HashSet<K,H,E>::rehash(unsigned new_buckets)
  207. {
  208. XENSURE(new_buckets >= _used);
  209. clear_freelist();
  210. unsigned spill = int(new_buckets * 0.37f + 1);
  211. unsigned old_size = _data.size;
  212. if (_data.size == 0) {
  213. allocate_data(new_buckets + spill);
  214. _buckets = new_buckets;
  215. _spill_unused = spill;
  216. for (unsigned i = 0; i<_data.size; ++i)
  217. _data.marker[i] = UNUSED;
  218. return;
  219. }
  220. this_type new_set(new_buckets, spill, _allocator);
  221. for (unsigned o = 0; o<old_size; ++o) {
  222. if (_data.marker[o] == UNUSED) {
  223. continue;
  224. }
  225. // Find/allocate entry
  226. auto &k = _data.key[o];
  227. unsigned i = new_set.hash(k);
  228. if (new_set._data.marker[i] == UNUSED) {
  229. new_set._data.marker[i] = END_OF_LIST;
  230. ++new_set._used;
  231. }
  232. else {
  233. // Walk the hash chain until the end and add a new entry
  234. while (new_set._data.marker[i] != END_OF_LIST)
  235. i = new_set._data.marker[i];
  236. unsigned j = new_set.allocate_spill();
  237. new_set._data.marker[i] = j;
  238. i = j;
  239. new_set._data.marker[i] = END_OF_LIST;
  240. }
  241. memmove(&new_set._data.key[i], &k, sizeof(K));
  242. _data.marker[o] = UNUSED;
  243. }
  244. swap(new_set);
  245. }
  246. template<class K, class H, class E>
  247. typename HashSet<K,H,E>::const_iterator HashSet<K,H,E>::begin() const
  248. {
  249. return const_iterator(*this, 0);
  250. }
  251. template<class K, class H, class E>
  252. typename HashSet<K,H,E>::const_iterator HashSet<K,H,E>::end() const
  253. {
  254. return const_iterator(*this, num_buckets());
  255. }
  256. template<class K, class H, class E>
  257. unsigned HashSet<K,H,E>::num_buckets() const
  258. {
  259. return _data.size;
  260. }
  261. template<class K, class H, class E>
  262. bool HashSet<K,H,E>::bucket_valid(unsigned i) const
  263. {
  264. return (_data.marker[i] & 0x80000000) == 0;
  265. }
  266. template<class K, class H, class E>
  267. const typename HashSet<K,H,E>::key_type &HashSet<K,H,E>::bucket_value(unsigned i) const
  268. {
  269. return _data.key[i];
  270. }
  271. template<class K, class H, class E>
  272. typename HashSet<K,H,E>::key_type &HashSet<K,H,E>::bucket_value(unsigned i)
  273. {
  274. return _data.key[i];
  275. }
  276. template<class K, class H, class E>
  277. bool HashSet<K,H,E>::full() const
  278. {
  279. return _spill_unused == 0 && _spill_freelist == END_OF_FREELIST;
  280. }
  281. template<class K, class H, class E>
  282. void HashSet<K,H,E>::grow()
  283. {
  284. do {
  285. unsigned new_buckets = _buckets * 2 + 1;
  286. if (new_buckets < 19)
  287. new_buckets = 19;
  288. rehash(new_buckets);
  289. } while (full());
  290. }
  291. template<class K, class H, class E> template <class KEY_EQ>
  292. unsigned HashSet<K,H,E>::hash(const KEY_EQ &k) const
  293. {
  294. return _hash(k) % _buckets;
  295. }
  296. template<class K, class H, class E>
  297. unsigned HashSet<K,H,E>::allocate_spill()
  298. {
  299. ++_used;
  300. unsigned i = 0;
  301. if (_spill_freelist != END_OF_FREELIST) {
  302. i = _spill_freelist & 0x7fffffff;
  303. _spill_freelist = _data.marker[i];
  304. return i;
  305. } else {
  306. XENSURE(_spill_unused > 0);
  307. i = _data.size - _spill_unused;
  308. --_spill_unused;
  309. }
  310. _data.marker[i] = UNUSED;
  311. return i;
  312. }
  313. template<class K, class H, class E>
  314. void HashSet<K,H,E>::free_spill(unsigned i)
  315. {
  316. --_used;
  317. _data.marker[i] = _spill_freelist;
  318. _spill_freelist = i | 0x80000000;
  319. }
  320. template<class K, class H, class E>
  321. void HashSet<K,H,E>::clear_freelist()
  322. {
  323. while (_spill_freelist != END_OF_FREELIST) {
  324. unsigned i = _spill_freelist & 0x7fffffff;
  325. _spill_freelist = _data.marker[i];
  326. _data.marker[i] = UNUSED;
  327. }
  328. }
  329. template<class K, class H, class E>
  330. void HashSet<K, H, E>::allocate_data(unsigned count)
  331. {
  332. XASSERT(_data.marker == nullptr, "Data must be deallocated/cleared before allocating new data");
  333. if (count == 0) {
  334. _data = Data();
  335. return;
  336. }
  337. auto marker_size = (((sizeof(unsigned) * count) + sizeof(void*) - 1) / sizeof(void*)) * sizeof(void*);
  338. auto key_size = sizeof(key_type) * count;
  339. auto data_size = marker_size + key_size;
  340. _data.marker = (unsigned*)_allocator.allocate(data_size);
  341. _data.key = (key_type*)&(((char*)_data.marker)[marker_size]);
  342. _data.size = count;
  343. }
  344. template<class K, class H, class E>
  345. template <class STREAM> void HashSet<K,H,E>::serialize(STREAM &stream)
  346. {
  347. unsigned n = _used;
  348. stream & n;
  349. if (stream.is_output()) {
  350. iterator it(*this, 0);
  351. iterator end(*this, num_buckets());
  352. for (; it != end; ++it)
  353. stream & *it;
  354. } else {
  355. for (unsigned i=0; i<n; ++i) {
  356. key_type k(_allocator);
  357. stream & k;
  358. insert(k);
  359. }
  360. }
  361. }
  362. } // namespace stingray_plugin_foundation