AGX Dynamics 2.42.1.1
Loading...
Searching...
No Matches
LinearProbingHashSet.h
Go to the documentation of this file.
1/*
2Copyright 2007-2025. Algoryx Simulation AB.
3
4All AGX source code, intellectual property, documentation, sample code,
5tutorials, scene files and technical white papers, are copyrighted, proprietary
6and confidential material of Algoryx Simulation AB. You may not download, read,
7store, distribute, publish, copy or otherwise disseminate, use or expose this
8material unless having a written signed agreement with Algoryx Simulation AB, or having been
9advised so by Algoryx Simulation AB for a time limited evaluation, or having purchased a
10valid commercial license from Algoryx Simulation AB.
11
12Algoryx Simulation AB disclaims all responsibilities for loss or damage caused
13from using this software, unless otherwise stated in written agreements with
14Algoryx Simulation AB.
15*/
16
17
18// Inspired by Emil Ernerfeldt:
19// https://github.com/emilk/emilib/tree/master/emilib
20// LICENSE:
21// This software is dual-licensed to the public domain and under the following
22// license: you are granted a perpetual, irrevocable license to copy, modify,
23// publish, and distribute this file as you see fit.
24
25
26
27
28#pragma once
29
30#include <cstdlib>
31#include <iterator>
32#include <utility>
33
34#include <agx/ref_ptr.h>
35#include <agx/hash.h>
36
37namespace agx
38{
42 template <typename KeyT, typename HashT = agx::HashFn<KeyT>>
44 {
45 private:
47 enum class State : uint8_t
48 {
49 INACTIVE, // Never been touched
50 ACTIVE, // Is inside a search-chain, but is empty
51 FILLED // Is set with key/value
52 };
53
54 public:
55 using size_type = size_t;
56 using value_type = KeyT;
57 using reference = KeyT&;
58 using const_reference = const KeyT&;
59
61 {
62 public:
63 using iterator_category = std::forward_iterator_tag;
64 using difference_type = size_t;
65 using distance_type = size_t;
66 using value_type = KeyT;
69
71 {
72 }
73
74 iterator(MyType* hash_set, size_t bucket) : _set(hash_set), _bucket(bucket)
75 {
76 }
77
79 {
80 this->goto_next_element();
81 return *this;
82 }
83
85 {
86 size_t old_index = _bucket;
87 this->goto_next_element();
88 return iterator(_set, old_index);
89 }
90
92 {
93 return _set->_keys[_bucket];
94 }
95
97 {
98 return _set->_keys + _bucket;
99 }
100
101 bool operator==(const iterator& rhs)
102 {
103 agxAssert(_set == rhs._set);
104 return this->_bucket == rhs._bucket;
105 }
106
107 bool operator!=(const iterator& rhs)
108 {
109 agxAssert(_set == rhs._set);
110 return this->_bucket != rhs._bucket;
111 }
112
113 private:
114 void goto_next_element()
115 {
116 agxAssert(_bucket < _set->_num_buckets);
117 do {
118 _bucket++;
119 } while (_bucket < _set->_num_buckets && _set->_states[_bucket] != State::FILLED);
120 }
121
122 //private:
123 // friend class MyType;
124 public:
126 size_t _bucket;
127 };
128
130 {
131 public:
132 using iterator_category = std::forward_iterator_tag;
133 using difference_type = size_t;
134 using distance_type = size_t;
135 using value_type = const KeyT;
138
140
142 {
143 }
144
145 const_iterator(const MyType* hash_set, size_t bucket) : _set(hash_set), _bucket(bucket)
146 {
147 }
148
150 {
151 this->goto_next_element();
152 return *this;
153 }
154
156 {
157 size_t old_index = _bucket;
158 this->goto_next_element();
159 return const_iterator(_set, old_index);
160 }
161
163 {
164 return _set->_keys[_bucket];
165 }
166
168 {
169 return _set->_keys + _bucket;
170 }
171
172 bool operator==(const const_iterator& rhs)
173 {
174 agxAssert(_set == rhs._set);
175 return this->_bucket == rhs._bucket;
176 }
177
178 bool operator!=(const const_iterator& rhs)
179 {
180 agxAssert(_set == rhs._set);
181 return this->_bucket != rhs._bucket;
182 }
183
184 private:
185 void goto_next_element()
186 {
187 agxAssert(_bucket < _set->_num_buckets);
188 do {
189 _bucket++;
190 } while (_bucket < _set->_num_buckets && _set->_states[_bucket] != State::FILLED);
191 }
192
193 //private:
194 // friend class MyType;
195 public:
196 const MyType* _set;
197 size_t _bucket;
198 };
199
200 // ------------------------------------------------------------------------
201
203
205 {
206 reserve(other.size());
207 insert(cbegin(other), cend(other));
208 }
209
211 {
212 *this = std::move(other);
213 }
214
216 {
217 if ( this == &other ) {
218 return *this;
219 }
220
221 clear();
222 reserve(other.size());
223 insert(cbegin(other), cend(other));
224 return *this;
225 }
226
228 {
229 this->swap(other);
230 return *this;
231 }
232
234 {
235 for (size_t bucket=0; bucket<_num_buckets; ++bucket) {
236 if (_states[bucket] == State::FILLED) {
237 _keys[bucket].~KeyT();
238 }
239 }
240 free(_states);
241 free(_keys);
242 }
243
245 {
246 std::swap(_hasher, other._hasher);
247 std::swap(_states, other._states);
248 std::swap(_keys, other._keys);
249 std::swap(_num_buckets, other._num_buckets);
250 std::swap(_num_filled, other._num_filled);
251 std::swap(_max_probe_length, other._max_probe_length);
252 std::swap(_mask, other._mask);
253 }
254
255 // -------------------------------------------------------------
256
258 {
259 size_t bucket = 0;
260 while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
261 ++bucket;
262 }
263 return iterator(this, bucket);
264 }
265
267 {
268 size_t bucket = 0;
269 while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
270 ++bucket;
271 }
272 return const_iterator(this, bucket);
273 }
274
276 { return iterator(this, _num_buckets); }
277
279 { return const_iterator(this, _num_buckets); }
280
281 size_t size() const
282 {
283 return _num_filled;
284 }
285
286 bool empty() const
287 {
288 return _num_filled==0;
289 }
290
291 // ------------------------------------------------------------
292
293 iterator find(const KeyT& key)
294 {
295 auto bucket = this->find_filled_bucket(key);
296 if (bucket == (size_t)-1) {
297 return this->end();
298 }
299 return iterator(this, bucket);
300 }
301
302 const_iterator find(const KeyT& key) const
303 {
304 auto bucket = this->find_filled_bucket(key);
305 if (bucket == (size_t)-1) {
306 return this->end();
307 }
308 return const_iterator(this, bucket);
309 }
310
311 bool contains(const KeyT& k) const
312 {
313 return find_filled_bucket(k) != (size_t)-1;
314 }
315
316 size_t count(const KeyT& k) const
317 {
318 return find_filled_bucket(k) != (size_t)-1 ? 1 : 0;
319 }
320
321 // -----------------------------------------------------
322
323 // Insert an element, unless it already exists.
324 // Returns a pair consisting of an iterator to the inserted element
325 // (or to the element that prevented the insertion)
326 // and a bool denoting whether the insertion took place.
327 iterator insert(const KeyT& key)
328 {
329 check_expand_need();
330
331 auto bucket = find_or_allocate(key);
332
333 if (_states[bucket] == State::FILLED) {
334 _keys[bucket] = key;
335 return iterator(this, bucket);
336 } else {
337 _states[bucket] = State::FILLED;
338 new(_keys + bucket) KeyT(key);
339 _num_filled++;
340 return iterator(this, bucket);
341 }
342 }
343
344 // Insert an element, unless it already exists.
345 // Returns a pair consisting of an iterator to the inserted element
346 // (or to the element that prevented the insertion)
347 // and a bool denoting whether the insertion took place.
348 iterator insert(KeyT&& key)
349 {
350 check_expand_need();
351
352 auto bucket = find_or_allocate(key);
353
354 if (_states[bucket] == State::FILLED) {
355 _keys[bucket] = std::move(key);
356 return iterator(this, bucket);
357 } else {
358 _states[bucket] = State::FILLED;
359 new(_keys + bucket) KeyT(std::move(key));
360 _num_filled++;
361 return iterator(this, bucket);
362 }
363 }
364
365 template<class... Args>
366 iterator emplace(Args&&... args)
367 {
368 return insert(KeyT(std::forward<Args>(args)...));
369 }
370
372 {
373 for (; begin != end; ++begin) {
374 insert(*begin);
375 }
376 }
377
378 // Same as above, but contains(key) MUST be false
379 void insert_unique(KeyT&& key)
380 {
381 agxAssert(!contains(key));
382 check_expand_need();
383 auto bucket = find_empty_bucket(key);
384 _states[bucket] = State::FILLED;
385 new(_keys + bucket) KeyT(std::move(key));
386 _num_filled++;
387 }
388
389 // -------------------------------------------------------
390
391 /* Erase an element from the hash set.
392 return false if element was not found */
393 bool erase(const KeyT& key)
394 {
395 auto bucket = find_filled_bucket(key);
396 if (bucket != (size_t)-1) {
397 erase_bucket(bucket);
398 return true;
399 } else {
400 return false;
401 }
402 }
403
404 /* Erase an element using an iterator.
405 Returns an iterator to the next element (or end()). */
407 {
408 agxAssert(it._set == this);
409 agxAssert(it._bucket < _num_buckets);
411 return ++it;
412 }
413
414 // Remove all elements, keeping full capacity.
415 void clear()
416 {
417 if (this->empty())
418 return;
419
420 for (size_t bucket=0; bucket<_num_buckets; ++bucket) {
421 if (_states[bucket] == State::FILLED) {
422 _states[bucket] = State::INACTIVE;
423 _keys[bucket].~KeyT();
424 }
425 }
426 _num_filled = 0;
427 _max_probe_length = -1;
428 }
429
430 // Make room for this many elements
431 void reserve(size_t num_elems)
432 {
433 size_t required_buckets = num_elems + num_elems/2 + 1;
434 if (required_buckets <= _num_buckets) {
435 return;
436 }
437 size_t num_buckets = 4;
438 while (num_buckets < required_buckets) { num_buckets *= 2; }
439
440 auto new_states = (State*)malloc(num_buckets * sizeof(State));
441 auto new_keys = (KeyT*)malloc(num_buckets * sizeof(KeyT));
442
443 if (!new_states || !new_keys) {
444 free(new_states);
445 free(new_keys);
446 throw std::bad_alloc();
447 }
448
449 // auto old_num_filled = _num_filled;
450 auto old_num_buckets = _num_buckets;
451 auto old_states = _states;
452 auto old_keys = _keys;
453
454 _num_filled = 0;
455 _num_buckets = num_buckets;
456 _mask = _num_buckets - 1;
457 _states = new_states;
458 _keys = new_keys;
459
460 std::fill_n(_states, num_buckets, State::INACTIVE);
461
462 _max_probe_length = -1;
463
464 for (size_t src_bucket=0; src_bucket<old_num_buckets; src_bucket++) {
465 if (old_states[src_bucket] == State::FILLED) {
466 auto& src = old_keys[src_bucket];
467
468 auto dst_bucket = find_empty_bucket(src);
469 agxAssert(dst_bucket != (size_t)-1);
470 agxAssert(_states[dst_bucket] != State::FILLED);
471 _states[dst_bucket] = State::FILLED;
472 new(_keys + dst_bucket) KeyT(std::move(src));
473 _num_filled += 1;
474
475 src.~KeyT();
476 }
477 }
478
479 // DCHECK_EQ_F(old_num_filled, _num_filled);
480
481 free(old_states);
482 free(old_keys);
483 }
484
485 protected:
486 // Find the bucket with this key, or return nullptr
487 template<typename T2>
488 size_t find_filled_bucket(const T2& key) const
489 {
490 if (empty()) { return (size_t)-1; } // Optimization
491
492 auto hash_value = _hasher(key);
493 for (int offset=0; offset<=_max_probe_length; ++offset) {
494 auto bucket = (hash_value + offset) & _mask;
495 if (_states[bucket] == State::FILLED && agx::hashKeyEqual(_keys[bucket], key)) {
496 return bucket;
497 }
498 if (_states[bucket] == State::INACTIVE) {
499 return (size_t)-1; // End of the chain!
500 }
501 }
502 return (size_t)-1;
503 }
504
505 void erase_bucket(size_t bucket) {
506 _states[bucket] = State::ACTIVE;
507 _keys[bucket].~KeyT();
508 _num_filled -= 1;
509 }
510
511 private:
512 // Can we fit another element?
513 void check_expand_need()
514 {
515 reserve(_num_filled + 1);
516 }
517
518 // Find the bucket with this key, or return a good empty bucket to place the key in.
519 // In the latter case, the bucket is expected to be filled.
520 size_t find_or_allocate(const KeyT& key)
521 {
522 auto hash_value = _hasher(key);
523 size_t hole = (size_t)-1;
524 int offset=0;
525 for (; offset<=_max_probe_length; ++offset) {
526 auto bucket = (hash_value + offset) & _mask;
527
528 if (_states[bucket] == State::FILLED) {
529 if (agx::hashKeyEqual(_keys[bucket], key)) {
530 return bucket;
531 }
532 } else if (_states[bucket] == State::INACTIVE) {
533 return bucket;
534 } else {
535 // ACTIVE: keep searching
536 if (hole == (size_t)-1) {
537 hole = bucket;
538 }
539 }
540 }
541
542 // No key found - but maybe a hole for it
543
544 agxAssert(offset == _max_probe_length+1);
545
546 if (hole != (size_t)-1) {
547 return hole;
548 }
549
550 // No hole found within _max_probe_length
551 for (; ; ++offset) {
552 auto bucket = (hash_value + offset) & _mask;
553
554 if (_states[bucket] != State::FILLED) {
555 _max_probe_length = offset;
556 return bucket;
557 }
558 }
559 }
560
561 // key is not in this map. Find a place to put it.
562 size_t find_empty_bucket(const KeyT& key)
563 {
564 auto hash_value = _hasher(key);
565 for (int offset=0; ; ++offset) {
566 auto bucket = (hash_value + offset) & _mask;
567 if (_states[bucket] != State::FILLED) {
568 if (offset > _max_probe_length) {
569 _max_probe_length = offset;
570 }
571 return bucket;
572 }
573 }
574 }
575
576 private:
577 HashT _hasher;
578 State* _states = nullptr;
579 KeyT* _keys = nullptr;
580 size_t _num_buckets = 0;
581 size_t _num_filled = 0;
582 int _max_probe_length = -1; // Our longest bucket-brigade is this long. ONLY when we have zero elements is this ever negative (-1).
583 size_t _mask = 0; // _num_buckets minus one
584 };
585
586 template <typename KeyT, typename HashT>
588 {
589 return set.begin();
590 }
591
592 template <typename KeyT, typename HashT>
594 {
595 return set.end();
596 }
597
598
599 template <typename KeyT, typename HashT>
601 {
602 return set.begin();
603 }
604
605 template <typename KeyT, typename HashT>
607 {
608 return set.end();
609 }
610
611
619 template <typename KeyT, typename HashT = agx::HashFn<KeyT>>
621 {
622 public:
624
626 {
627 }
628 };
629
630
631
632 template <typename KeyT, typename HashT>
633 class LinearProbingHashSet< agx::ref_ptr<KeyT>, HashT> : public LinearProbingHashSetImplementation<agx::ref_ptr<KeyT>, HashT>
634 {
635 public:
637 typedef typename Implementation::iterator iterator;
638 typedef typename Implementation::const_iterator const_iterator;
639
641 {}
642
644 bool contains(const KeyT *key) const
645 {
646 return this->find_filled_bucket(key) != (size_t)-1;
647 }
648
650 iterator find(const KeyT *key)
651 {
652 auto bucket = this->find_filled_bucket(key);
653 if (bucket == (size_t)-1) {
654 return this->end();
655 }
656 return iterator(this, bucket);
657 }
658
659 const_iterator find(const KeyT *key) const
660 {
661 auto bucket = this->find_filled_bucket(key);
662 if (bucket == (size_t)-1) {
663 return this->end();
664 }
665 return const_iterator(this, bucket);
666 }
667
669 bool erase(const KeyT *key)
670 {
671 auto bucket = this->find_filled_bucket(key);
672 if (bucket != (size_t)-1) {
673 this->erase_bucket(bucket);
674 return true;
675 } else {
676 return false;
677 }
678 }
679
680 };
681}
const_iterator(const MyType *hash_set, size_t bucket)
A cache-friendly hash set with open addressing, linear probing and power-of-two capacity.
size_t find_filled_bucket(const T2 &key) const
LinearProbingHashSetImplementation & operator=(const LinearProbingHashSetImplementation &other)
void swap(LinearProbingHashSetImplementation &other)
LinearProbingHashSetImplementation & operator=(LinearProbingHashSetImplementation &&other)
LinearProbingHashSetImplementation(LinearProbingHashSetImplementation &&other)
const_iterator find(const KeyT &key) const
void insert(const_iterator begin, const_iterator end)
LinearProbingHashSetImplementation(const LinearProbingHashSetImplementation &other)
LinearProbingHashSetImplementation< agx::ref_ptr< KeyT >, HashT > Implementation
Inheritance with partial specialization due to bug with ref_ptr containers.
LinearProbingHashSetImplementation< KeyT, HashT > Implementation
Smart pointer for handling referenced counted objects.
Definition: ref_ptr.h:30
#define agxAssert(expr)
Definition: debug.h:143
The agx namespace contains the dynamics/math part of the AGX Dynamics API.
LinearProbingHashSetImplementation< KeyT, HashT >::const_iterator cend(const LinearProbingHashSetImplementation< KeyT, HashT > &set)
LinearProbingHashSetImplementation< KeyT, HashT >::const_iterator cbegin(const LinearProbingHashSetImplementation< KeyT, HashT > &set)
LinearProbingHashSetImplementation< KeyT, HashT >::iterator end(LinearProbingHashSetImplementation< KeyT, HashT > &set)
LinearProbingHashSetImplementation< KeyT, HashT >::iterator begin(LinearProbingHashSetImplementation< KeyT, HashT > &set)
bool hashKeyEqual(const T1 &key1, const T2 &key2)
Definition: HashFunction.h:177
void swap(agx::Name &lhs, agx::Name &rhs)
Definition: Name.h:323