Loading...
Searching...
No Matches
denseHashSet.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_SET_H
8#define PXR_BASE_TF_DENSE_HASH_SET_H
9
11
12#include "pxr/pxr.h"
14#include "pxr/base/tf/hashmap.h"
15
16#include <memory>
17#include <utility>
18#include <vector>
19
20PXR_NAMESPACE_OPEN_SCOPE
21
33template <
34 class Element,
35 class HashFn,
36 class EqualElement = std::equal_to<Element>,
37 unsigned Threshold = 128
38>
40{
41public:
42
43 using value_type = Element;
44 using pointer = value_type*;
45 using const_pointer = const value_type*;
46
48
49private:
50
51 // The vector type holding all data for this dense hash set.
52 typedef std::vector<Element> _Vector;
53
54 // The hash map used when the map holds more than Threshold elements.
55 typedef TfHashMap<Element, size_t, HashFn, EqualElement> _HashMap;
56
58
59public:
60
64 typedef typename _Vector::const_iterator iterator;
65
67 typedef typename _Vector::const_iterator const_iterator;
68
70 typedef std::pair<const_iterator, bool> insert_result;
71
72public:
73
77 const HashFn &hashFn = HashFn(),
78 const EqualElement &equalElement = EqualElement())
79 {
80 _hash() = hashFn;
81 _equ() = equalElement;
82 }
83
87 : _storage(rhs._storage) {
88 if (rhs._h) {
89 _h = std::make_unique<_HashMap>(*rhs._h);
90 }
91 }
92
96
99 template <class Iterator>
100 TfDenseHashSet(Iterator begin, Iterator end) {
101 insert(begin, end);
102 }
103
106 TfDenseHashSet(std::initializer_list<Element> l) {
107 insert(l.begin(), l.end());
108 }
109
113 if (this != &rhs) {
114 TfDenseHashSet temp(rhs);
115 temp.swap(*this);
116 }
117 return *this;
118 }
119
123
126 TfDenseHashSet &operator=(std::initializer_list<Element> l) {
127 clear();
128 insert(l.begin(), l.end());
129 return *this;
130 }
131
134 bool operator==(const TfDenseHashSet &rhs) const {
135
136 if (size() != rhs.size())
137 return false;
138
139 //XXX: Should we compare the HashFn and EqualElement too?
140 const_iterator tend = end();
141
142 for(const_iterator iter = begin(); iter != tend; ++iter) {
143 if (!rhs.count(*iter))
144 return false;
145 }
146
147 return true;
148 }
149
150 bool operator!=(const TfDenseHashSet &rhs) const {
151 return !(*this == rhs);
152 }
153
156 void clear() {
157 _vec().clear();
158 _h.reset();
159 }
160
163 void swap(TfDenseHashSet &rhs) {
164 _storage.swap(rhs._storage);
165 _h.swap(rhs._h);
166 }
167
170 bool empty() const {
171 return _vec().empty();
172 }
173
176 size_t size() const {
177 return _vec().size();
178 }
179
183 return _vec().begin();
184 }
185
189 return _vec().end();
190 }
191
195 return _vec().cbegin();
196 }
197
201 return _vec().cend();
202 }
203
206 pointer data() {
207 return _vec().empty() ? nullptr : &_vec().front();
208 }
209
212 const_pointer data() const {
213 return _vec().empty() ? nullptr : &_vec().front();
214 }
215
218 const_pointer cdata() const {
219 return _vec().empty() ? nullptr : &_vec().front();
220 }
221
224 const_iterator find(const Element &k) const {
225
226 if (_h) {
227 typename _HashMap::const_iterator iter = _h->find(k);
228 if (iter == _h->end())
229 return end();
230
231 return _vec().begin() + iter->second;
232 }
233
234 typename _Vector::const_iterator iter, end = _vec().end();
235
236 for(iter = _vec().begin(); iter != end; ++iter)
237 if (_equ()(*iter, k))
238 break;
239
240 return iter;
241 }
242
245 size_t count(const Element &k) const {
246 return find(k) != end();
247 }
248
252 insert_result insert(const value_type &v) {
253
254 if (_h) {
255
256 // Attempt to insert the new index. If this fails, we can't
257 // insert v.
258
259 std::pair<typename _HashMap::iterator, bool> res =
260 _h->insert(std::make_pair(v, size()));
261
262 if (!res.second)
263 return insert_result(_vec().begin() + res.first->second, false);
264
265 } else {
266
267 // Bail if already inserted.
268 const_iterator iter = find(v);
269 if (iter != end())
270 return insert_result(iter, false);
271 }
272
273 // Insert at end and create table if necessary.
274 _vec().push_back(v);
275 _CreateTableIfNeeded();
276
277 return insert_result(std::prev(end()), true);
278 }
279
283 template<class IteratorType>
284 void insert(IteratorType i0, IteratorType i1) {
285 // Assume elements are more often than not unique, so if the sum of the
286 // current size and the size of the range is greater than or equal to
287 // the threshold, we create the table immediately so we don't do m*n
288 // work before creating the table.
289 if (size() + std::distance(i0, i1) >= Threshold)
290 _CreateTable();
291
292 // Insert elements.
293 for (IteratorType iter = i0; iter != i1; ++iter)
294 insert(*iter);
295 }
296
300 template <class Iterator>
301 void insert_unique(Iterator begin, Iterator end) {
302 // Special-case empty container.
303 if (empty()) {
304 _vec().assign(begin, end);
305 _CreateTableIfNeeded();
306 } else {
307 // Just insert, since duplicate checking will use the hash.
308 insert(begin, end);
309 }
310 }
311
314 size_t erase(const Element &k) {
315
316 const_iterator iter = find(k);
317 if (iter != end()) {
318 erase(iter);
319 return 1;
320 }
321 return 0;
322 }
323
326 void erase(const iterator &iter) {
327
328 // Erase key from hash table if applicable.
329 if (_h)
330 _h->erase(*iter);
331
332 // If we are not removing that last element...
333 if (iter != std::prev(end())) {
334 using std::swap;
335
336 // ... move the last element into the erased placed.
337 // Note that we can cast constness away because we explicitly update
338 // the TfHashMap _h below.
339 swap(*const_cast<Element *>(&(*iter)), _vec().back());
340
341 // ... and update the moved element's index.
342 if (_h)
343 (*_h)[*iter] = iter - _vec().begin();
344 }
345
346 _vec().pop_back();
347 }
348
351 void erase(const iterator &i0, const iterator &i1) {
352
353 if (_h) {
354 for(const_iterator iter = i0; iter != i1; ++iter)
355 _h->erase(*iter);
356 }
357
358 const_iterator vremain = _vec().erase(i0, i1);
359
360 if (_h) {
361 for(; vremain != _vec().end(); ++vremain)
362 (*_h)[*vremain] = vremain - _vec().begin();
363 }
364 }
365
369
370 // Shrink the vector to best size.
371 _vec().shrink_to_fit();
372
373 if (!_h)
374 return;
375
376 size_t sz = size();
377
378 // If we have a hash map and are underneath the threshold, discard it.
379 if (sz < Threshold) {
380
381 _h.reset();
382
383 } else {
384
385 // Otherwise, allocate a new hash map with the optimal size.
386 _h.reset(new _HashMap(sz, _hash(), _equ()));
387 for(size_t i=0; i<sz; ++i)
388 (*_h)[_vec()[i]] = i;
389 }
390 }
391
394 const Element &operator[](size_t index) const {
395 TF_VERIFY(index < size());
396 return _vec()[index];
397 }
398
400
401private:
402
403 // Helper to access the storage vector.
404 _Vector &_vec() {
405 return _storage.vector;
406 }
407
408 // Helper to access the hash functor.
409 HashFn &_hash() {
410 return _storage;
411 }
412
413 // Helper to access the equality functor.
414 EqualElement &_equ() {
415 return _storage;
416 }
417
418 // Helper to access the storage vector.
419 const _Vector &_vec() const {
420 return _storage.vector;
421 }
422
423 // Helper to access the hash functor.
424 const HashFn &_hash() const {
425 return _storage;
426 }
427
428 // Helper to access the equality functor.
429 const EqualElement &_equ() const {
430 return _storage;
431 }
432
433 // Helper to create the acceleration table if size dictates.
434 inline void _CreateTableIfNeeded() {
435 if (size() >= Threshold) {
436 _CreateTable();
437 }
438 }
439
440 // Unconditionally create the acceleration table if it doesn't already
441 // exist.
442 inline void _CreateTable() {
443 if (!_h) {
444 _h.reset(new _HashMap(Threshold, _hash(), _equ()));
445 for(size_t i=0; i < size(); ++i)
446 (*_h)[_vec()[i]] = i;
447 }
448 }
449
450 // Since sizeof(EqualElement) == 0 and sizeof(HashFn) == 0 in many cases
451 // we use the empty base optimization to not pay a size penalty.
452 // In C++20, explore using [[no_unique_address]] as an alternative
453 // way to get this optimization.
454 struct ARCH_EMPTY_BASES _CompressedStorage :
455 private EqualElement, private HashFn {
456 static_assert(!std::is_same<EqualElement, HashFn>::value,
457 "EqualElement and HashFn must be distinct types.");
458 _CompressedStorage() = default;
459 _CompressedStorage(const EqualElement& equal, const HashFn& hashFn)
460 : EqualElement(equal), HashFn(hashFn) {}
461
462 void swap(_CompressedStorage& other) {
463 using std::swap;
464 vector.swap(other.vector);
465 swap(static_cast<EqualElement&>(*this),
466 static_cast<EqualElement&>(other));
467 swap(static_cast<HashFn&>(*this), static_cast<HashFn&>(other));
468 }
469 _Vector vector;
470 friend class TfDenseHashSet;
471 };
472 _CompressedStorage _storage;
473
474 // Optional hash map that maps from keys to vector indices.
475 std::unique_ptr<_HashMap> _h;
476};
477
478PXR_NAMESPACE_CLOSE_SCOPE
479
480#endif // PXR_BASE_TF_DENSE_HASH_SET_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 set with contiguous storage, suitable for use with TfSpan, e.g.
size_t erase(const Element &k)
Erase element with key k.
const_pointer cdata() const
Returns a const pointer to the set's data.
size_t size() const
Returns the size of the set.
const_iterator begin() const
Returns a const_iterator pointing to the beginning of the set.
TfDenseHashSet(TfDenseHashSet &&rhs)=default
Move Ctor.
TfDenseHashSet(Iterator begin, Iterator end)
Construct from range.
const Element & operator[](size_t index) const
Index into set via index.
const_pointer data() const
Returns a const pointer to the set's data.
pointer data()
Returns a pointer to the set's data.
const_iterator cbegin() const
Returns a const_iterator pointing to the beginning of the set.
void insert(IteratorType i0, IteratorType i1)
Insert a range into the hash set.
size_t count(const Element &k) const
Returns the number of elements with key k.
TfDenseHashSet(const HashFn &hashFn=HashFn(), const EqualElement &equalElement=EqualElement())
Ctor.
void shrink_to_fit()
Optimize storage space.
bool empty() const
true if the set's size is 0.
void erase(const iterator &i0, const iterator &i1)
Erases a range from the set.
void erase(const iterator &iter)
Erases element pointed to by iter.
TfDenseHashSet & operator=(TfDenseHashSet &&rhs)=default
Move assignment operator.
_Vector::const_iterator iterator
An iterator type for this set.
void swap(TfDenseHashSet &rhs)
Swaps the contents of two sets.
const_iterator find(const Element &k) const
Finds the element with key k.
TfDenseHashSet(std::initializer_list< Element > l)
Construct from an initializer_list.
const_iterator cend() const
Returns a const_iterator pointing to the end of the set.
std::pair< const_iterator, bool > insert_result
Return type for insert() method.
bool operator==(const TfDenseHashSet &rhs) const
Equality 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.
TfDenseHashSet & operator=(const TfDenseHashSet &rhs)
Copy assignment operator.
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 set.
_Vector::const_iterator const_iterator
A const_iterator type for this set.
TfDenseHashSet(const TfDenseHashSet &rhs)
Copy Ctor.
TfDenseHashSet & operator=(std::initializer_list< Element > l)
Assignment from an initializer_list.
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition diagnostic.h:267