All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 <vector>
18
19PXR_NAMESPACE_OPEN_SCOPE
20
32template <
33 class Element,
34 class HashFn,
35 class EqualElement = std::equal_to<Element>,
36 unsigned Threshold = 128
37>
39{
40public:
41
42 typedef Element value_type;
43
45
46private:
47
48 // The vector type holding all data for this dense hash set.
49 typedef std::vector<Element> _Vector;
50
51 // The hash map used when the map holds more than Threshold elements.
52 typedef TfHashMap<Element, size_t, HashFn, EqualElement> _HashMap;
53
55
56public:
57
61 typedef typename _Vector::const_iterator iterator;
62
64 typedef typename _Vector::const_iterator const_iterator;
65
67 typedef std::pair<const_iterator, bool> insert_result;
68
69public:
70
74 const HashFn &hashFn = HashFn(),
75 const EqualElement &equalElement = EqualElement())
76 {
77 _hash() = hashFn;
78 _equ() = equalElement;
79 }
80
84 : _storage(rhs._storage) {
85 if (rhs._h) {
86 _h = std::make_unique<_HashMap>(*rhs._h);
87 }
88 }
89
93
96 template <class Iterator>
97 TfDenseHashSet(Iterator begin, Iterator end) {
99 }
100
103 TfDenseHashSet(std::initializer_list<Element> l) {
104 insert(l.begin(), l.end());
105 }
106
110 if (this != &rhs) {
111 TfDenseHashSet temp(rhs);
112 temp.swap(*this);
113 }
114 return *this;
115 }
116
120
123 TfDenseHashSet &operator=(std::initializer_list<Element> l) {
124 clear();
125 insert(l.begin(), l.end());
126 return *this;
127 }
128
131 bool operator==(const TfDenseHashSet &rhs) const {
132
133 if (size() != rhs.size())
134 return false;
135
136 //XXX: Should we compare the HashFn and EqualElement too?
137 const_iterator tend = end();
138
139 for(const_iterator iter = begin(); iter != tend; ++iter) {
140 if (!rhs.count(*iter))
141 return false;
142 }
143
144 return true;
145 }
146
147 bool operator!=(const TfDenseHashSet &rhs) const {
148 return !(*this == rhs);
149 }
150
153 void clear() {
154 _vec().clear();
155 _h.reset();
156 }
157
160 void swap(TfDenseHashSet &rhs) {
161 _storage.swap(rhs._storage);
162 _h.swap(rhs._h);
163 }
164
167 bool empty() const {
168 return _vec().empty();
169 }
170
173 size_t size() const {
174 return _vec().size();
175 }
176
180 return _vec().begin();
181 }
182
186 return _vec().end();
187 }
188
191 const_iterator find(const Element &k) const {
192
193 if (_h) {
194 typename _HashMap::const_iterator iter = _h->find(k);
195 if (iter == _h->end())
196 return end();
197
198 return _vec().begin() + iter->second;
199 }
200
201 typename _Vector::const_iterator iter, end = _vec().end();
202
203 for(iter = _vec().begin(); iter != end; ++iter)
204 if (_equ()(*iter, k))
205 break;
206
207 return iter;
208 }
209
212 size_t count(const Element &k) const {
213 return find(k) != end();
214 }
215
219 insert_result insert(const value_type &v) {
220
221 if (_h) {
222
223 // Attempt to insert the new index. If this fails, we can't
224 // insert v.
225
226 std::pair<typename _HashMap::iterator, bool> res =
227 _h->insert(std::make_pair(v, size()));
228
229 if (!res.second)
230 return insert_result(_vec().begin() + res.first->second, false);
231
232 } else {
233
234 // Bail if already inserted.
235 const_iterator iter = find(v);
236 if (iter != end())
237 return insert_result(iter, false);
238 }
239
240 // Insert at end and create table if necessary.
241 _vec().push_back(v);
242 _CreateTableIfNeeded();
243
244 return insert_result(std::prev(end()), true);
245 }
246
250 template<class IteratorType>
251 void insert(IteratorType i0, IteratorType i1) {
252 // Assume elements are more often than not unique, so if the sum of the
253 // current size and the size of the range is greater than or equal to
254 // the threshold, we create the table immediately so we don't do m*n
255 // work before creating the table.
256 if (size() + std::distance(i0, i1) >= Threshold)
257 _CreateTable();
258
259 // Insert elements.
260 for (IteratorType iter = i0; iter != i1; ++iter)
261 insert(*iter);
262 }
263
267 template <class Iterator>
268 void insert_unique(Iterator begin, Iterator end) {
269 // Special-case empty container.
270 if (empty()) {
271 _vec().assign(begin, end);
272 _CreateTableIfNeeded();
273 } else {
274 // Just insert, since duplicate checking will use the hash.
275 insert(begin, end);
276 }
277 }
278
281 size_t erase(const Element &k) {
282
283 const_iterator iter = find(k);
284 if (iter != end()) {
285 erase(iter);
286 return 1;
287 }
288 return 0;
289 }
290
293 void erase(const iterator &iter) {
294
295 // Erase key from hash table if applicable.
296 if (_h)
297 _h->erase(*iter);
298
299 // If we are not removing that last element...
300 if (iter != std::prev(end())) {
301 using std::swap;
302
303 // ... move the last element into the erased placed.
304 // Note that we can cast constness away because we explicitly update
305 // the TfHashMap _h below.
306 swap(*const_cast<Element *>(&(*iter)), _vec().back());
307
308 // ... and update the moved element's index.
309 if (_h)
310 (*_h)[*iter] = iter - _vec().begin();
311 }
312
313 _vec().pop_back();
314 }
315
318 void erase(const iterator &i0, const iterator &i1) {
319
320 if (_h) {
321 for(const_iterator iter = i0; iter != i1; ++iter)
322 _h->erase(iter->first);
323 }
324
325 const_iterator vremain = _vec().erase(i0, i1);
326
327 if (_h) {
328 for(; vremain != _vec().end(); ++vremain)
329 (*_h)[*vremain] = vremain - _vec().begin();
330 }
331 }
332
336
337 // Shrink the vector to best size.
338 _vec().shrink_to_fit();
339
340 if (!_h)
341 return;
342
343 size_t sz = size();
344
345 // If we have a hash map and are underneath the threshold, discard it.
346 if (sz < Threshold) {
347
348 _h.reset();
349
350 } else {
351
352 // Otherwise, allocate a new hash map with the optimal size.
353 _h.reset(new _HashMap(sz, _hash(), _equ()));
354 for(size_t i=0; i<sz; ++i)
355 (*_h)[_vec()[i]] = i;
356 }
357 }
358
361 const Element &operator[](size_t index) const {
362 TF_VERIFY(index < size());
363 return _vec()[index];
364 }
365
367
368private:
369
370 // Helper to access the storage vector.
371 _Vector &_vec() {
372 return _storage.vector;
373 }
374
375 // Helper to access the hash functor.
376 HashFn &_hash() {
377 return _storage;
378 }
379
380 // Helper to access the equality functor.
381 EqualElement &_equ() {
382 return _storage;
383 }
384
385 // Helper to access the storage vector.
386 const _Vector &_vec() const {
387 return _storage.vector;
388 }
389
390 // Helper to access the hash functor.
391 const HashFn &_hash() const {
392 return _storage;
393 }
394
395 // Helper to access the equality functor.
396 const EqualElement &_equ() const {
397 return _storage;
398 }
399
400 // Helper to create the acceleration table if size dictates.
401 inline void _CreateTableIfNeeded() {
402 if (size() >= Threshold) {
403 _CreateTable();
404 }
405 }
406
407 // Unconditionally create the acceleration table if it doesn't already
408 // exist.
409 inline void _CreateTable() {
410 if (!_h) {
411 _h.reset(new _HashMap(Threshold, _hash(), _equ()));
412 for(size_t i=0; i < size(); ++i)
413 (*_h)[_vec()[i]] = i;
414 }
415 }
416
417 // Since sizeof(EqualElement) == 0 and sizeof(HashFn) == 0 in many cases
418 // we use the empty base optimization to not pay a size penalty.
419 // In C++20, explore using [[no_unique_address]] as an alternative
420 // way to get this optimization.
421 struct ARCH_EMPTY_BASES _CompressedStorage :
422 private EqualElement, private HashFn {
423 static_assert(!std::is_same<EqualElement, HashFn>::value,
424 "EqualElement and HashFn must be distinct types.");
425 _CompressedStorage() = default;
426 _CompressedStorage(const EqualElement& equal, const HashFn& hashFn)
427 : EqualElement(equal), HashFn(hashFn) {}
428
429 void swap(_CompressedStorage& other) {
430 using std::swap;
431 vector.swap(other.vector);
432 swap(static_cast<EqualElement&>(*this),
433 static_cast<EqualElement&>(other));
434 swap(static_cast<HashFn&>(*this), static_cast<HashFn&>(other));
435 }
436 _Vector vector;
437 friend class TfDenseHashSet;
438 };
439 _CompressedStorage _storage;
440
441 // Optional hash map that maps from keys to vector indices.
442 std::unique_ptr<_HashMap> _h;
443};
444
445PXR_NAMESPACE_CLOSE_SCOPE
446
447#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:153
This is a space efficient container that mimics the TfHashSet API that uses a vector for storage when...
Definition: denseHashSet.h:39
size_t erase(const Element &k)
Erase element with key k.
Definition: denseHashSet.h:281
size_t size() const
Returns the size of the set.
Definition: denseHashSet.h:173
const_iterator begin() const
Returns an const_iterator pointing to the beginning of the set.
Definition: denseHashSet.h:179
TfDenseHashSet(TfDenseHashSet &&rhs)=default
Move Ctor.
TfDenseHashSet(Iterator begin, Iterator end)
Construct from range.
Definition: denseHashSet.h:97
const Element & operator[](size_t index) const
Index into set via index.
Definition: denseHashSet.h:361
void insert(IteratorType i0, IteratorType i1)
Insert a range into the hash set.
Definition: denseHashSet.h:251
size_t count(const Element &k) const
Returns the number of elements with key k.
Definition: denseHashSet.h:212
TfDenseHashSet(const HashFn &hashFn=HashFn(), const EqualElement &equalElement=EqualElement())
Ctor.
Definition: denseHashSet.h:73
void shrink_to_fit()
Optimize storage space.
Definition: denseHashSet.h:335
bool empty() const
true if the set's size is 0.
Definition: denseHashSet.h:167
void erase(const iterator &i0, const iterator &i1)
Erases a range from the set.
Definition: denseHashSet.h:318
void erase(const iterator &iter)
Erases element pointed to by iter.
Definition: denseHashSet.h:293
TfDenseHashSet & operator=(TfDenseHashSet &&rhs)=default
Move assignment operator.
void swap(TfDenseHashSet &rhs)
Swaps the contents of two sets.
Definition: denseHashSet.h:160
std::pair< const_iterator, bool > insert_result
Return type for insert() method.
Definition: denseHashSet.h:67
const_iterator find(const Element &k) const
Finds the element with key k.
Definition: denseHashSet.h:191
TfDenseHashSet(std::initializer_list< Element > l)
Construct from an initializer_list.
Definition: denseHashSet.h:103
_Vector::const_iterator const_iterator
A const_iterator type for this set.
Definition: denseHashSet.h:64
bool operator==(const TfDenseHashSet &rhs) const
Equality operator.
Definition: denseHashSet.h:131
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 ...
Definition: denseHashSet.h:219
void clear()
Erases all of the elements.
Definition: denseHashSet.h:153
TfDenseHashSet & operator=(const TfDenseHashSet &rhs)
Copy assignment operator.
Definition: denseHashSet.h:109
void insert_unique(Iterator begin, Iterator end)
Insert a range of unique elements into the container.
Definition: denseHashSet.h:268
const_iterator end() const
Returns an const_iterator pointing to the end of the set.
Definition: denseHashSet.h:185
_Vector::const_iterator iterator
An iterator type for this set.
Definition: denseHashSet.h:61
TfDenseHashSet(const TfDenseHashSet &rhs)
Copy Ctor.
Definition: denseHashSet.h:83
TfDenseHashSet & operator=(std::initializer_list< Element > l)
Assignment from an initializer_list.
Definition: denseHashSet.h:123
#define TF_VERIFY(cond, format,...)
Checks a condition and reports an error if it evaluates false.
Definition: diagnostic.h:266