Loading...
Searching...
No Matches
denseHashMap.h
Go to the documentation of this file.
1//
2// Copyright 2016 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_BASE_TF_DENSE_HASH_MAP_H
8#define PXR_BASE_TF_DENSE_HASH_MAP_H
9
11
12#include "pxr/pxr.h"
14#include "pxr/base/tf/hashmap.h"
15
16#include <memory>
17#include <type_traits>
18#include <utility>
19#include <vector>
20
21PXR_NAMESPACE_OPEN_SCOPE
22
34template <
35 class Key,
36 class Data,
37 class HashFn,
38 class EqualKey = std::equal_to<Key>,
39 unsigned Threshold = 128
40>
41
43{
44public:
45
46 using key_type = Key;
47 using mapped_type = Data;
48 using value_type = std::pair<const Key, Data>;
49 using pointer = value_type*;
50 using const_pointer = const value_type*;
51
53
54private:
55
56 // This helper implements a std::pair with an assignment operator that
57 // uses placement new instead of assignment. The benefit here is that
58 // the two elements of the pair may be const.
59 //
60 struct _InternalValueType
61 {
62 _InternalValueType() {}
63
64 _InternalValueType(const Key &k, const Data &d)
65 : _value(k, d) {}
66
67 _InternalValueType &operator=(const _InternalValueType &rhs) {
68
69 if (this != &rhs) {
70 // Since value_type's first member is const we need to
71 // use placement new to put the new element in place. Just
72 // make sure we destruct the element we are about to overwrite.
73 _value.value_type::~value_type();
74 new (&_value) value_type(rhs.GetValue());
75 }
76
77 return *this;
78 }
79
80 value_type &GetValue() {
81 return _value;
82 }
83
84 const value_type &GetValue() const {
85 return _value;
86 }
87
88 void swap(_InternalValueType &rhs) {
89 using std::swap;
90
91 // We do this in order to take advantage of a potentially fast
92 // swap implementation.
93 Key tmp = _value.first;
94
95 _value.first.Key::~Key();
96 new (const_cast<Key *>(&_value.first)) Key(rhs._value.first);
97
98 rhs._value.first.Key::~Key();
99 new (const_cast<Key *>(&rhs._value.first)) Key(tmp);
100
101 swap(_value.second, rhs._value.second);
102 }
103
104 private:
105
106 // Data hold by _InternalValueType. Note that the key portion of
107 // value_type maybe const.
108 value_type _value;
109 };
110
111 // The vector type holding all data for this dense hash map.
112 typedef std::vector<_InternalValueType> _Vector;
113
114 // The hash map used when the map holds more than Threshold elements.
115 typedef TfHashMap<Key, size_t, HashFn, EqualKey> _HashMap;
116
117 // Note that we don't just use _Vector::iterator for accessing elements
118 // of the TfDenseHashMap. This is because the vector's iterator would
119 // expose the _InternalValueType _including_ its assignment operator
120 // that allows overwriting keys.
121 //
122 // Clearly not a good thing.
123 //
124 // Therefore we create an iterator that uses the map's value_type as
125 // externally visible type.
126 //
127 template <class ElementType, class UnderlyingIterator>
128 class _IteratorBase
129 {
130 public:
131 using iterator_category = std::bidirectional_iterator_tag;
132 using value_type = ElementType;
133 using reference = ElementType&;
134 using pointer = ElementType*;
135 using difference_type = typename UnderlyingIterator::difference_type;
136
137 // Empty ctor.
138 _IteratorBase() = default;
139
140 // Allow conversion of an iterator to a const_iterator.
141 template<class OtherIteratorType>
142 _IteratorBase(const OtherIteratorType &rhs)
143 : _iter(rhs._GetUnderlyingIterator()) {}
144
145 reference operator*() const { return dereference(); }
146 pointer operator->() const { return &(dereference()); }
147
148 _IteratorBase& operator++() {
149 increment();
150 return *this;
151 }
152
153 _IteratorBase& operator--() {
154 decrement();
155 return *this;
156 }
157
158 _IteratorBase operator++(int) {
159 _IteratorBase result(*this);
160 increment();
161 return result;
162 }
163
164 _IteratorBase operator--(int) {
165 _IteratorBase result(*this);
166 decrement();
167 return result;
168 }
169
170 template <class OtherIteratorType>
171 bool operator==(const OtherIteratorType& other) const {
172 return equal(other);
173 }
174
175 template <class OtherIteratorType>
176 bool operator!=(const OtherIteratorType& other) const {
177 return !equal(other);
178 }
179
180 private:
181
182 friend class TfDenseHashMap;
183
184 // Ctor from an underlying iterator.
185 _IteratorBase(const UnderlyingIterator &iter)
186 : _iter(iter) {}
187
188 template<class OtherIteratorType>
189 bool equal(const OtherIteratorType &rhs) const {
190 return _iter == rhs._iter;
191 }
192
193 void increment() {
194 ++_iter;
195 }
196
197 void decrement() {
198 --_iter;
199 }
200
201 ElementType &dereference() const {
202 // The dereference() method accesses the correct value_type (ie.
203 // the one with potentially const key_type. This way, clients don't
204 // see the assignment operator of _InternalValueType.
205 return _iter->GetValue();
206 }
207
208 UnderlyingIterator _GetUnderlyingIterator() const {
209 return _iter;
210 }
211
212 private:
213
214 UnderlyingIterator _iter;
215 };
216
218
219public:
220
223 typedef
224 _IteratorBase<value_type, typename _Vector::iterator>
226
229 typedef
230 _IteratorBase<const value_type, typename _Vector::const_iterator>
232
234 typedef std::pair<iterator, bool> insert_result;
235
236public:
237
241 const HashFn &hashFn = HashFn(),
242 const EqualKey &equalKey = EqualKey())
243 {
244 _hash() = hashFn;
245 _equ() = equalKey;
246 }
247
250 template <class Iterator>
251 TfDenseHashMap(Iterator begin, Iterator end) {
252 insert(begin, end);
253 }
254
257 TfDenseHashMap(std::initializer_list<value_type> l) {
258 insert(l.begin(), l.end());
259 }
260
264 : _storage(rhs._storage) {
265 if (rhs._h) {
266 _h = std::make_unique<_HashMap>(*rhs._h);
267 }
268 }
272
276 if (this != &rhs) {
277 TfDenseHashMap temp(rhs);
278 temp.swap(*this);
279 }
280 return *this;
281 }
282
286
289 TfDenseHashMap &operator=(std::initializer_list<value_type> l) {
290 clear();
291 insert(l.begin(), l.end());
292 return *this;
293 }
294
297 bool operator==(const TfDenseHashMap &rhs) const {
298
299 if (size() != rhs.size())
300 return false;
301
302 //XXX: Should we compare the HashFn and EqualKey too?
303 const_iterator tend = end(), rend = rhs.end();
304
305 for(const_iterator iter = begin(); iter != tend; ++iter) {
306 const_iterator riter = rhs.find(iter->first);
307 if (riter == rend)
308 return false;
309 if (iter->second != riter->second)
310 return false;
311 }
312
313 return true;
314 }
315
316 bool operator!=(const TfDenseHashMap &rhs) const {
317 return !(*this == rhs);
318 }
319
322 void clear() {
323 _vec().clear();
324 _h.reset();
325 }
326
329 void swap(TfDenseHashMap &rhs) {
330 _storage.swap(rhs._storage);
331 _h.swap(rhs._h);
332 }
333
336 bool empty() const {
337 return _vec().empty();
338 }
339
342 size_t size() const {
343 return _vec().size();
344 }
345
349 return _vec().begin();
350 }
351
355 return _vec().end();
356 }
357
361 return _vec().begin();
362 }
363
367 return _vec().end();
368 }
369
373 return _vec().cbegin();
374 }
375
379 return _vec().cbegin();
380 }
381
384 pointer data() {
385 return _vec().empty() ? nullptr : &_vec().front().GetValue();
386 }
387
390 const_pointer data() const {
391 return _vec().empty() ? nullptr : &_vec().front().GetValue();
392 }
393
396 const_pointer cdata() const {
397 return _vec().empty() ? nullptr : &_vec().front().GetValue();
398 }
399
402 iterator find(const key_type &k) {
403 if (_h) {
404 typename _HashMap::const_iterator iter = _h->find(k);
405 if (iter == _h->end())
406 return end();
407
408 return _vec().begin() + iter->second;
409 } else {
410 return _FindInVec(k);
411 }
412 }
413
416 const_iterator find(const key_type &k) const {
417 if (_h) {
418 typename _HashMap::const_iterator iter = _h->find(k);
419 if (iter == _h->end())
420 return end();
421
422 return _vec().begin() + iter->second;
423 } else {
424 return _FindInVec(k);
425 }
426 }
427
430 size_t count(const key_type &k) const {
431 return find(k) != end();
432 }
433
437 insert_result insert(const value_type &v) {
438 if (_h) {
439 // Attempt to insert the new index. If this fails, we can't insert
440 // v.
441 std::pair<typename _HashMap::iterator, bool> res =
442 _h->insert(std::make_pair(v.first, size()));
443
444 if (!res.second)
445 return insert_result(_vec().begin() + res.first->second, false);
446 } else {
447 // Bail if already inserted.
448 iterator iter = _FindInVec(v.first);
449 if (iter != end())
450 return insert_result(iter, false);
451 }
452
453 // Insert at end and create table if necessary.
454 _vec().push_back(_InternalValueType(v.first, v.second));
455 _CreateTableIfNeeded();
456
457 return insert_result(std::prev(end()), true);
458 }
459
463 template<class IteratorType>
464 void insert(IteratorType i0, IteratorType i1) {
465 // Assume elements are more often than not unique, so if the sum of the
466 // current size and the size of the range is greater than or equal to
467 // the threshold, we create the table immediately so we don't do m*n
468 // work before creating the table.
469 if (size() + std::distance(i0, i1) >= Threshold)
470 _CreateTable();
471
472 // Insert elements.
473 for(IteratorType iter = i0; iter != i1; ++iter)
474 insert(*iter);
475 }
476
480 template <class Iterator>
481 void insert_unique(Iterator begin, Iterator end) {
482 // Special-case empty container.
483 if (empty()) {
484 _vec().assign(begin, end);
485 _CreateTableIfNeeded();
486 } else {
487 // Just insert, since duplicate checking will use the hash.
488 insert(begin, end);
489 }
490 }
491
497 Data &operator[](const key_type &key) {
498 return insert(value_type(key, Data())).first->second;
499 }
500
503 size_t erase(const key_type &k) {
504
505 iterator iter = find(k);
506 if (iter != end()) {
507 erase(iter);
508 return 1;
509 }
510 return 0;
511 }
512
515 void erase(const iterator &iter) {
516
517 // Erase key from hash table if applicable.
518 if (_h)
519 _h->erase(iter->first);
520
521 // If we are not removing that last element...
522 if (iter != std::prev(end())) {
523
524 // Need to get the underlying vector iterator directly, because
525 // we want to operate on the vector.
526 typename _Vector::iterator vi = iter._GetUnderlyingIterator();
527
528 // ... move the last element into the erased placed.
529 vi->swap(_vec().back());
530
531 // ... and update the moved element's index.
532 if (_h)
533 (*_h)[vi->GetValue().first] = vi - _vec().begin();
534 }
535
536 _vec().pop_back();
537 }
538
541 void erase(iterator i0, iterator i1) {
542
543 if (_h) {
544 for(iterator iter = i0; iter != i1; ++iter)
545 _h->erase(iter->first);
546 }
547
548 typename _Vector::const_iterator vremain = _vec().erase(
549 i0._GetUnderlyingIterator(), i1._GetUnderlyingIterator());
550
551 if (_h) {
552 for(; vremain != _vec().end(); ++vremain)
553 (*_h)[vremain->GetValue().first] = vremain - _vec().begin();
554 }
555 }
556
560
561 // Shrink the vector to best size.
562 _vec().shrink_to_fit();
563
564 if (!_h)
565 return;
566
567 size_t sz = size();
568
569 // If we have a hash map and are underneath the threshold, discard it.
570 if (sz < Threshold) {
571
572 _h.reset();
573
574 } else {
575
576 // Otherwise, allocate a new hash map with the optimal size.
577 _h.reset(new _HashMap(sz, _hash(), _equ()));
578 for(size_t i=0; i<sz; ++i)
579 _h->insert(std::make_pair(_vec()[i].GetValue().first, i));
580 }
581 }
582
585 void reserve(size_t n) {
586 _vec().reserve(n);
587 }
588
590
591private:
592
593 // Helper to access the storage vector.
594 _Vector &_vec() {
595 return _storage.vector;
596 }
597
598 // Helper to access the hash functor.
599 HashFn &_hash() {
600 return _storage;
601 }
602
603 // Helper to access the equality functor.
604 EqualKey &_equ() {
605 return _storage;
606 }
607
608 // Helper to access the storage vector.
609 const _Vector &_vec() const {
610 return _storage.vector;
611 }
612
613 // Helper to access the hash functor.
614 const HashFn &_hash() const {
615 return _storage;
616 }
617
618 // Helper to access the equality functor.
619 const EqualKey &_equ() const {
620 return _storage;
621 }
622
623 // Helper to linear-search the vector for a key.
624 inline iterator _FindInVec(const key_type &k) {
625 _Vector &vec = _vec();
626 EqualKey &equ = _equ();
627 typename _Vector::iterator iter = vec.begin(), end = vec.end();
628 for (; iter != end; ++iter) {
629 if (equ(iter->GetValue().first, k))
630 break;
631 }
632 return iter;
633 }
634
635 // Helper to linear-search the vector for a key.
636 inline const_iterator _FindInVec(const key_type &k) const {
637 _Vector const &vec = _vec();
638 EqualKey const &equ = _equ();
639 typename _Vector::const_iterator iter = vec.begin(), end = vec.end();
640 for (; iter != end; ++iter) {
641 if (equ(iter->GetValue().first, k))
642 break;
643 }
644 return iter;
645 }
646
647 // Helper to create the acceleration table if size dictates.
648 inline void _CreateTableIfNeeded() {
649 if (size() >= Threshold) {
650 _CreateTable();
651 }
652 }
653
654 // Unconditionally create the acceleration table if it doesn't already
655 // exist.
656 inline void _CreateTable() {
657 if (!_h) {
658 _h.reset(new _HashMap(Threshold, _hash(), _equ()));
659 for(size_t i=0; i < size(); ++i)
660 _h->insert(std::make_pair(_vec()[i].GetValue().first, i));
661 }
662 }
663
664 // Since sizeof(EqualKey) == 0 and sizeof(HashFn) == 0 in many cases
665 // we use the empty base optimization to not pay a size penalty.
666 // In C++20, explore using [[no_unique_address]] as an alternative
667 // way to get this optimization.
668 struct ARCH_EMPTY_BASES _CompressedStorage :
669 private EqualKey, private HashFn {
670 static_assert(!std::is_same<EqualKey, HashFn>::value,
671 "EqualKey and HashFn must be distinct types.");
672 _CompressedStorage() = default;
673 _CompressedStorage(const EqualKey& equalKey, const HashFn& hashFn)
674 : EqualKey(equalKey), HashFn(hashFn) {}
675
676 void swap(_CompressedStorage& other) {
677 using std::swap;
678 vector.swap(other.vector);
679 swap(static_cast<EqualKey&>(*this), static_cast<EqualKey&>(other));
680 swap(static_cast<HashFn&>(*this), static_cast<HashFn&>(other));
681 }
682 _Vector vector;
683 friend class TfDenseHashMap;
684 };
685 _CompressedStorage _storage;
686
687 // Optional hash map that maps from keys to vector indices.
688 std::unique_ptr<_HashMap> _h;
689};
690
691PXR_NAMESPACE_CLOSE_SCOPE
692
693#endif // PXR_BASE_TF_DENSE_HASH_MAP_H
Define function attributes.
#define ARCH_EMPTY_BASES
Macro to begin the definition of a class that is using private inheritance to take advantage of the e...
Definition attributes.h:147
A hash map with contiguous storage, suitable for use with TfSpan, e.g.
Data & operator[](const key_type &key)
Indexing operator.
_IteratorBase< value_type, typename _Vector::iterator > iterator
An iterator type for this map.
const_pointer cdata() const
Returns a const pointer to the map's data.
size_t size() const
Returns the size of the map.
TfDenseHashMap(std::initializer_list< value_type > l)
Construct from an initializer_list.
const_iterator begin() const
Returns a const_iterator pointing to the beginning of the map.
TfDenseHashMap(const HashFn &hashFn=HashFn(), const EqualKey &equalKey=EqualKey())
Ctor.
const_pointer data() const
Returns a const pointer to the map's data.
pointer data()
Returns a pointer to the map's data.
const_iterator cbegin() const
Returns a const_iterator pointing to the beginning of the map.
size_t count(const key_type &k) const
Returns the number of elements with key k.
size_t erase(const key_type &k)
Erase element with key k.
const_iterator find(const key_type &k) const
Finds the element with key k.
void insert(IteratorType i0, IteratorType i1)
Insert a range into the hash map.
iterator find(const key_type &k)
Finds the element with key k.
TfDenseHashMap & operator=(TfDenseHashMap &&rhs)=default
Move assignment operator.
void shrink_to_fit()
Optimize storage space.
bool empty() const
true if the map's size is 0.
bool operator==(const TfDenseHashMap &rhs) const
Equality operator.
void erase(const iterator &iter)
Erases element pointed to by iter.
_IteratorBase< const value_type, typename _Vector::const_iterator > const_iterator
An iterator type for this map.
TfDenseHashMap(Iterator begin, Iterator end)
Construct with range.
const_iterator cend() const
Returns a const_iterator pointing to the end of the map.
TfDenseHashMap & operator=(const TfDenseHashMap &rhs)
Copy assignment operator.
insert_result insert(const value_type &v)
Returns a pair of <iterator, bool> where iterator points to the element in the list and bool is true ...
void clear()
Erases all of the elements.
iterator end()
Returns an iterator pointing to the end of the map.
void insert_unique(Iterator begin, Iterator end)
Insert a range of unique elements into the container.
const_iterator end() const
Returns a const_iterator pointing to the end of the map.
std::pair< iterator, bool > insert_result
Return type for insert() method.
void swap(TfDenseHashMap &rhs)
Swaps the contents of two maps.
iterator begin()
Returns an iterator pointing to the beginning of the map.
void erase(iterator i0, iterator i1)
Erases a range from the map.
TfDenseHashMap(const TfDenseHashMap &rhs)
Copy Ctor.
TfDenseHashMap(TfDenseHashMap &&rhs)=default
Move Ctor.
void reserve(size_t n)
Reserve space.
TfDenseHashMap & operator=(std::initializer_list< value_type > l)
Assignment from an initializer_list.