AGX Dynamics 2.42.1.1
Loading...
Searching...
No Matches
LinearProbingHashTable.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
19// Inspired by Emil Ernerfeldt:
20// https://github.com/emilk/emilib/tree/master/emilib
21// LICENSE:
22// This software is dual-licensed to the public domain and under the following
23// license: you are granted a perpetual, irrevocable license to copy, modify,
24// publish, and distribute this file as you see fit.
25
26
27
28
29
30#ifndef AGX_LINEAR_PROBING_HASH_TABLE_H
31#define AGX_LINEAR_PROBING_HASH_TABLE_H
32
33#include <cstdlib>
34#include <iterator>
35#include <utility>
36
37#include <agx/ref_ptr.h>
38#include <agx/hash.h>
39
40#include <agx/Allocator.h>
41
42namespace agx
43{
47template <typename KeyT, typename ValueT, typename HashT = agx::HashFn<KeyT>, typename AllocatorT = ByteAllocator>
49{
50private:
52 using PairT = std::pair<KeyT, ValueT>;
53 enum class State : uint8_t
54 {
55 INACTIVE, // Never been touched
56 ACTIVE, // Is inside a search-chain, but is empty
57 FILLED // Is set with key/value
58 };
59
60 const size_t MIN_SIZE = 4;
61 const size_t GROW_FACTOR = 2;
62 const float SHRINK_THRESHOLD = 0.25;
63 const float SMOOTHING_FACTOR = 0.8f;
64
65public:
66 using key_type = KeyT;
67 using size_type = size_t;
68 using value_type = ValueT;
69 using bucket_type = PairT;
70 using reference = PairT&;
71 using const_reference = const PairT&;
72
73 // The quadratic probing hash table supports various types of behavior when
74 // clearing. The linear probing hash table does not.
75 //
76 // We repeat the clear policy type here so that users can compile, even though
77 // the value is ignored for now.
79 {
83 };
84
85
86
88 {
89 public:
90 using iterator_category = std::forward_iterator_tag;
91 using difference_type = size_t;
92 using distance_type = size_t;
93 using bucket_type = std::pair<KeyT, ValueT>;
96
98
99 iterator(MyType* hash_map, size_t bucket) : _map(hash_map), _bucket(bucket)
100 {
101 }
102
104 {
105 this->goto_next_element();
106 return *this;
107 }
108
110 {
111 size_t old_index = _bucket;
112 this->goto_next_element();
113 return iterator(_map, old_index);
114 }
115
119 {
120 this->goto_previous_element();
121 return *this;
122 }
123
127 {
128 size_t old_index = _bucket;
129 this->goto_previous_element();
130 return iterator(_map, old_index);
131 }
132
134 {
135 return _map->_pairs[_bucket];
136 }
137
139 {
140 return _map->_pairs + _bucket;
141 }
142
143 bool operator==(const iterator& rhs) const
144 {
145 agxAssert(_map == rhs._map);
146 return this->_bucket == rhs._bucket;
147 }
148
149 bool operator!=(const iterator& rhs) const
150 {
151 agxAssert(_map == rhs._map);
152 return this->_bucket != rhs._bucket;
153 }
154
155 private:
156 void goto_next_element()
157 {
158 agxAssert(_bucket < _map->_num_buckets);
159 do {
160 _bucket++;
161 } while (_bucket < _map->_num_buckets && _map->_states[_bucket] != State::FILLED);
162 }
163
164 // This is tremendously dangerous. There is currently no way of detecting
165 // that we've reached rend since there is no rend.
166 void goto_previous_element()
167 {
168 agxAssert(_bucket > 0);
169
170 do {
171 _bucket--;
172 } while(_bucket > 0 && _map->_states[_bucket] != State::FILLED);
173
174 agxAssert(_map->_states[_bucket] == State::FILLED);
175 }
176
177 public:
179 size_t _bucket;
180 };
181
183 {
184 public:
185 using iterator_category = std::forward_iterator_tag;
186 using difference_type = size_t;
187 using distance_type = size_t;
188 using bucket_type = std::pair<KeyT, ValueT>;
191
193
195 {
196 }
197
198 const_iterator(const MyType* hash_map, size_t bucket) : _map(hash_map), _bucket(bucket)
199 {
200 }
201
203 {
204 this->goto_next_element();
205 return *this;
206 }
207
209 {
210 size_t old_index = _bucket;
211 this->goto_next_element();
212 return const_iterator(_map, old_index);
213 }
214
218 {
219 this->goto_previous_element();
220 return *this;
221 }
222
226 {
227 size_t old_index = _bucket;
228 this->goto_previous_element();
229 return iterator(_map, old_index);
230 }
231
233 {
234 return _map->_pairs[_bucket];
235 }
236
238 {
239 return _map->_pairs + _bucket;
240 }
241
242 bool operator==(const const_iterator& rhs) const
243 {
244 agxAssert(_map == rhs._map);
245 return this->_bucket == rhs._bucket;
246 }
247
248 bool operator!=(const const_iterator& rhs) const
249 {
250 agxAssert(_map == rhs._map);
251 return this->_bucket != rhs._bucket;
252 }
253
254 private:
255 void goto_next_element()
256 {
257 agxAssert(_bucket < _map->_num_buckets);
258 do {
259 _bucket++;
260 } while (_bucket < _map->_num_buckets && _map->_states[_bucket] != State::FILLED);
261 }
262
263 // This is tremendously dangerous. There is currently no way of detecting
264 // that we've reached rend since there is no rend.
265 void goto_previous_element()
266 {
267 agxAssert(_bucket > 0);
268
269 do {
270 _bucket--;
271 } while(_bucket > 0 && _map->_states[_bucket] != State::FILLED);
272
273 agxAssert(_map->_states[_bucket] == State::FILLED);
274 }
275
276 //private:
277 // friend class MyType;
278 public:
279 const MyType* _map;
280 size_t _bucket;
281 };
282
283 // ------------------------------------------------------------------------
284
286
288 {
289 reserve(other.size());
290 insert(cbegin(other), cend(other));
291 }
292
294 {
295 *this = std::move(other);
296 }
297
299 {
300 if ( this == &other ) {
301 return *this;
302 }
303
304 clear();
305 reserve(other.size());
306 insert(cbegin(other), cend(other));
307 return *this;
308 }
309
311 {
312 this->swap(other);
313 return *this;
314 }
315
317 {
318 for (size_t bucket=0; bucket<_num_buckets; ++bucket) {
319 if (_states[bucket] == State::FILLED) {
320 _pairs[bucket].~PairT();
321 }
322 }
323
324 _allocator.deallocateBytes(_states, _num_buckets * sizeof(State));
325 _allocator.deallocateBytes(_pairs, _num_buckets * sizeof(PairT));
326 }
327
329 {
330 std::swap(_hasher, other._hasher);
331 std::swap(_allocator, other._allocator);
332 std::swap(_states, other._states);
333 std::swap(_pairs, other._pairs);
334 std::swap(_num_buckets, other._num_buckets);
335 std::swap(_num_filled, other._num_filled);
336 std::swap(_max_probe_length, other._max_probe_length);
337 std::swap(_mask, other._mask);
338 }
339
340 // -------------------------------------------------------------
341
346 {
347 size_t bucket = 0;
348 while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
349 ++bucket;
350 }
351 return iterator(this, bucket);
352 }
353
358 {
359 size_t bucket = 0;
360 while (bucket<_num_buckets && _states[bucket] != State::FILLED) {
361 ++bucket;
362 }
363 return const_iterator(this, bucket);
364 }
365
366
371 { return iterator(this, _num_buckets); }
372
373
378 { return const_iterator(this, _num_buckets); }
379
383 size_t size() const
384 {
385 return _num_filled;
386 }
387
391 size_t capacity() const
392 {
393 return _num_buckets;
394 }
395
399 bool empty() const
400 {
401 return _num_filled==0;
402 }
403
404 // ------------------------------------------------------------
405
411 iterator find(const KeyT& key)
412 {
413 auto bucket = this->find_filled_bucket(key);
414 if (bucket == (size_t)-1) {
415 return this->end();
416 }
417 return iterator(this, bucket);
418 }
419
425 const_iterator find(const KeyT& key) const
426 {
427 auto bucket = this->find_filled_bucket(key);
428 if (bucket == (size_t)-1)
429 {
430 return this->end();
431 }
432 return const_iterator(this, bucket);
433 }
434
440 bool contains(const KeyT& k) const
441 {
442 return find_filled_bucket(k) != (size_t)-1;
443 }
444
451 size_t count(const KeyT& k) const
452 {
453 return find_filled_bucket(k) != (size_t)-1 ? 1 : 0;
454 }
455
461 ValueT* try_get(const KeyT& k)
462 {
463 auto bucket = find_filled_bucket(k);
464 if (bucket != (size_t)-1) {
465 return &_pairs[bucket].second;
466 } else {
467 return nullptr;
468 }
469 }
470
476 const ValueT* try_get(const KeyT& k) const
477 {
478 auto bucket = find_filled_bucket(k);
479 if (bucket != (size_t)-1) {
480 return &_pairs[bucket].second;
481 } else {
482 return nullptr;
483 }
484 }
485
486 // Convenience function.
487 const ValueT get_or_return_default(const KeyT& k) const
488 {
489 const ValueT* ret = try_get(k);
490 if (ret) {
491 return *ret;
492 } else {
493 return ValueT();
494 }
495 }
496
497 // -----------------------------------------------------
498
506 iterator insert(const KeyT& key, const ValueT value)
507 {
508 check_expand_need();
509
510 auto bucket = find_or_allocate(key);
511 if (_states[bucket] == State::FILLED) {
512 (_pairs + bucket)->second = value;
513 } else {
514 _states[bucket] = State::FILLED;
515 new(_pairs + bucket) PairT(key, value);
516 _num_filled++;
517 }
518
519 return iterator(this, bucket);
520 }
521
522 iterator insert(const std::pair<KeyT, ValueT>& p)
523 {
524 return insert(p.first, p.second);
525 }
526
534 {
535 for (; begin != end; ++begin) {
536 insert(begin->first, begin->second);
537 }
538 }
539
548 void insert_unique(KeyT&& key, ValueT&& value)
549 {
550 agxAssert(!contains(key));
551 check_expand_need();
552 auto bucket = find_empty_bucket(key);
553 _states[bucket] = State::FILLED;
554 new(_pairs + bucket) PairT(std::move(key), std::move(value));
555 _num_filled++;
556 }
557
558 void insert_unique(std::pair<KeyT, ValueT>&& p)
559 {
560 insert_unique(std::move(p.first), std::move(p.second));
561 }
562
563 // Return the old value or ValueT() if it didn't exist.
564 ValueT set_get(const KeyT& key, const ValueT& new_value)
565 {
566 check_expand_need();
567
568 auto bucket = find_or_allocate(key);
569
570 // Check if inserting a new value rather than overwriting an old entry
571 if (_states[bucket] == State::FILLED) {
572 ValueT old_value = _pairs[bucket].second;
573 _pairs[bucket] = new_value.second;
574 return old_value;
575 } else {
576 _states[bucket] = State::FILLED;
577 new(_pairs + bucket) PairT(key, new_value);
578 _num_filled++;
579 return ValueT();
580 }
581 }
582
590 ValueT& operator[](const KeyT& key)
591 {
592 check_expand_need();
593
594 auto bucket = find_or_allocate(key);
595
596 /* Check if inserting a new value rather than overwriting an old entry */
597 if (_states[bucket] != State::FILLED) {
598 _states[bucket] = State::FILLED;
599 new(_pairs + bucket) PairT(key, ValueT());
600 _num_filled++;
601 }
602
603 return _pairs[bucket].second;
604 }
605
606 // -------------------------------------------------------
607
612 bool erase(const KeyT& key)
613 {
614 auto bucket = find_filled_bucket(key);
615 if (bucket != (size_t)-1) {
616 this->erase_bucket(bucket);
617 return true;
618 } else {
619 return false;
620 }
621 }
622
628 {
629 agxAssert(it._map == this);
630 agxAssert(it._bucket < _num_buckets);
631 this->erase_bucket(it._bucket);
632 return ++it;
633 }
634
638 void clear(int policy = SHRINK_BUFFER_AVERAGED )
639 {
640 if (this->empty())
641 return;
642
643 if (policy == MAINTAIN_BUFFER) {
644 for (size_t bucket = 0; bucket < _num_buckets; ++bucket) {
645 if (_states[bucket] == State::FILLED) {
646 _states[bucket] = State::INACTIVE;
647 _pairs[bucket].~PairT();
648 }
649 }
650 _num_filled = 0;
651 _max_probe_length = -1;
652 }
653 else if (policy == SHRINK_BUFFER) {
654 clearResize( 0 );
655 m_smoothingAverage = 0.0;
656 }
657 else //if (policy == SHRINK_BUFFER_AVERAGED)
658 {
659 /* Smoothing average is never less than current size */
660 if ((Real32)_num_filled > m_smoothingAverage)
661 m_smoothingAverage = Real32( _num_filled );
662
663 /* Update smoothing average */
664 m_smoothingAverage = Real32( SMOOTHING_FACTOR * m_smoothingAverage + ( 1.0 - SMOOTHING_FACTOR ) * (Real32)_num_filled );
665
666 /* Reallocate buffer if the smoothing average is sufficiently below the current capacity */
667 if (m_smoothingAverage < SHRINK_THRESHOLD * (Real32)_num_buckets && m_smoothingAverage >= (Real32)MIN_SIZE) {
668 clearResize( (int)m_smoothingAverage );
669 }
670 else {
671 for (size_t bucket = 0; bucket < _num_buckets; ++bucket) {
672 if (_states[bucket] == State::FILLED) {
673 _states[bucket] = State::INACTIVE;
674 _pairs[bucket].~PairT();
675 }
676 }
677 _num_filled = 0;
678 _max_probe_length = -1;
679 }
680 }
681 }
682
686 void reserve(size_t num_elems)
687 {
688 size_t required_buckets = num_elems + num_elems/2 + 1;
689 if (required_buckets <= _num_buckets) {
690 return;
691 }
692 size_t num_buckets = MIN_SIZE;
693 while (num_buckets < required_buckets)
694 num_buckets *= GROW_FACTOR;
695
696 auto new_states = (State*)_allocator.allocateBytes(num_buckets * sizeof(State));
697 auto new_pairs = (PairT*)_allocator.allocateBytes(num_buckets * sizeof(PairT));
698
699 if (!new_states || !new_pairs) {
700 _allocator.deallocateBytes(new_states, num_buckets * sizeof(State));
701 _allocator.deallocateBytes(new_pairs, num_buckets * sizeof(PairT));
702 throw std::bad_alloc();
703 }
704
705 //auto old_num_filled = _num_filled;
706 auto old_num_buckets = _num_buckets;
707 auto old_states = _states;
708 auto old_pairs = _pairs;
709
710 _num_filled = 0;
711 _num_buckets = num_buckets;
712 _mask = _num_buckets - 1;
713 _states = new_states;
714 _pairs = new_pairs;
715
716 std::fill_n(_states, num_buckets, State::INACTIVE);
717
718 _max_probe_length = -1;
719
720 for (size_t src_bucket=0; src_bucket<old_num_buckets; src_bucket++) {
721 if (old_states[src_bucket] == State::FILLED) {
722 auto& src_pair = old_pairs[src_bucket];
723
724 auto dst_bucket = find_empty_bucket(src_pair.first);
725 agxAssert(dst_bucket != (size_t)-1);
726 agxAssert(_states[dst_bucket] != State::FILLED);
727 _states[dst_bucket] = State::FILLED;
728 new(_pairs + dst_bucket) PairT(std::move(src_pair));
729 _num_filled += 1;
730
731 src_pair.~PairT();
732 }
733 }
734
735 //agxAssert(old_num_filles == _num_filled);
736
737 _allocator.deallocateBytes(old_states, old_num_buckets * sizeof(State));
738 _allocator.deallocateBytes(old_pairs, old_num_buckets * sizeof(PairT));
739 }
740
744 void clearResize( size_t num_elems )
745 {
746 size_t required_buckets = num_elems + num_elems / 2 + 1;
747
748 size_t num_buckets = MIN_SIZE;
749 while (num_buckets < required_buckets)
750 num_buckets *= GROW_FACTOR;
751
752 auto new_states = (State*)_allocator.allocateBytes( num_buckets * sizeof( State ) );
753 auto new_pairs = (PairT*)_allocator.allocateBytes( num_buckets * sizeof( PairT ) );
754
755 if (!new_states || !new_pairs) {
756 _allocator.deallocateBytes( new_states, num_buckets * sizeof( State ) );
757 _allocator.deallocateBytes( new_pairs, num_buckets * sizeof( PairT ) );
758 throw std::bad_alloc();
759 }
760
761 for (size_t bucket = 0; bucket < _num_buckets; bucket++)
762 if (_states[bucket] == State::FILLED)
763 _pairs[bucket].~PairT();
764
765 _allocator.deallocateBytes( _states, _num_buckets * sizeof( State ) );
766 _allocator.deallocateBytes( _pairs, _num_buckets * sizeof( PairT ) );
767
768 _num_filled = 0;
769 _num_buckets = num_buckets;
770 _mask = _num_buckets - 1;
771 _states = new_states;
772 _pairs = new_pairs;
773
774 std::fill_n( _states, num_buckets, State::INACTIVE );
775
776 _max_probe_length = -1;
777 }
778
779
783 const void *ptr() const
784 {
785 return _pairs;
786 }
787
788protected:
789 // Find the bucket with this key, or return nullptr
790 template<typename T2>
791 size_t find_filled_bucket(const T2& key) const
792 {
793 if (empty()) { return (size_t)-1; } // Optimization
794
795 auto hash_value = _hasher(key);
796 for (int offset=0; offset<=_max_probe_length; ++offset) {
797 auto bucket = (hash_value + offset) & _mask;
798 if (_states[bucket] == State::FILLED && agx::hashKeyEqual(_pairs[bucket].first, key)) {
799 return bucket;
800 }
801 if (_states[bucket] == State::INACTIVE) {
802 return (size_t)-1; // End of the chain!
803 }
804 }
805 return (size_t)-1;
806 }
807
808 void erase_bucket(size_t bucket) {
809 _states[bucket] = State::ACTIVE;
810 _pairs[bucket].~PairT();
811 _num_filled -= 1;
812 }
813
814
815private:
816 // Can we fit another element?
817 void check_expand_need()
818 {
819 reserve(_num_filled + 1);
820 }
821 // Find the bucket with this key, or return a good empty bucket to place the key in.
822 // In the latter case, the bucket is expected to be filled.
823 size_t find_or_allocate(const KeyT& key)
824 {
825 auto hash_value = _hasher(key);
826 size_t hole = (size_t)-1;
827 int offset=0;
828 for (; offset<=_max_probe_length; ++offset) {
829 auto bucket = (hash_value + offset) & _mask;
830
831 if (_states[bucket] == State::FILLED) {
832 if (agx::hashKeyEqual(_pairs[bucket].first, key)) {
833 return bucket;
834 }
835 } else if (_states[bucket] == State::INACTIVE) {
836 return bucket;
837 } else {
838 // ACTIVE: keep searching
839 if (hole == (size_t)-1) {
840 hole = bucket;
841 }
842 }
843 }
844
845 // No key found - but maybe a hole for it
846
847 agxAssert(offset == _max_probe_length+1);
848
849 if (hole != (size_t)-1) {
850 return hole;
851 }
852
853 // No hole found within _max_probe_length
854 for (; ; ++offset) {
855 auto bucket = (hash_value + offset) & _mask;
856
857 if (_states[bucket] != State::FILLED) {
858 _max_probe_length = offset;
859 return bucket;
860 }
861 }
862 }
863
864 // key is not in this map. Find a place to put it.
865 size_t find_empty_bucket(const KeyT& key)
866 {
867 auto hash_value = _hasher(key);
868 for (int offset=0; ; ++offset) {
869 auto bucket = (hash_value + offset) & _mask;
870 if (_states[bucket] != State::FILLED) {
871 if (offset > _max_probe_length) {
872 _max_probe_length = offset;
873 }
874 return bucket;
875 }
876 }
877 }
878
879private:
880 HashT _hasher;
881 AllocatorT _allocator = AllocatorT();
882 State* _states = nullptr;
883 PairT* _pairs = nullptr;
884 size_t _num_buckets = 0;
885 size_t _num_filled = 0;
886 int _max_probe_length = -1; // Our longest bucket-brigade is this long. ONLY when we have zero elements is this ever negative (-1).
887 size_t _mask = 0; // _num_buckets minus one
888 Real32 m_smoothingAverage = 0;
889};
890
891
892 template <typename KeyT, typename ValueT, typename HashT, typename AllocatorT>
894 {
895 return table.begin();
896 }
897
898 template <typename KeyT, typename ValueT, typename HashT, typename AllocatorT>
900 {
901 return table.end();
902 }
903
904
905 template <typename KeyT, typename ValueT, typename HashT, typename AllocatorT>
907 {
908 return table.begin();
909 }
910
911 template <typename KeyT, typename ValueT, typename HashT, typename AllocatorT>
913 {
914 return table.end();
915 }
916
917
925 template <typename KeyT, typename DataT, typename HashT = agx::HashFn<KeyT>, typename AllocatorT = ByteAllocator>
926 class LinearProbingHashTable : public LinearProbingHashTableImplementation<KeyT, DataT, HashT, AllocatorT>
927 {
928 public:
930
932 {
933 }
934 };
935
936
937
938 template <typename KeyT, typename DataT, typename HashT, typename AllocatorT>
939 class LinearProbingHashTable< agx::ref_ptr<KeyT>, DataT, HashT, AllocatorT> : public LinearProbingHashTableImplementation<agx::ref_ptr<KeyT>, DataT, HashT, AllocatorT>
940 {
941 public:
943 typedef typename Implementation::iterator iterator;
944 typedef typename Implementation::const_iterator const_iterator;
945
947 {}
948
950 bool contains(const KeyT *key) const
951 {
952 return this->find_filled_bucket(key) != (size_t)-1;
953 }
954
956 iterator find(const KeyT *key)
957 {
958 auto bucket = this->find_filled_bucket(key);
959 if (bucket == (size_t)-1) {
960 return this->end();
961 }
962 return iterator(this, bucket);
963 }
964
965 const_iterator find(const KeyT *key) const
966 {
967 auto bucket = this->find_filled_bucket(key);
968 if (bucket == (size_t)-1) {
969 return this->end();
970 }
971 return const_iterator(this, bucket);
972 }
973
975 bool erase(const KeyT *key)
976 {
977 auto bucket = this->find_filled_bucket(key);
978 if (bucket != (size_t)-1) {
979 this->erase_bucket(bucket);
980 return true;
981 } else {
982 return false;
983 }
984 }
985
986 };
987}
988
989#endif
const_iterator operator--(int)
Does not do any bounds checking, so only call this if there really is a previous element.
const_iterator operator--()
Does not do any bounds checking, so only call this if there really is a previous element.
iterator operator--(int)
Does not do any bounds checking, so only call this if there really is a previous element.
iterator operator--()
Does not do any bounds checking, so only call this if there really is a previous element.
A cache-friendly hash table with open addressing, linear probing and power-of-two capacity.
iterator insert(const KeyT &key, const ValueT value)
Insert a key/value pair into the hash table.
iterator begin()
Iterator to first element in hash table.
bool contains(const KeyT &k) const
Check if the hash table contains a key/value pair for the given key.
void insert(const_iterator begin, const_iterator end)
Insert all elements in the given range.
void clearResize(size_t num_elems)
Resize the table to fit at least num_elems elements and clear all existing entries.
ValueT & operator[](const KeyT &key)
Return the value associated with the given key.
iterator erase(iterator it)
Erase an element from the hash table.
const_iterator end() const
Iterator marking end of hash table.
const ValueT * try_get(const KeyT &k) const
Find the value associated with the given key.
iterator end()
Iterator marking end of hash table.
@ SHRINK_BUFFER_AVERAGED
Buffer is shrunk if a smoothing average (which is updated each clear call) goes below a threshold.
@ MAINTAIN_BUFFER
Buffer is maintained (normal stl behavior).
@ SHRINK_BUFFER
Buffer is deallocated and replaced by an newly allocated empty buffer.
bool erase(const KeyT &key)
Erase an element from the hash table.
void clear(int policy=SHRINK_BUFFER_AVERAGED)
Remove all elements.
LinearProbingHashTableImplementation & operator=(const LinearProbingHashTableImplementation &other)
void insert_unique(KeyT &&key, ValueT &&value)
Insert a key/value pair into the hash table assuming that the given key does not already exist in the...
ValueT * try_get(const KeyT &k)
Find the value associated with the given key.
void insert_unique(std::pair< KeyT, ValueT > &&p)
ValueT set_get(const KeyT &key, const ValueT &new_value)
const_iterator find(const KeyT &key) const
Find a key/value pair in the hash table given a key.
const ValueT get_or_return_default(const KeyT &k) const
LinearProbingHashTableImplementation & operator=(LinearProbingHashTableImplementation &&other)
void reserve(size_t num_elems)
Make room for this many elements in the hash table.
LinearProbingHashTableImplementation(LinearProbingHashTableImplementation &&other)
void swap(LinearProbingHashTableImplementation &other)
iterator find(const KeyT &key)
Find a key/value pair in the hash table given a key.
const_iterator begin() const
Iterator to first element in hash table.
size_t count(const KeyT &k) const
Count the number of key/value pairs matching the given key.
iterator insert(const std::pair< KeyT, ValueT > &p)
LinearProbingHashTableImplementation(const LinearProbingHashTableImplementation &other)
LinearProbingHashTableImplementation< agx::ref_ptr< KeyT >, DataT, HashT, AllocatorT > Implementation
Inheritance with partial specialization due to bug with ref_ptr containers.
LinearProbingHashTableImplementation< KeyT, DataT, HashT, AllocatorT > 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)
float Real32
Definition: Real.h:43
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