Blender V4.3
BLI_set.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
58#include "BLI_array.hh"
59#include "BLI_hash.hh"
60#include "BLI_hash_tables.hh"
62#include "BLI_set_slots.hh"
63
64namespace blender {
65
66template<
71 typename Key,
77 int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key)),
81 typename ProbingStrategy = DefaultProbingStrategy,
86 typename Hash = DefaultHash<Key>,
91 typename IsEqual = DefaultEquality<Key>,
100 typename Slot = typename DefaultSetSlot<Key>::type,
105 typename Allocator = GuardedAllocator>
106class Set {
107 public:
108 class Iterator;
110 using pointer = Key *;
111 using const_pointer = const Key *;
112 using reference = Key &;
113 using const_reference = const Key &;
116
117 private:
122 int64_t removed_slots_;
123 int64_t occupied_and_removed_slots_;
124
129 int64_t usable_slots_;
130
135 uint64_t slot_mask_;
136
138 BLI_NO_UNIQUE_ADDRESS Hash hash_;
139
141 BLI_NO_UNIQUE_ADDRESS IsEqual is_equal_;
142
144#define LOAD_FACTOR 1, 2
145 LoadFactor max_load_factor_ = LoadFactor(LOAD_FACTOR);
146 using SlotArray =
147 Array<Slot, LoadFactor::compute_total_slots(InlineBufferCapacity, LOAD_FACTOR), Allocator>;
148#undef LOAD_FACTOR
149
154 SlotArray slots_;
155
157#define SET_SLOT_PROBING_BEGIN(HASH, R_SLOT) \
158 SLOT_PROBING_BEGIN (ProbingStrategy, HASH, slot_mask_, SLOT_INDEX) \
159 auto &R_SLOT = slots_[SLOT_INDEX];
160#define SET_SLOT_PROBING_END() SLOT_PROBING_END()
161
162 public:
168 Set(Allocator allocator = {}) noexcept
169 : removed_slots_(0),
170 occupied_and_removed_slots_(0),
171 usable_slots_(0),
172 slot_mask_(0),
173 slots_(1, allocator)
174 {
175 }
176
177 Set(NoExceptConstructor, Allocator allocator = {}) noexcept : Set(allocator) {}
178
179 Set(Span<Key> values, Allocator allocator = {}) : Set(NoExceptConstructor(), allocator)
180 {
181 this->add_multiple(values);
182 }
183
187 Set(const std::initializer_list<Key> &values) : Set(Span<Key>(values)) {}
188
189 ~Set() = default;
190
191 Set(const Set &other) = default;
192
193 Set(Set &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>)
194 : Set(NoExceptConstructor(), other.slots_.allocator())
195
196 {
197 if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) {
198 slots_ = std::move(other.slots_);
199 }
200 else {
201 try {
202 slots_ = std::move(other.slots_);
203 }
204 catch (...) {
205 other.noexcept_reset();
206 throw;
207 }
208 }
209 removed_slots_ = other.removed_slots_;
210 occupied_and_removed_slots_ = other.occupied_and_removed_slots_;
211 usable_slots_ = other.usable_slots_;
212 slot_mask_ = other.slot_mask_;
213 hash_ = std::move(other.hash_);
214 is_equal_ = std::move(other.is_equal_);
215 other.noexcept_reset();
216 }
217
218 Set &operator=(const Set &other)
219 {
220 return copy_assign_container(*this, other);
221 }
222
223 Set &operator=(Set &&other)
224 {
225 return move_assign_container(*this, std::move(other));
226 }
227
233 void add_new(const Key &key)
234 {
235 this->add_new__impl(key, hash_(key));
236 }
237 void add_new(Key &&key)
238 {
239 this->add_new__impl(std::move(key), hash_(key));
240 }
241
248 bool add(const Key &key)
249 {
250 return this->add_as(key);
251 }
252 bool add(Key &&key)
253 {
254 return this->add_as(std::move(key));
255 }
256 template<typename ForwardKey> bool add_as(ForwardKey &&key)
257 {
258 return this->add__impl(std::forward<ForwardKey>(key), hash_(key));
259 }
260
269 {
270 for (const Key &key : keys) {
271 this->add(key);
272 }
273 }
274
280 {
281 for (const Key &key : keys) {
282 this->add_new(key);
283 }
284 }
285
291 bool contains(const Key &key) const
292 {
293 return this->contains_as(key);
294 }
295 template<typename ForwardKey> bool contains_as(const ForwardKey &key) const
296 {
297 return this->contains__impl(key, hash_(key));
298 }
299
304 const Key &lookup_key(const Key &key) const
305 {
306 return this->lookup_key_as(key);
307 }
308 template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const
309 {
310 return this->lookup_key__impl(key, hash_(key));
311 }
312
317 const Key &lookup_key_default(const Key &key, const Key &default_value) const
318 {
319 return this->lookup_key_default_as(key, default_value);
320 }
321 template<typename ForwardKey>
322 const Key &lookup_key_default_as(const ForwardKey &key, const Key &default_key) const
323 {
324 const Key *ptr = this->lookup_key_ptr__impl(key, hash_(key));
325 if (ptr == nullptr) {
326 return default_key;
327 }
328 return *ptr;
329 }
330
335 const Key *lookup_key_ptr(const Key &key) const
336 {
337 return this->lookup_key_ptr_as(key);
338 }
339 template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const
340 {
341 return this->lookup_key_ptr__impl(key, hash_(key));
342 }
343
348 const Key &lookup_key_or_add(const Key &key)
349 {
350 return this->lookup_key_or_add_as(key);
351 }
353 {
354 return this->lookup_key_or_add_as(std::move(key));
355 }
356 template<typename ForwardKey> const Key &lookup_key_or_add_as(ForwardKey &&key)
357 {
358 return this->lookup_key_or_add__impl(std::forward<ForwardKey>(key), hash_(key));
359 }
360
366 bool remove(const Key &key)
367 {
368 return this->remove_as(key);
369 }
370 template<typename ForwardKey> bool remove_as(const ForwardKey &key)
371 {
372 return this->remove__impl(key, hash_(key));
373 }
374
378 void remove_contained(const Key &key)
379 {
380 this->remove_contained_as(key);
381 }
382 template<typename ForwardKey> void remove_contained_as(const ForwardKey &key)
383 {
384 this->remove_contained__impl(key, hash_(key));
385 }
386
394 class Iterator {
395 public:
396 using iterator_category = std::forward_iterator_tag;
398 using pointer = const Key *;
399 using reference = const Key &;
400 using difference_type = std::ptrdiff_t;
401
402 private:
403 const Slot *slots_;
404 int64_t total_slots_;
405 int64_t current_slot_;
406
407 friend Set;
408
409 public:
410 Iterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
411 : slots_(slots), total_slots_(total_slots), current_slot_(current_slot)
412 {
413 }
414
416 {
417 while (++current_slot_ < total_slots_) {
418 if (slots_[current_slot_].is_occupied()) {
419 break;
420 }
421 }
422 return *this;
423 }
424
426 {
427 Iterator copied_iterator = *this;
428 ++(*this);
429 return copied_iterator;
430 }
431
432 const Key &operator*() const
433 {
434 return *slots_[current_slot_].key();
435 }
436
437 const Key *operator->() const
438 {
439 return slots_[current_slot_].key();
440 }
441
442 friend bool operator!=(const Iterator &a, const Iterator &b)
443 {
444 BLI_assert(a.slots_ == b.slots_);
445 BLI_assert(a.total_slots_ == b.total_slots_);
446 return a.current_slot_ != b.current_slot_;
447 }
448
449 friend bool operator==(const Iterator &a, const Iterator &b)
450 {
451 return !(a != b);
452 }
453
454 protected:
455 const Slot &current_slot() const
456 {
457 return slots_[current_slot_];
458 }
459 };
460
462 {
463 for (int64_t i = 0; i < slots_.size(); i++) {
464 if (slots_[i].is_occupied()) {
465 return Iterator(slots_.data(), slots_.size(), i);
466 }
467 }
468 return this->end();
469 }
470
471 Iterator end() const
472 {
473 return Iterator(slots_.data(), slots_.size(), slots_.size());
474 }
475
481 void remove(const Iterator &it)
482 {
483 /* The const cast is valid because this method itself is not const. */
484 Slot &slot = const_cast<Slot &>(it.current_slot());
485 BLI_assert(slot.is_occupied());
486 slot.remove();
487 removed_slots_++;
488 }
489
496 template<typename Predicate> int64_t remove_if(Predicate &&predicate)
497 {
498 const int64_t prev_size = this->size();
499 for (Slot &slot : slots_) {
500 if (slot.is_occupied()) {
501 const Key &key = *slot.key();
502 if (predicate(key)) {
503 slot.remove();
504 removed_slots_++;
505 }
506 }
507 }
508 return prev_size - this->size();
509 }
510
514 void print_stats(const char *name) const
515 {
516 HashTableStats stats(*this, *this);
517 stats.print(name);
518 }
519
524 int64_t count_collisions(const Key &key) const
525 {
526 return this->count_collisions__impl(key, hash_(key));
527 }
528
532 void clear()
533 {
534 for (Slot &slot : slots_) {
535 slot.~Slot();
536 new (&slot) Slot();
537 }
538
539 removed_slots_ = 0;
540 occupied_and_removed_slots_ = 0;
541 }
542
547 {
548 std::destroy_at(this);
549 new (this) Set(NoExceptConstructor{});
550 }
551
556 void rehash()
557 {
558 this->realloc_and_reinsert(this->size());
559 }
560
564 int64_t size() const
565 {
566 return occupied_and_removed_slots_ - removed_slots_;
567 }
568
572 bool is_empty() const
573 {
574 return occupied_and_removed_slots_ == removed_slots_;
575 }
576
581 {
582 return slots_.size();
583 }
584
589 {
590 return removed_slots_;
591 }
592
597 {
598 return sizeof(Slot);
599 }
600
606 {
607 return sizeof(Slot) * slots_.size();
608 }
609
614 void reserve(const int64_t n)
615 {
616 if (usable_slots_ < n) {
617 this->realloc_and_reinsert(n);
618 }
619 }
620
624 static bool Intersects(const Set &a, const Set &b)
625 {
626 /* Make sure we iterate over the shorter set. */
627 if (a.size() > b.size()) {
628 return Intersects(b, a);
629 }
630
631 for (const Key &key : a) {
632 if (b.contains(key)) {
633 return true;
634 }
635 }
636 return false;
637 }
638
642 static bool Disjoint(const Set &a, const Set &b)
643 {
644 return !Intersects(a, b);
645 }
646
647 friend bool operator==(const Set &a, const Set &b)
648 {
649 if (a.size() != b.size()) {
650 return false;
651 }
652 for (const Key &key : a) {
653 if (!b.contains(key)) {
654 return false;
655 }
656 }
657 return true;
658 }
659
660 friend bool operator!=(const Set &a, const Set &b)
661 {
662 return !(a == b);
663 }
664
665 private:
666 BLI_NOINLINE void realloc_and_reinsert(const int64_t min_usable_slots)
667 {
668 int64_t total_slots, usable_slots;
669 max_load_factor_.compute_total_and_usable_slots(
670 SlotArray::inline_buffer_capacity(), min_usable_slots, &total_slots, &usable_slots);
671 BLI_assert(total_slots >= 1);
672 const uint64_t new_slot_mask = uint64_t(total_slots) - 1;
673
677 if (this->size() == 0) {
678 try {
679 slots_.reinitialize(total_slots);
680 }
681 catch (...) {
682 this->noexcept_reset();
683 throw;
684 }
685 removed_slots_ = 0;
686 occupied_and_removed_slots_ = 0;
687 usable_slots_ = usable_slots;
688 slot_mask_ = new_slot_mask;
689 return;
690 }
691
692 /* The grown array that we insert the keys into. */
693 SlotArray new_slots(total_slots);
694
695 try {
696 for (Slot &slot : slots_) {
697 if (slot.is_occupied()) {
698 this->add_after_grow(slot, new_slots, new_slot_mask);
699 slot.remove();
700 }
701 }
702 slots_ = std::move(new_slots);
703 }
704 catch (...) {
705 this->noexcept_reset();
706 throw;
707 }
708
709 occupied_and_removed_slots_ -= removed_slots_;
710 usable_slots_ = usable_slots;
711 removed_slots_ = 0;
712 slot_mask_ = new_slot_mask;
713 }
714
715 void add_after_grow(Slot &old_slot, SlotArray &new_slots, const uint64_t new_slot_mask)
716 {
717 const uint64_t hash = old_slot.get_hash(Hash());
718
719 SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) {
720 Slot &slot = new_slots[slot_index];
721 if (slot.is_empty()) {
722 slot.occupy(std::move(*old_slot.key()), hash);
723 return;
724 }
725 }
727 }
728
733 void noexcept_reset() noexcept
734 {
735 Allocator allocator = slots_.allocator();
736 this->~Set();
737 new (this) Set(NoExceptConstructor(), allocator);
738 }
739
740 template<typename ForwardKey>
741 bool contains__impl(const ForwardKey &key, const uint64_t hash) const
742 {
744 if (slot.is_empty()) {
745 return false;
746 }
747 if (slot.contains(key, is_equal_, hash)) {
748 return true;
749 }
750 }
752 }
753
754 template<typename ForwardKey>
755 const Key &lookup_key__impl(const ForwardKey &key, const uint64_t hash) const
756 {
757 BLI_assert(this->contains_as(key));
758
760 if (slot.contains(key, is_equal_, hash)) {
761 return *slot.key();
762 }
763 }
765 }
766
767 template<typename ForwardKey>
768 const Key *lookup_key_ptr__impl(const ForwardKey &key, const uint64_t hash) const
769 {
771 if (slot.contains(key, is_equal_, hash)) {
772 return slot.key();
773 }
774 if (slot.is_empty()) {
775 return nullptr;
776 }
777 }
779 }
780
781 template<typename ForwardKey> void add_new__impl(ForwardKey &&key, const uint64_t hash)
782 {
783 BLI_assert(!this->contains_as(key));
784
785 this->ensure_can_add();
786
788 if (slot.is_empty()) {
789 slot.occupy(std::forward<ForwardKey>(key), hash);
790 BLI_assert(hash_(*slot.key()) == hash);
791 occupied_and_removed_slots_++;
792 return;
793 }
794 }
796 }
797
798 template<typename ForwardKey> bool add__impl(ForwardKey &&key, const uint64_t hash)
799 {
800 this->ensure_can_add();
801
803 if (slot.is_empty()) {
804 slot.occupy(std::forward<ForwardKey>(key), hash);
805 BLI_assert(hash_(*slot.key()) == hash);
806 occupied_and_removed_slots_++;
807 return true;
808 }
809 if (slot.contains(key, is_equal_, hash)) {
810 return false;
811 }
812 }
814 }
815
816 template<typename ForwardKey> bool remove__impl(const ForwardKey &key, const uint64_t hash)
817 {
819 if (slot.contains(key, is_equal_, hash)) {
820 slot.remove();
821 removed_slots_++;
822 return true;
823 }
824 if (slot.is_empty()) {
825 return false;
826 }
827 }
829 }
830
831 template<typename ForwardKey>
832 void remove_contained__impl(const ForwardKey &key, const uint64_t hash)
833 {
834 BLI_assert(this->contains_as(key));
835
837 if (slot.contains(key, is_equal_, hash)) {
838 slot.remove();
839 removed_slots_++;
840 return;
841 }
842 }
844 }
845
846 template<typename ForwardKey>
847 const Key &lookup_key_or_add__impl(ForwardKey &&key, const uint64_t hash)
848 {
849 this->ensure_can_add();
850
852 if (slot.contains(key, is_equal_, hash)) {
853 return *slot.key();
854 }
855 if (slot.is_empty()) {
856 slot.occupy(std::forward<ForwardKey>(key), hash);
857 BLI_assert(hash_(*slot.key()) == hash);
858 occupied_and_removed_slots_++;
859 return *slot.key();
860 }
861 }
863 }
864
865 template<typename ForwardKey>
866 int64_t count_collisions__impl(const ForwardKey &key, const uint64_t hash) const
867 {
868 int64_t collisions = 0;
869
871 if (slot.contains(key, is_equal_, hash)) {
872 return collisions;
873 }
874 if (slot.is_empty()) {
875 return collisions;
876 }
877 collisions++;
878 }
880 }
881
882 void ensure_can_add()
883 {
884 if (occupied_and_removed_slots_ >= usable_slots_) {
885 this->realloc_and_reinsert(this->size() + 1);
886 BLI_assert(occupied_and_removed_slots_ < usable_slots_);
887 }
888 }
889};
890
895template<typename Key,
896 int64_t InlineBufferCapacity = default_inline_buffer_capacity(sizeof(Key)),
897 typename ProbingStrategy = DefaultProbingStrategy,
898 typename Hash = DefaultHash<Key>,
899 typename IsEqual = DefaultEquality<Key>,
900 typename Slot = typename DefaultSetSlot<Key>::type>
902
903} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_NOINLINE
#define LOAD_FACTOR
Definition BLI_map.hh:162
#define SLOT_PROBING_BEGIN(PROBING_STRATEGY, HASH, MASK, R_SLOT_INDEX)
#define SLOT_PROBING_END()
#define SET_SLOT_PROBING_END()
Definition BLI_set.hh:160
#define SET_SLOT_PROBING_BEGIN(HASH, R_SLOT)
Definition BLI_set.hh:157
#define BLI_NO_UNIQUE_ADDRESS
struct Key Key
int64_t size() const
Definition BLI_array.hh:245
static int64_t inline_buffer_capacity()
Definition BLI_array.hh:379
const T * data() const
Definition BLI_array.hh:301
void reinitialize(const int64_t new_size)
Definition BLI_array.hh:388
Allocator & allocator()
Definition BLI_array.hh:366
void print(const char *name) const
static constexpr int64_t compute_total_slots(int64_t min_usable_slots, uint8_t numerator, uint8_t denominator)
void compute_total_and_usable_slots(int64_t min_total_slots, int64_t min_usable_slots, int64_t *r_total_slots, int64_t *r_usable_slots) const
Iterator operator++(int)
Definition BLI_set.hh:425
std::ptrdiff_t difference_type
Definition BLI_set.hh:400
friend bool operator!=(const Iterator &a, const Iterator &b)
Definition BLI_set.hh:442
Iterator & operator++()
Definition BLI_set.hh:415
const Key * operator->() const
Definition BLI_set.hh:437
std::forward_iterator_tag iterator_category
Definition BLI_set.hh:396
const Key & operator*() const
Definition BLI_set.hh:432
const Slot & current_slot() const
Definition BLI_set.hh:455
friend bool operator==(const Iterator &a, const Iterator &b)
Definition BLI_set.hh:449
Iterator(const Slot *slots, int64_t total_slots, int64_t current_slot)
Definition BLI_set.hh:410
Set(NoExceptConstructor, Allocator allocator={}) noexcept
Definition BLI_set.hh:177
const Key & lookup_key_or_add_as(ForwardKey &&key)
Definition BLI_set.hh:356
void remove(const Iterator &it)
Definition BLI_set.hh:481
int64_t size_per_element() const
Definition BLI_set.hh:596
const Key & lookup_key_default(const Key &key, const Key &default_value) const
Definition BLI_set.hh:317
bool remove_as(const ForwardKey &key)
Definition BLI_set.hh:370
Set(const std::initializer_list< Key > &values)
Definition BLI_set.hh:187
int64_t size_in_bytes() const
Definition BLI_set.hh:605
~Set()=default
Set(const Set &other)=default
Iterator begin() const
Definition BLI_set.hh:461
const Key & lookup_key_or_add(const Key &key)
Definition BLI_set.hh:348
void add_multiple_new(Span< Key > keys)
Definition BLI_set.hh:279
const Key & lookup_key(const Key &key) const
Definition BLI_set.hh:304
void print_stats(const char *name) const
Definition BLI_set.hh:514
void clear_and_shrink()
Definition BLI_set.hh:546
int64_t size_type
Definition BLI_set.hh:115
static bool Disjoint(const Set &a, const Set &b)
Definition BLI_set.hh:642
bool add(Key &&key)
Definition BLI_set.hh:252
const Key & lookup_key_as(const ForwardKey &key) const
Definition BLI_set.hh:308
int64_t size() const
Definition BLI_set.hh:564
bool add_as(ForwardKey &&key)
Definition BLI_set.hh:256
int64_t removed_amount() const
Definition BLI_set.hh:588
bool contains_as(const ForwardKey &key) const
Definition BLI_set.hh:295
void reserve(const int64_t n)
Definition BLI_set.hh:614
const Key & lookup_key_default_as(const ForwardKey &key, const Key &default_key) const
Definition BLI_set.hh:322
Iterator end() const
Definition BLI_set.hh:471
Set(Span< Key > values, Allocator allocator={})
Definition BLI_set.hh:179
void remove_contained(const Key &key)
Definition BLI_set.hh:378
friend bool operator!=(const Set &a, const Set &b)
Definition BLI_set.hh:660
bool contains(const Key &key) const
Definition BLI_set.hh:291
static bool Intersects(const Set &a, const Set &b)
Definition BLI_set.hh:624
int64_t capacity() const
Definition BLI_set.hh:580
int64_t count_collisions(const Key &key) const
Definition BLI_set.hh:524
friend bool operator==(const Set &a, const Set &b)
Definition BLI_set.hh:647
bool add(const Key &key)
Definition BLI_set.hh:248
Set & operator=(const Set &other)
Definition BLI_set.hh:218
bool is_empty() const
Definition BLI_set.hh:572
Set & operator=(Set &&other)
Definition BLI_set.hh:223
void add_multiple(Span< Key > keys)
Definition BLI_set.hh:268
const Key * lookup_key_ptr_as(const ForwardKey &key) const
Definition BLI_set.hh:339
Set(Set &&other) noexcept(std::is_nothrow_move_constructible_v< SlotArray >)
Definition BLI_set.hh:193
void add_new(const Key &key)
Definition BLI_set.hh:233
const Key & lookup_key_or_add(Key &&key)
Definition BLI_set.hh:352
Set(Allocator allocator={}) noexcept
Definition BLI_set.hh:168
int64_t remove_if(Predicate &&predicate)
Definition BLI_set.hh:496
void clear()
Definition BLI_set.hh:532
void remove_contained_as(const ForwardKey &key)
Definition BLI_set.hh:382
const Key * lookup_key_ptr(const Key &key) const
Definition BLI_set.hh:335
bool remove(const Key &key)
Definition BLI_set.hh:366
void rehash()
Definition BLI_set.hh:556
void add_new(Key &&key)
Definition BLI_set.hh:237
local_group_size(16, 16) .push_constant(Type b
Container & copy_assign_container(Container &dst, const Container &src)
Container & move_assign_container(Container &dst, Container &&src) noexcept(std::is_nothrow_move_constructible_v< Container >)
constexpr int64_t default_inline_buffer_capacity(size_t element_size)
PythonProbingStrategy<> DefaultProbingStrategy
#define hash
Definition noise.c:154
__int64 int64_t
Definition stdint.h:89
unsigned __int64 uint64_t
Definition stdint.h:90
SimpleSetSlot< Key > type
PointerRNA * ptr
Definition wm_files.cc:4126