HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
denseHashSet.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_DENSE_HASH_SET_H
25 #define PXR_BASE_TF_DENSE_HASH_SET_H
26 
27 /// \file tf/denseHashSet.h
28 
29 #include "pxr/pxr.h"
31 #include "pxr/base/tf/hashmap.h"
32 
33 #include <memory>
34 #include <vector>
35 
37 
38 /// \class TfDenseHashSet
39 ///
40 /// This is a space efficient container that mimics the TfHashSet API that
41 /// uses a vector for storage when the size of the set is small.
42 ///
43 /// When the set gets bigger than \p Threshold a TfHashMap is allocated
44 /// that is used to accelerate lookup in the vector.
45 ///
46 /// \warning This differs from a TfHashSet in so far that inserting and
47 /// removing elements invalidate all iterators of the container.
48 ///
49 template <
50  class Element,
51  class HashFn,
52  class EqualElement = std::equal_to<Element>,
53  unsigned Threshold = 128
54 >
56 {
57 public:
58 
60 
61 ////////////////////////////////////////////////////////////////////////////////
62 
63 private:
64 
65  // The vector type holding all data for this dense hash set.
66  typedef std::vector<Element> _Vector;
67 
68  // The hash map used when the map holds more than Threshold elements.
70 
71 ////////////////////////////////////////////////////////////////////////////////
72 
73 public:
74 
75  /// An iterator type for this set. Note that this one is const as well,
76  /// as we can't allow in-place modification of elements due to the
77  /// potentially allocated hash map.
78  typedef typename _Vector::const_iterator iterator;
79 
80  /// A const_iterator type for this set.
81  typedef typename _Vector::const_iterator const_iterator;
82 
83  /// Return type for insert() method.
84  typedef std::pair<const_iterator, bool> insert_result;
85 
86 public:
87 
88  /// Ctor.
89  ///
90  explicit TfDenseHashSet(
91  const HashFn &hashFn = HashFn(),
92  const EqualElement &equalElement = EqualElement())
93  {
94  _hash() = hashFn;
95  _equ() = equalElement;
96  }
97 
98  /// Copy Ctor.
99  ///
101  : _storage(rhs._storage) {
102  if (rhs._h) {
103  _h = std::make_unique<_HashMap>(*rhs._h);
104  }
105  }
106 
107  /// Move Ctor.
108  ///
109  TfDenseHashSet(TfDenseHashSet &&rhs) = default;
110 
111  /// Construct from range.
112  ///
113  template <class Iterator>
114  TfDenseHashSet(Iterator begin, Iterator end) {
115  insert(begin, end);
116  }
117 
118  /// Construct from an initializer_list.
119  ///
120  TfDenseHashSet(std::initializer_list<Element> l) {
121  insert(l.begin(), l.end());
122  }
123 
124  /// Copy assignment operator.
125  ///
127  if (this != &rhs) {
128  TfDenseHashSet temp(rhs);
129  temp.swap(*this);
130  }
131  return *this;
132  }
133 
134  /// Move assignment operator.
135  ///
136  TfDenseHashSet &operator=(TfDenseHashSet &&rhs) = default;
137 
138  /// Assignment from an initializer_list.
139  ///
140  TfDenseHashSet &operator=(std::initializer_list<Element> l) {
141  clear();
142  insert(l.begin(), l.end());
143  return *this;
144  }
145 
146  /// Equality operator.
147  ///
148  bool operator==(const TfDenseHashSet &rhs) const {
149 
150  if (size() != rhs.size())
151  return false;
152 
153  //XXX: Should we compare the HashFn and EqualElement too?
154  const_iterator tend = end();
155 
156  for(const_iterator iter = begin(); iter != tend; ++iter) {
157  if (!rhs.count(*iter))
158  return false;
159  }
160 
161  return true;
162  }
163 
164  bool operator!=(const TfDenseHashSet &rhs) const {
165  return !(*this == rhs);
166  }
167 
168  /// Erases all of the elements
169  ///
170  void clear() {
171  _vec().clear();
172  _h.reset();
173  }
174 
175  /// Swaps the contents of two sets.
176  ///
177  void swap(TfDenseHashSet &rhs) {
178  _storage.swap(rhs._storage);
179  _h.swap(rhs._h);
180  }
181 
182  /// \c true if the \c set's size is 0.
183  ///
184  bool empty() const {
185  return _vec().empty();
186  }
187 
188  /// Returns the size of the set.
189  ///
190  size_t size() const {
191  return _vec().size();
192  }
193 
194  /// Returns an const_iterator pointing to the beginning of the set.
195  ///
197  return _vec().begin();
198  }
199 
200  /// Returns an const_iterator pointing to the end of the set.
201  ///
202  const_iterator end() const {
203  return _vec().end();
204  }
205 
206  /// Finds the element with key \p k.
207  ///
208  const_iterator find(const Element &k) const {
209 
210  if (_h) {
211  typename _HashMap::const_iterator iter = _h->find(k);
212  if (iter == _h->end())
213  return end();
214 
215  return _vec().begin() + iter->second;
216  }
217 
218  typename _Vector::const_iterator iter, end = _vec().end();
219 
220  for(iter = _vec().begin(); iter != end; ++iter)
221  if (_equ()(*iter, k))
222  break;
223 
224  return iter;
225  }
226 
227  /// Returns the number of elements with key \p k. Which is either 0 or 1.
228  ///
229  size_t count(const Element &k) const {
230  return find(k) != end();
231  }
232 
233  /// Returns a pair of <iterator, bool> where iterator points to the element
234  /// in the list and bool is true if a new element was inserted.
235  ///
237 
238  if (_h) {
239 
240  // Attempt to insert the new index. If this fails, we can't
241  // insert v.
242 
243  std::pair<typename _HashMap::iterator, bool> res =
244  _h->insert(std::make_pair(v, size()));
245 
246  if (!res.second)
247  return insert_result(_vec().begin() + res.first->second, false);
248 
249  } else {
250 
251  // Bail if already inserted.
252  const_iterator iter = find(v);
253  if (iter != end())
254  return insert_result(iter, false);
255  }
256 
257  // Insert at end and create table if necessary.
258  _vec().push_back(v);
259  _CreateTableIfNeeded();
260 
261  return insert_result(std::prev(end()), true);
262  }
263 
264  /// Insert a range into the hash set. Note that \p i0 and \p i1 can't
265  /// point into the hash set.
266  ///
267  template<class IteratorType>
268  void insert(IteratorType i0, IteratorType i1) {
269  // Assume elements are more often than not unique, so if the sum of the
270  // current size and the size of the range is greater than or equal to
271  // the threshold, we create the table immediately so we don't do m*n
272  // work before creating the table.
273  if (size() + std::distance(i0, i1) >= Threshold)
274  _CreateTable();
275 
276  // Insert elements.
277  for (IteratorType iter = i0; iter != i1; ++iter)
278  insert(*iter);
279  }
280 
281  /// Insert a range of unique elements into the container. [begin, end)
282  /// *must not* contain any duplicate elements.
283  ///
284  template <class Iterator>
285  void insert_unique(Iterator begin, Iterator end) {
286  // Special-case empty container.
287  if (empty()) {
288  _vec().assign(begin, end);
289  _CreateTableIfNeeded();
290  } else {
291  // Just insert, since duplicate checking will use the hash.
292  insert(begin, end);
293  }
294  }
295 
296  /// Erase element with key \p k. Returns the number of elements erased.
297  ///
298  size_t erase(const Element &k) {
299 
300  const_iterator iter = find(k);
301  if (iter != end()) {
302  erase(iter);
303  return 1;
304  }
305  return 0;
306  }
307 
308  /// Erases element pointed to by \p iter.
309  ///
310  void erase(const iterator &iter) {
311 
312  // Erase key from hash table if applicable.
313  if (_h)
314  _h->erase(*iter);
315 
316  // If we are not removing that last element...
317  if (iter != std::prev(end())) {
318  using std::swap;
319 
320  // ... move the last element into the erased placed.
321  // Note that we can cast constness away because we explicitly update
322  // the TfHashMap _h below.
323  swap(*const_cast<Element *>(&(*iter)), _vec().back());
324 
325  // ... and update the moved element's index.
326  if (_h)
327  (*_h)[*iter] = iter - _vec().begin();
328  }
329 
330  _vec().pop_back();
331  }
332 
333  /// Erases a range from the set.
334  ///
335  void erase(const iterator &i0, const iterator &i1) {
336 
337  if (_h) {
338  for(const_iterator iter = i0; iter != i1; ++iter)
339  _h->erase(iter->first);
340  }
341 
342  const_iterator vremain = _vec().erase(i0, i1);
343 
344  if (_h) {
345  for(; vremain != _vec().end(); ++vremain)
346  (*_h)[*vremain] = vremain - _vec().begin();
347  }
348  }
349 
350  /// Optimize storage space.
351  ///
352  void shrink_to_fit() {
353 
354  // Shrink the vector to best size.
355  _vec().shrink_to_fit();
356 
357  if (!_h)
358  return;
359 
360  size_t sz = size();
361 
362  // If we have a hash map and are underneath the threshold, discard it.
363  if (sz < Threshold) {
364 
365  _h.reset();
366 
367  } else {
368 
369  // Otherwise, allocate a new hash map with the optimal size.
370  _h.reset(new _HashMap(sz, _hash(), _equ()));
371  for(size_t i=0; i<sz; ++i)
372  (*_h)[_vec()[i]] = i;
373  }
374  }
375 
376  /// Index into set via \p index.
377  ///
378  const Element &operator[](size_t index) const {
379  TF_VERIFY(index < size());
380  return _vec()[index];
381  }
382 
383 ////////////////////////////////////////////////////////////////////////////////
384 
385 private:
386 
387  // Helper to access the storage vector.
388  _Vector &_vec() {
389  return _storage.vector;
390  }
391 
392  // Helper to access the hash functor.
393  HashFn &_hash() {
394  return _storage;
395  }
396 
397  // Helper to access the equality functor.
398  EqualElement &_equ() {
399  return _storage;
400  }
401 
402  // Helper to access the storage vector.
403  const _Vector &_vec() const {
404  return _storage.vector;
405  }
406 
407  // Helper to access the hash functor.
408  const HashFn &_hash() const {
409  return _storage;
410  }
411 
412  // Helper to access the equality functor.
413  const EqualElement &_equ() const {
414  return _storage;
415  }
416 
417  // Helper to create the acceleration table if size dictates.
418  inline void _CreateTableIfNeeded() {
419  if (size() >= Threshold) {
420  _CreateTable();
421  }
422  }
423 
424  // Unconditionally create the acceleration table if it doesn't already
425  // exist.
426  inline void _CreateTable() {
427  if (!_h) {
428  _h.reset(new _HashMap(Threshold, _hash(), _equ()));
429  for(size_t i=0; i < size(); ++i)
430  (*_h)[_vec()[i]] = i;
431  }
432  }
433 
434  // Since sizeof(EqualElement) == 0 and sizeof(HashFn) == 0 in many cases
435  // we use the empty base optimization to not pay a size penalty.
436  // In C++20, explore using [[no_unique_address]] as an alternative
437  // way to get this optimization.
438  struct ARCH_EMPTY_BASES _CompressedStorage :
439  private EqualElement, private HashFn {
441  "EqualElement and HashFn must be distinct types.");
442  _CompressedStorage() = default;
443  _CompressedStorage(const EqualElement& equal, const HashFn& hashFn)
444  : EqualElement(equal), HashFn(hashFn) {}
445 
446  void swap(_CompressedStorage& other) {
447  using std::swap;
448  vector.swap(other.vector);
449  swap(static_cast<EqualElement&>(*this),
450  static_cast<EqualElement&>(other));
451  swap(static_cast<HashFn&>(*this), static_cast<HashFn&>(other));
452  }
453  _Vector vector;
454  friend class TfDenseHashSet;
455  };
456  _CompressedStorage _storage;
457 
458  // Optional hash map that maps from keys to vector indices.
459  std::unique_ptr<_HashMap> _h;
460 };
461 
463 
464 #endif // PXR_BASE_TF_DENSE_HASH_SET_H
_Base::const_iterator const_iterator
Definition: hashmap.h:283
void erase(const iterator &i0, const iterator &i1)
Definition: denseHashSet.h:335
bool operator==(const TfDenseHashSet &rhs) const
Definition: denseHashSet.h:148
void swap(UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &a, UT::ArraySet< Key, MULTI, MAX_LOAD_FACTOR_256, Clearer, Hash, KeyEqual > &b)
Definition: UT_ArraySet.h:1639
const GLdouble * v
Definition: glcorearb.h:837
const_iterator end() const
Definition: denseHashSet.h:202
GLsizei const GLfloat * value
Definition: glcorearb.h:824
void erase(const iterator &iter)
Definition: denseHashSet.h:310
void shrink_to_fit()
Definition: denseHashSet.h:352
std::pair< const_iterator, bool > insert_result
Return type for insert() method.
Definition: denseHashSet.h:84
IMATH_HOSTDEVICE constexpr bool equal(T1 a, T2 b, T3 t) IMATH_NOEXCEPT
Definition: ImathFun.h:105
TfDenseHashSet(const TfDenseHashSet &rhs)
Definition: denseHashSet.h:100
const_iterator find(const Element &k) const
Definition: denseHashSet.h:208
size_t count(const Element &k) const
Definition: denseHashSet.h:229
void insert(IteratorType i0, IteratorType i1)
Definition: denseHashSet.h:268
GLuint GLuint end
Definition: glcorearb.h:475
const Element & operator[](size_t index) const
Definition: denseHashSet.h:378
GLint i1
Definition: glad.h:2724
Definition: path.h:290
TfDenseHashSet & operator=(const TfDenseHashSet &rhs)
Definition: denseHashSet.h:126
TfDenseHashSet(const HashFn &hashFn=HashFn(), const EqualElement &equalElement=EqualElement())
Definition: denseHashSet.h:90
size_t size() const
Definition: denseHashSet.h:190
TfDenseHashSet & operator=(std::initializer_list< Element > l)
Definition: denseHashSet.h:140
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1432
TfDenseHashSet(Iterator begin, Iterator end)
Definition: denseHashSet.h:114
GLuint index
Definition: glcorearb.h:786
TfDenseHashSet(std::initializer_list< Element > l)
Definition: denseHashSet.h:120
_Vector::const_iterator iterator
Definition: denseHashSet.h:78
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
void swap(TfDenseHashSet &rhs)
Definition: denseHashSet.h:177
size_t erase(const Element &k)
Definition: denseHashSet.h:298
Element value_type
Definition: denseHashSet.h:59
bool operator!=(const TfDenseHashSet &rhs) const
Definition: denseHashSet.h:164
bool empty() const
Definition: denseHashSet.h:184
void insert_unique(Iterator begin, Iterator end)
Definition: denseHashSet.h:285
insert_result insert(const value_type &v)
Definition: denseHashSet.h:236
SIM_API const UT_StringHolder distance
const_iterator begin() const
Definition: denseHashSet.h:196
_Vector::const_iterator const_iterator
A const_iterator type for this set.
Definition: denseHashSet.h:81