Ocean
Loading...
Searching...
No Matches
HashSet.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8#ifndef META_OCEAN_BASE_HASH_SET_H
9#define META_OCEAN_BASE_HASH_SET_H
10
11#include "ocean/base/Base.h"
12
13#include <vector>
14
15namespace Ocean
16{
17
18/**
19 * This class implements a hash set.
20 * @tparam T The data type that is stored by the hash set
21 * @ingroup base
22 */
23template <typename T>
25{
26 protected:
27
28 /**
29 * Definition of a pair combining a counter states and an object.
30 */
31 using Element = typename std::pair<std::pair<size_t, size_t>, T>;
32
33 /**
34 * Definition of a vector holding the set objects.
35 */
36 using Elements = std::vector<Element>;
37
38 /**
39 * Definition of a function pointer returning a hash set value.
40 */
41 using ValueFunction = size_t (*)(const T& element);
42
43 public:
44
45 /**
46 * Copy constructor.
47 * @param hashSet The hash set to copy
48 */
49 inline HashSet(const HashSet<T>& hashSet);
50
51 /**
52 * Move constructor.
53 * @param hashSet The hash set to move
54 */
55 inline HashSet(HashSet<T>&& hashSet) noexcept;
56
57 /**
58 * Creates a new hash set object by a given capacity.
59 * @param capacity Maximal capacity the hash set will support
60 * @param function Hash function to be used
61 */
62 explicit HashSet(const size_t capacity, const ValueFunction& function = defaultHashFunction);
63
64 /**
65 * Adds a new element to this hash set.
66 * @param element Element to be added
67 * @param oneOnly Adds the element if it does not exist already
68 * @param extendCapacity True, to extend the capacity if necessary
69 * @return True, if the element has been added
70 */
71 bool insert(const T& element, const bool oneOnly = true, const bool extendCapacity = true);
72
73 /**
74 * Adds (moves) a new element to this hash set.
75 * @param element Element to be moved
76 * @param oneOnly Adds the element if it does not exist already
77 * @param extendCapacity True, to extend the capacity if necessary
78 * @return True, if the element has been added
79 */
80 bool insert(T&& element, const bool oneOnly = true, const bool extendCapacity = true);
81
82 /**
83 * Removes an element from this hash set.
84 * @param element Element to be removed
85 * @return True, if succeeded
86 */
87 bool remove(const T& element);
88
89 /**
90 * Returns whether this hash set holds a given element.
91 * @param element Element to be checked
92 * @return True, if succeeded
93 */
94 bool find(const T& element) const;
95
96 /**
97 * Removes all elements from this has set.
98 */
99 void clear();
100
101 /**
102 * Returns the number of elements this hash set currently holds.
103 * @return Number of elements
104 */
105 inline size_t size() const;
106
107 /**
108 * Returns the capacity of this hash set.
109 * @return Maximal capacity this hash set supports
110 */
111 inline size_t capacity() const;
112
113 /**
114 * Returns whether this hash set is empty.
115 * @return True, if so
116 */
117 inline bool isEmpty() const;
118
119 /**
120 * Assign operator.
121 * @param hashSet The hash set to assign
122 * @return The reference to this object
123 */
124 inline HashSet<T>& operator=(const HashSet<T>& hashSet);
125
126 /**
127 * Move operator.
128 * @param hashSet The hash set to move
129 * @return The reference to this object
130 */
131 inline HashSet<T>& operator=(HashSet<T>&& hashSet) noexcept;
132
133 protected:
134
135 /**
136 * Creates a new hash set by a given hash set.
137 * @param capacity The capacity of the new has set, with range [hashSet.size(), infinity)
138 * @param hashSet The hash set which defines the initial values of this hash set, will be moved
139 */
140 HashSet(const size_t capacity, HashSet<T>&& hashSet);
141
142 /**
143 * Default hash function for elements supporting an cast size_t cast.
144 * @param element Element to return the hash value for
145 * @return Resulting hash value
146 */
147 static inline size_t defaultHashFunction(const T& element);
148
149 /**
150 * Returns whether this hash set is still consistent.
151 * @return True, if so
152 */
153 bool isConsistent() const;
154
155 protected:
156
157 /// Hash set elements.
159
160 /// Number of elements this has set holds.
161 size_t size_ = 0;
162
163 /// Value function.
165};
166
167template <typename T>
168inline HashSet<T>::HashSet(const HashSet<T>& hashSet) :
169 elements_(hashSet.elements_),
170 size_(hashSet.size_),
171 function_(hashSet.function_)
172{
173 // nothing to do here
174}
175
176template <typename T>
177inline HashSet<T>::HashSet(HashSet<T>&& hashSet) noexcept :
178 elements_(std::move(hashSet.elements_)),
179 size_(hashSet.size_),
180 function_(hashSet.function_)
181{
182 hashSet.size_ = 0;
183}
184
185template <typename T>
186HashSet<T>::HashSet(const size_t capacity, const ValueFunction& function) :
187 elements_(capacity),
188 size_(0),
189 function_(function)
190{
191 ocean_assert(isConsistent());
192}
193
194template <typename T>
195HashSet<T>::HashSet(const size_t capacity, HashSet<T>&& hashSet) :
196 elements_(capacity),
197 size_(0),
198 function_(hashSet.function_)
199{
200 ocean_assert(capacity >= hashSet.size());
201
202 for (Element& hashSetElement : hashSet.elements_)
203 {
204 if (hashSetElement.first.first != 0)
205 {
206 T& element = hashSetElement.second;
207
208 // duplicate elements are preserved, and this set is already large enough for all elements
209 insert(std::move(element), false /*oneOnly*/, false /*extendCapacity*/);
210 }
211 }
212
213 ocean_assert(size() == hashSet.size());
214 ocean_assert(isConsistent());
215
216 hashSet.clear();
217}
218
219template <typename T>
220bool HashSet<T>::insert(const T& element, const bool oneOnly, const bool extendCapacity)
221{
222 ocean_assert(size_ <= elements_.size());
223 ocean_assert(isConsistent());
224
225 // check whether we have to extend the capacity of this hash set (we extend the set if more than 80% is occupied)
226 if (extendCapacity && size_ >= elements_.size() * 80 / 100)
227 {
228 *this = HashSet<T>(max(size_t(32), elements_.size() * 2), std::move(*this));
229 ocean_assert(size_ < elements_.size() * 80 / 100);
230 }
231
232 if (size_ == elements_.size())
233 {
234 return false;
235 }
236
237 if (oneOnly)
238 {
239 // linear search
240 for (size_t n = 0; n < elements_.size(); ++n)
241 {
242 const size_t value = (function_(element) + n) % elements_.size();
243
244 // check whether the place is free
245 if (elements_[value].first.first == 0)
246 {
247 elements_[value].first.first = 1;
248 elements_[value].first.second = n;
249 elements_[value].second = element;
250
251 ++size_;
252 return true;
253 }
254 else if (elements_[value].second == element)
255 {
256 // undo the counts added while probing towards the already existing element
257 const size_t startIndex = function_(element);
258
259 for (size_t i = 0; i < n; ++i)
260 {
261 elements_[(startIndex + i) % elements_.size()].first.first--;
262 }
263
264 return false;
265 }
266 else
267 {
268 elements_[value].first.first++;
269 }
270 }
271 }
272 else
273 {
274 // linear search
275 for (size_t n = 0; n < elements_.size(); ++n)
276 {
277 const size_t value = (function_(element) + n) % elements_.size();
278
279 // check whether the place is free
280 if (elements_[value].first.first == 0)
281 {
282 elements_[value].first.first = 1;
283 elements_[value].first.second = n;
284 elements_[value].second = element;
285
286 ++size_;
287 return true;
288 }
289 else
290 {
291 elements_[value].first.first++;
292 }
293 }
294 }
295
296 ocean_assert(false && "This must never happen!");
297 return false;
298}
299
300template <typename T>
301bool HashSet<T>::insert(T&& element, const bool oneOnly, const bool extendCapacity)
302{
303 ocean_assert(size_ <= elements_.size());
304 ocean_assert(isConsistent());
305
306 // check whether we have to extend the capacity of this hash set (we extend the set if more than 80% is occupied)
307 if (extendCapacity && size_ >= elements_.size() * 80 / 100)
308 {
309 *this = HashSet<T>(max(size_t(32), elements_.size() * 2), std::move(*this));
310 ocean_assert(size_ < elements_.size() * 80 / 100);
311 }
312
313 if (size_ == elements_.size())
314 {
315 return false;
316 }
317
318 if (oneOnly)
319 {
320 // linear search
321 for (size_t n = 0; n < elements_.size(); ++n)
322 {
323 const size_t value = (function_(element) + n) % elements_.size();
324
325 // check whether the place is free
326 if (elements_[value].first.first == 0)
327 {
328 elements_[value].first.first = 1;
329 elements_[value].first.second = n;
330 elements_[value].second = std::move(element);
331
332 ++size_;
333 return true;
334 }
335 else if (elements_[value].second == element)
336 {
337 // undo the counts added while probing towards the already existing element
338 const size_t startIndex = function_(element);
339
340 for (size_t i = 0; i < n; ++i)
341 {
342 elements_[(startIndex + i) % elements_.size()].first.first--;
343 }
344
345 return false;
346 }
347 else
348 {
349 elements_[value].first.first++;
350 }
351 }
352 }
353 else
354 {
355 // linear search
356 for (size_t n = 0; n < elements_.size(); ++n)
357 {
358 const size_t value = (function_(element) + n) % elements_.size();
359
360 // check whether the place is free
361 if (elements_[value].first.first == 0)
362 {
363 elements_[value].first.first = 1;
364 elements_[value].first.second = n;
365 elements_[value].second = std::move(element);
366
367 ++size_;
368 return true;
369 }
370 else
371 {
372 elements_[value].first.first++;
373 }
374 }
375 }
376
377 ocean_assert(false && "This must never happen!");
378 return false;
379}
380
381template <typename T>
382bool HashSet<T>::remove(const T& element)
383{
384 ocean_assert(size_ <= elements_.size());
385 ocean_assert(isConsistent());
386
387 // linear search
388 for (size_t n = 0; n < elements_.size(); ++n)
389 {
390 const size_t value = (function_(element) + n) % elements_.size();
391
392 // check whether this place is free
393 if (elements_[value].first.first == 0)
394 {
395 return false;
396 }
397
398 // check whether this place has no shift problem
399 if (elements_[value].first.first == 1)
400 {
401 if (elements_[value].second == element)
402 {
403 // the slots this element was probed past no longer carry it
404 const size_t startIndex = function_(element);
405
406 for (size_t i = 0; i < n; ++i)
407 {
408 elements_[(startIndex + i) % elements_.size()].first.first--;
409 }
410
411 elements_[value].first.first = 0;
412 elements_[value].second = T();
413 --size_;
414
415 ocean_assert(isConsistent());
416
417 return true;
418 }
419
420 // the element is not the element to be removed, but also there is no other position for this element
421 return false;
422 }
423
424 ocean_assert(elements_[value].first.first > 1);
425
426 size_t elementOffset = 0u;
427
428 if (elements_[value].second == element)
429 {
430 // the element exists however, the following elements needs a special handling
431
432 size_t localValue = value;
433 size_t endLocation = elements_.size();
434
435 while (true)
436 {
437 size_t lastOffset = 0;
438
439 // find last element to swap
440 for (size_t i = 1; i < endLocation; ++i)
441 {
442 const size_t testValue = (localValue + i) % elements_.size();
443
444 if (elements_[testValue].first.first >= 1)
445 {
446 if (elements_[testValue].first.second >= i)
447 {
448 lastOffset = i;
449 }
450 }
451
452 if (elements_[testValue].first.first <= 1)
453 {
454 break;
455 }
456 }
457
458 if (lastOffset == 0)
459 {
460 break;
461 }
462
463 ocean_assert(endLocation >= lastOffset);
464 endLocation -= lastOffset;
465
466 elementOffset += lastOffset;
467
468 const size_t lastValue = (localValue + lastOffset) % elements_.size();
469
470 // move the found element
471
472 // elements_[localValue].first.first stays constant
473 elements_[localValue].first.second = elements_[lastValue].first.second - lastOffset;
474 elements_[localValue].second = elements_[lastValue].second;
475
476 localValue = lastValue;
477
478 if (elements_[lastValue].first.first == 1)
479 {
480 break;
481 }
482 }
483
484 // decrease the used counter
485 const size_t startIndex = function_(element);
486
487 for (size_t i = 0u; i < elementOffset + n; ++i)
488 elements_[(startIndex + i) % elements_.size()].first.first--;
489
490 elements_[(value + elementOffset) % elements_.size()].first.first = 0;
491 elements_[(value + elementOffset) % elements_.size()].second = T();
492 --size_;
493
494 ocean_assert(isConsistent());
495
496 return true;
497 }
498 }
499
500 ocean_assert(false && "This must never happen!");
501 return false;
502}
503
504template <typename T>
505bool HashSet<T>::find(const T& element) const
506{
507 ocean_assert(size_ <= elements_.size());
508 ocean_assert(isConsistent());
509
510 // linear search
511 for (size_t n = 0; n < elements_.size(); ++n)
512 {
513 const size_t value = (function_(element) + n) % elements_.size();
514
515 // check whether this place is free
516 if (elements_[value].first.first == 0)
517 {
518 return false;
519 }
520
521 // check whether this element is equal to the given one
522 if (elements_[value].second == element)
523 {
524 return true;
525 }
526
527 // check whether this place is not free but unique
528 if (elements_[value].first.first == 1)
529 {
530 return false;
531 }
532 }
533
534 return false;
535}
536
537template <typename T>
539{
540 ocean_assert(isConsistent());
541
542 for (Element& element : elements_)
543 {
544 element.first.first = 0;
545 }
546
547 size_ = 0;
548
549 ocean_assert(isConsistent());
550}
551
552template <typename T>
553inline size_t HashSet<T>::size() const
554{
555 return size_;
556}
557
558template <typename T>
559inline size_t HashSet<T>::capacity() const
560{
561 return elements_.size();
562}
563
564template <typename T>
565inline bool HashSet<T>::isEmpty() const
566{
567 return size_ == 0;
568}
569
570template <typename T>
572{
573 if (this != &hashSet)
574 {
575 elements_ = hashSet.elements_;
576 size_ = hashSet.size_;
577 function_ = hashSet.function_;
578 }
579
580 return *this;
581}
582
583template <typename T>
584inline HashSet<T>& HashSet<T>::operator=(HashSet<T>&& hashSet) noexcept
585{
586 if (this != &hashSet)
587 {
588 elements_ = std::move(hashSet.elements_);
589 size_ = hashSet.size_;
590 function_ = hashSet.function_;
591
592 hashSet.size_ = 0;
593 }
594
595 return *this;
596}
597
598template <typename T>
599inline size_t HashSet<T>::defaultHashFunction(const T& element)
600{
601 return size_t(element);
602}
603
604template <typename T>
606{
607 size_t count = 0;
608
609 // every element contributes one count to each slot of its probe sequence, so both sums must match
610 size_t slotCounters = 0;
611 size_t probeSequenceLengths = 0;
612
613 for (const Element& element : elements_)
614 {
615 slotCounters += element.first.first;
616
617 if (element.first.first != 0)
618 {
619 ++count;
620 probeSequenceLengths += element.first.second + 1;
621 }
622 }
623
624 return count == size_ && slotCounters == probeSequenceLengths;
625}
626
627}
628
629#endif // META_OCEAN_BASE_HASH_SET_H
This class implements a hash set.
Definition HashSet.h:25
Elements elements_
Hash set elements.
Definition HashSet.h:158
bool remove(const T &element)
Removes an element from this hash set.
Definition HashSet.h:382
size_t capacity() const
Returns the capacity of this hash set.
Definition HashSet.h:559
std::vector< Element > Elements
Definition of a vector holding the set objects.
Definition HashSet.h:36
static size_t defaultHashFunction(const T &element)
Default hash function for elements supporting an cast size_t cast.
Definition HashSet.h:599
void clear()
Removes all elements from this has set.
Definition HashSet.h:538
HashSet(const HashSet< T > &hashSet)
Copy constructor.
Definition HashSet.h:168
size_t size_
Number of elements this has set holds.
Definition HashSet.h:161
ValueFunction function_
Value function.
Definition HashSet.h:164
HashSet< T > & operator=(const HashSet< T > &hashSet)
Assign operator.
Definition HashSet.h:571
size_t size() const
Returns the number of elements this hash set currently holds.
Definition HashSet.h:553
bool isEmpty() const
Returns whether this hash set is empty.
Definition HashSet.h:565
bool insert(const T &element, const bool oneOnly=true, const bool extendCapacity=true)
Adds a new element to this hash set.
Definition HashSet.h:220
typename std::pair< std::pair< size_t, size_t >, T > Element
Definition of a pair combining a counter states and an object.
Definition HashSet.h:31
size_t(*)(const T &element) ValueFunction
Definition of a function pointer returning a hash set value.
Definition HashSet.h:41
bool find(const T &element) const
Returns whether this hash set holds a given element.
Definition HashSet.h:505
bool isConsistent() const
Returns whether this hash set is still consistent.
Definition HashSet.h:605
The namespace covering the entire Ocean framework.
Definition Accessor.h:15