fennec
Loading...
Searching...
No Matches
set.h
Go to the documentation of this file.
1// =====================================================================================================================
2// fennec, a free and open source game engine
3// Copyright © 2025 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
30
31#ifndef FENNEC_CONTAINERS_SET_H
32#define FENNEC_CONTAINERS_SET_H
33
34// https://programming.guide/robin-hood-hashing.html
35
38#include <fennec/lang/compare.h>
39#include <fennec/math/ext/primes.h>
41#include <fennec/lang/hashing.h>
42
43namespace fennec
44{
45
67template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
68struct set {
69
70// Definitions =========================================================================================================
71public:
72 using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
73 using hash_t = Hash;
74 using equal_t = Equals;
75 using elem_t = TypeT;
76
77 class iterator;
78 static constexpr size_t npos = -1;
79 static constexpr double default_load = 0.8;
80
81private:
82 struct node {
83 optional<elem_t> value;
84 int psl;
85
86 constexpr node() = default;
87 constexpr ~node() = default;
88 };
89
90// Constructors ========================================================================================================
91public:
92
95
98 constexpr set()
99 : _alloc()
100 , _hash()
101 , _size(0)
102 , _sumpsl(0)
103 , _load(default_load) {
104 };
105
109 constexpr set(const hash_t& hash)
110 : _alloc()
111 , _hash(hash)
112 , _size(0)
113 , _sumpsl(0)
114 , _load(default_load) {
115 }
116
120 constexpr set(const alloc_t& alloc)
121 : _alloc(alloc)
122 , _hash()
123 , _size(0)
124 , _sumpsl(0)
125 , _load(default_load) {
126 }
127
132 constexpr set(const hash_t& hash, const alloc_t& alloc)
133 : _alloc(alloc)
134 , _hash(hash)
135 , _size(0)
136 , _sumpsl(0)
137 , _load(default_load) {
138 }
139
143 constexpr set(const set& set)
144 : _alloc(set._alloc)
145 , _hash(set._hash)
146 , _size(set._size)
147 , _sumpsl(set._sumpsl)
148 , _load(set._load) {
149 }
150
154 constexpr set(set&& set) noexcept
155 : _alloc(fennec::move(set._alloc))
156 , _hash(fennec::move(set._hash))
157 , _size(fennec::move(set._size))
158 , _sumpsl(set._sumpsl)
159 , _load(set._load) {
160 }
161
164 constexpr ~set() {
165 for (size_t i = 0; i < capacity(); ++i) {
166 _alloc[i].value = nullopt;
167 }
168 }
169
171
172
173// Properties ==========================================================================================================
174
177
180 constexpr size_t size() const {
181 return _size;
182 }
183
186 constexpr bool empty() const {
187 return _size == 0;
188 }
189
192 constexpr size_t capacity() const {
193 return _alloc.size();
194 }
195
197
198
199// Access ==============================================================================================================
200
203
208 constexpr iterator find(const elem_t& val) const {
209 if (capacity() == 0) {
210 return end();
211 }
212 size_t s = _hash(val) % capacity(); // Initial search index
213 int psl = (_size != 0) ? _sumpsl / _size : 0; // Initial psl
214 size_t i = (s + psl) % capacity(); // Median search
215 size_t n = 0;
216
217 // Check the first element;
218 if (_alloc[i].psl >= psl && _alloc[i].value) {
219 if (_equal(*_alloc[i].value, val)) {
220 return iterator(this, i);
221 }
222 }
223
224 // Loop while there is a value and its psl is greater than our probe
225 while (true) {
226 ++n;
227 size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
228 size_t i1 = (i + n) % capacity();
229 int p0 = psl - n, p1 = psl + n;
230 bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
231
232 if (c0 && _alloc[i0].value) {
233 if (_equal(*_alloc[i0].value, val)) {
234 return iterator(this, i0);
235 }
236 }
237
238 if (c1 && _alloc[i1].value) {
239 if (_equal(*_alloc[i1].value, val)) {
240 return iterator(this, i1);
241 }
242 }
243
244 if (not(c0 or c1)) {
245 break;
246 }
247 }
248
249 return iterator(this, npos);
250 }
251
256 constexpr bool contains(const elem_t& val) const {
257 return this->find(val) != end();
258 }
259
265 constexpr elem_t* at(const iterator& it) {
266 if (it == end()) {
267 return nullptr;
268 }
269 if (not _alloc[it._i].value) {
270 return nullptr;
271 }
272 return &*_alloc[it._i].value;
273 }
274
279 constexpr const elem_t* at(const iterator& it) const {
280 if (not _alloc[it._i].value) return nullptr;
281 return &*_alloc[it._i].value;
282 }
283
285
286// Modifiers ===========================================================================================================
287
290
294 constexpr iterator insert(elem_t&& val) {
295 return this->_insert(fennec::forward<elem_t>(val));
296 }
297
301 constexpr iterator insert(const elem_t& val) {
302 return this->_insert(val);
303 }
304
309 template<typename...ArgsT>
310 constexpr iterator emplace(ArgsT&&...args) {
311 return this->_insert(fennec::forward<ArgsT>(args)...);
312 }
313
317 constexpr void erase(iterator it) {
318 size_t i = it._i;
319 if (i >= capacity()) {
320 return;
321 } // These are separated due to compilers being inconsistent
322 if (not _alloc[i].value) {
323 return;
324 }
325
326 _alloc[i].value = nullopt;
327 _sumpsl -= _alloc[i].psl;
328 --_size;
329 size_t p = i;
330 while (_alloc[i = (i + 1) % capacity()].value) {
331 if (_alloc[i].psl == 0) break;
332
333 fennec::swap(_alloc[p].value, _alloc[i].value);
334 --_alloc[p].psl, --_sumpsl;
335 p = i;
336 }
337 }
338
342 constexpr void erase(const elem_t& val) {
343 this->erase(this->find(val));
344 }
345
348 constexpr void clear() {
349 for (size_t i = 0; i < _alloc.capacity(); ++i) {
350
351 }
352 }
353
355
356
357// ITERATOR ============================================================================================================
358
361
364 constexpr iterator begin() const {
365 iterator it(this, 0);
366 if (not _alloc[it._i].value) {
367 ++it;
368 }
369 return it;
370 }
371
374 constexpr iterator end() const {
375 return iterator(this, npos);
376 }
377
379
382 class iterator {
383 public:
384 constexpr iterator(const set* set, size_t i)
385 : _set(set)
386 , _i(i) {
387 }
388
389 constexpr ~iterator() {
390 _set = nullptr;
391 }
392
393 // prefix operator
394 constexpr friend iterator& operator++(iterator& rhs) {
395 while (++rhs._i < rhs._set->capacity()) {
396 if (rhs._set->_alloc[rhs._i].value) {
397 return rhs;
398 }
399 }
400 rhs._i = npos;
401 return rhs;
402 }
403
404 constexpr friend iterator operator++(iterator& lhs, int) {
405 iterator prev = lhs;
406 ++lhs;
407 return prev;
408 }
409
410 constexpr const elem_t& operator*() const {
411 return *_set->_alloc[_i].value;
412 }
413
414 constexpr const elem_t* operator->() const {
415 if (not _set->_alloc[_i].value) return nullptr;
416 return &*_set->_alloc[_i].value;
417 }
418
419 constexpr bool operator==(const iterator& it) const {
420 return _set == it._set and _i == it._i;
421 }
422
423 constexpr bool operator!=(const iterator& it) const {
424 return _set != it._set or _i != it._i;
425 }
426
427 constexpr size_t index() const { return _i; }
428
429 private:
430 const set* _set;
431 size_t _i;
432 friend set;
433 };
434
435
436// PRIVATE =============================================================================================================
437
438private:
439 constexpr void _expand() {
440 set cpy; // Create a new set
441 cpy._alloc.resize(
442 fennec::next_prime2(_alloc.capacity())
443 );
444
445 // rehash
446 for (size_t i = 0; i < capacity(); ++i) {
447 if (_alloc[i].value) {
448 cpy.insert(fennec::move(*_alloc[i].value));
449 }
450 }
451
452 // Swap buffers
453 fennec::swap(_alloc, cpy._alloc);
454 }
455
456 template<typename...ArgsT>
457 constexpr iterator _insert(ArgsT&&...args) {
458 if (_size == 0 or static_cast<float>(_size) / capacity() >= _load) { // expand when full
459 _expand();
460 }
461
462 elem_t value(fennec::forward<ArgsT>(args)...);
463 size_t i = _hash(value) % capacity(); // Initial search index
464 int psl = 0;
465 while (_alloc[i].value) { // Search for empty cell
466 if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
467 return iterator(this, i);
468 }
469 if (psl > _alloc[i].psl) { // When psl is higher, swap
470 _sumpsl += psl - _alloc[i].psl;
471 fennec::swap(_alloc[i].psl, psl);
472 fennec::swap(*_alloc[i].value, value);
473 }
474 i = (i + 1) % capacity(); ++psl;
475 }
476 _alloc[i].value = fennec::move(value);
477 _sumpsl += (_alloc[i].psl = psl);
478 ++_size;
479 return iterator(this, npos);
480 }
481
482 dynarray<node, alloc_t> _alloc;
483 hash_t _hash;
484 equal_t _equal;
485 size_t _size;
486 size_t _sumpsl;
487 float _load;
488};
489
490}
491
492#endif // FENNEC_CONTAINERS_SET_H
This header contains structures and classes related to allocating blocks of memory.
Class for Iterating the Set.
Definition set.h:382
A header containing the definition for a container with an optionally present variable.
A header containing the definition for a set of unique values.
typename _rebind< Alloc, TypeT >::type rebind
Rebinds the allocator type to produce an element type of type TypeT
Definition allocator.h:134
constexpr size_t capacity() const
Definition dynarray.h:337
constexpr size_t size() const
Definition dynarray.h:331
constexpr void resize(size_t n)
Resize the dynarray, invoking the default constructor for all new elements.
Definition dynarray.h:512
Struct for hashing types, there is no default hashing function.
Definition hashing.h:32
Structure to hold an optional value.
Definition optional.h:51
wrapper for sets of elements
Definition set.h:68
constexpr iterator find(const elem_t &val) const
Find an Element.
Definition set.h:208
constexpr size_t size() const
Definition set.h:180
constexpr iterator insert(elem_t &&val)
Move Insertion.
Definition set.h:294
constexpr size_t capacity() const
Definition set.h:192
constexpr bool empty() const
Definition set.h:186
constexpr set(const set &set)
Set Copy Constructor.
Definition set.h:143
constexpr iterator emplace(ArgsT &&...args)
Emplace Insertion.
Definition set.h:310
constexpr ~set()
Destructor, destructs all elements and releases the allocation.
Definition set.h:164
constexpr set()
Default Constructor, initializes empty set.
Definition set.h:98
constexpr elem_t * at(const iterator &it)
Iterator Access.
Definition set.h:265
constexpr iterator begin() const
Definition set.h:364
constexpr iterator insert(const elem_t &val)
Copy Insertion.
Definition set.h:301
constexpr set(const hash_t &hash, const alloc_t &alloc)
Hash Alloc Copy Constructor, initializes empty set with a hash and allocator.
Definition set.h:132
constexpr void erase(const elem_t &val)
Element Erase.
Definition set.h:342
constexpr set(set &&set) noexcept
Set Move Constructor.
Definition set.h:154
constexpr const elem_t * at(const iterator &it) const
Iterator Const Access.
Definition set.h:279
constexpr set(const hash_t &hash)
Hash Copy Constructor, initializes empty set with a hash.
Definition set.h:109
constexpr bool contains(const elem_t &val) const
Check if a set contains a value.
Definition set.h:256
constexpr iterator end() const
Definition set.h:374
constexpr set(const alloc_t &alloc)
Alloc Copy Constructor, initializes empty set with an allocator.
Definition set.h:120
constexpr void erase(iterator it)
Element Erase.
Definition set.h:317
constexpr void swap(T &x, T &y) noexcept
Swaps x and y.
Definition utility.h:114
constexpr remove_reference_t< T > && move(T &&x) noexcept
produces an x-value type to indicate x may be "moved"
Definition utility.h:92