FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
slotmap.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8// includes, system
9#include <algorithm>
10#include <optional>
11#include <vector>
12
13// includes, feat
15
16namespace FEAT::Util
17{
27 {
28 SlotMapKey() : index(0), generation(0)
29 {
30 }
31 SlotMapKey(std::uint32_t i, std::uint32_t g) : index(i), generation(g)
32 {
33 }
34
35 std::uint32_t index;
36 std::uint32_t generation;
37 };
38
46 template<typename V, typename Key = SlotMapKey>
48 {
49 KeyValuePair(V& v, Key k) : value(v), key(k)
50 {
51 }
52
53 V& value;
54 Key key;
55 };
56
64 template<typename V, typename Key = SlotMapKey>
66 {
67 ConstKeyValuePair(const V& v, Key k) : value(v), key(k)
68 {
69 }
70
71 const V& value;
72 Key key;
73 };
74
147 template<typename V, typename Key = SlotMapKey>
149 {
150 public:
151 using ValueType = V;
152 using KeyType = Key;
153
157 SlotMap() : freelist{0, 0}
158 {
159 // Create initial slot, so that freelist is always valid
160 _slots.emplace_back(Slot());
161 }
162
163 SlotMap(const SlotMap& other) = default;
164 SlotMap(SlotMap&& other) = default;
165
166 SlotMap& operator=(const SlotMap& other) = default;
167 SlotMap& operator=(SlotMap&& other) = default;
168
177 Key insert(V&& value)
178 {
179 std::uint32_t idx = freelist.head;
180
181 // Adjust freelist
182 if(idx == freelist.tail)
183 {
184 // Used up the last Slot. Push a new one to keep freelist valid
185 _slots.push_back(Slot());
186 freelist.head = freelist.tail = (std::uint32_t)_slots.size() - 1;
187 }
188 else
189 {
190 // Move head to next element of freelist
191 freelist.head = _slots[idx].index;
192 }
193
194 Slot& slot = _slots[idx];
195
196 // Point slot to new data
197 slot.index = (std::uint32_t)_data.size();
198
199 // Push new data
200 _data.push_back(std::move(value));
201
202 // Update erase list
203 // Slot for new data is _slots[idx]
204 _erase.push_back(idx);
205
206 return Key(idx, slot.generation);
207 }
208
217 Key insert(const V& value)
218 {
219 std::uint32_t idx = freelist.head;
220
221 if(idx == freelist.tail)
222 {
223 // Used up the last Slot. Push a new one to keep freelist valid
224 _slots.push_back(Slot());
225 freelist.head = freelist.tail = (std::uint32_t)_slots.size() - 1;
226 }
227 else
228 {
229 // Move head to next element of freelist
230 freelist.head = _slots[idx].index;
231 }
232
233 Slot& slot = _slots[idx];
234
235 // Point slot to new data
236 slot.index = static_cast<std::uint32_t>(_data.size());
237
238 // Push new data
239 _data.push_back(value);
240
241 // Update erase list
242 // Slot for new data is _slots[idx]
243 _erase.push_back(idx);
244
245 return Key(idx, slot.generation);
246 }
247
255 void erase(const Key& key)
256 {
257 if(!contains_key(key))
258 {
259 return;
260 }
261 // Retrieve Slot for key
262 Slot& slot = try_get_slot(key);
263
264 // Increment the slots generation
265 // Previous keys are no longer valid
266 slot.generation++;
267
268 // Exchange positions of to-be-deleted element and last element
269 // of _data and _erase for O(1) deletion later
270 std::swap(_data[slot.index], _data[_data.size() - 1]);
271 std::swap(_erase[slot.index], _erase[_erase.size() - 1]);
272
273 // Fix Slot of moved element
274 _slots[_erase[slot.index]].index = slot.index;
275
276 // Actually delete elements
277 _data.pop_back();
278 _erase.pop_back();
279
280 // Slot can now be reused. Add slot to freelist
281 _slots[freelist.tail].index = key.index;
282 freelist.tail = key.index;
283 _slots[freelist.tail].index = freelist.tail;
284 }
285
297 Key replace(Key& key, V&& value)
298 {
299 erase(key);
300 return insert(value);
301 }
302
314 Key replace(Key& key, V& value)
315 {
316 erase(key);
317 return insert(value);
318 }
319
328 bool contains_key(const Key& key) const
329 {
330 return (key.index < _slots.size() && _slots[key.index].generation == key.generation);
331 }
332
341 const V& operator[](const Key& key) const
342 {
343 const Slot& slot = try_get_slot(key);
344 ASSERTM(slot.index < _data.size(), "Invalid slot index.");
345 return _data[slot.index];
346 }
347
356 V& operator[](const Key& key)
357 {
358 Slot& slot = try_get_slot(key);
359 ASSERTM(slot.index < _data.size(), "Invalid slot index.");
360 return _data[slot.index];
361 }
362
369 {
370 _slots.reserve(capacity + 1);
371 _data.reserve(capacity);
372 _slots.reserve(capacity);
373 }
374
381 {
382 return _data.capacity();
383 }
384
390 Index size() const
391 {
392 return _data.size();
393 }
394
395 V* data()
396 {
397 return _data.data();
398 }
399
400 const V* data() const
401 {
402 return _data.data();
403 }
404
405 std::size_t bytes() const
406 {
407 return sizeof(freelist) + _slots.size() * sizeof(Slot) + _data.size() * sizeof(V) +
408 _erase.size() * sizeof(Index);
409 }
410
426 {
427 public:
428 SlotMapIterator(SlotMap<V, Key>& map, Index i) : _slotmap(map), _index(i)
429 {
430 }
431
432 bool operator==(const SlotMapIterator& rhs)
433 {
434 // Iterators are identical if they refer to the same slotmap and point to the same indices
435 return &(this->_slotmap) == &(rhs._slotmap) && this->_index == rhs._index;
436 }
437 bool operator!=(const SlotMapIterator& rhs)
438 {
439 return !(*this == rhs);
440 }
441
442 SlotMapIterator& operator++()
443 {
444 _index++;
445 return *this;
446 }
447
448 KeyValuePair<V, Key> operator*()
449 {
450 // Retrieve slot for current index via erase table
451 std::uint32_t slot_index = _slotmap._erase[_index];
452 Slot& slot = _slotmap._slots[slot_index];
453 return KeyValuePair<V, Key>(_slotmap._data[slot.index], Key(slot_index, slot.generation));
454 }
455
456 private:
457 SlotMap<V, Key>& _slotmap;
458 Index _index;
459 };
460
469 {
470 public:
471 ConstSlotMapIterator(const SlotMap<V, Key>& map, Index i) : _slotmap(map), _index(i)
472 {
473 }
474
475 bool operator==(const ConstSlotMapIterator& rhs)
476 {
477 // Iterators are identical if they refer to the same slotmap and point to the same indices
478 return &(this->_slotmap) == &(rhs._slotmap) && this->_index == rhs._index;
479 }
480 bool operator!=(const ConstSlotMapIterator& rhs)
481 {
482 return !(*this == rhs);
483 }
484
485 ConstSlotMapIterator& operator++()
486 {
487 _index++;
488 return *this;
489 }
490
491 ConstKeyValuePair<V, Key> operator*()
492 {
493 // Retrieve slot for current index via erase table
494 std::uint32_t slot_index = _slotmap._erase[_index];
495 const Slot& slot = _slotmap._slots[slot_index];
496 return ConstKeyValuePair<V, Key>(_slotmap._data[slot.index], Key(slot_index, slot.generation));
497 }
498
499 private:
500 const SlotMap<V, Key>& _slotmap;
501 Index _index;
502 };
503
504 SlotMapIterator begin()
505 {
506 return SlotMapIterator(*this, 0);
507 }
508
509 SlotMapIterator end()
510 {
511 return SlotMapIterator(*this, _data.size());
512 }
513
514 ConstSlotMapIterator begin() const
515 {
516 return ConstSlotMapIterator(*this, 0);
517 }
518
519 ConstSlotMapIterator end() const
520 {
521 return ConstSlotMapIterator(*this, _data.size());
522 }
523
524 private:
528 struct Slot
529 {
530 Slot() : index(0), generation(1)
531 {
532 }
533 Slot(std::uint32_t i, std::uint32_t g) : index(i), generation(g)
534 {
535 }
536
537 std::uint32_t index;
538 std::uint32_t generation;
539 };
540
549 Slot& try_get_slot(const Key& key)
550 {
551 ASSERTM(key.index < _slots.size(), "Invalid SlotMap key. Index exceeds number of slots.");
552 Slot& slot = _slots[key.index];
553 ASSERTM(key.generation == slot.generation, "Invalid SlotMap key. Generation mismatch.");
554 return slot;
555 }
556
557 const Slot& try_get_slot(const Key& key) const
558 {
559 ASSERTM(key.index < _slots.size(), "Invalid SlotMap key. Index exceeds number of slots.");
560 const Slot& slot = _slots[key.index];
561 ASSERTM(key.generation == slot.generation, "Invalid SlotMap key. Generation mismatch.");
562 return slot;
563 }
564
565 struct
566 {
567 std::uint32_t head;
568 std::uint32_t tail;
569 } freelist;
570
571 std::vector<Slot> _slots;
572 std::vector<V> _data;
573 std::vector<std::uint32_t> _erase;
574 };
575
576 template<typename V, typename Key = SlotMapKey>
578 {
579 public:
580 void insert(const Key& key, const V& value)
581 {
582 if(key.index >= _slots.size())
583 {
584 Index extend_by = 1 + key.index - _slots.size();
585 _slots.insert(_slots.end(), extend_by, Slot());
586 }
587
588
589 Slot& slot = _slots[key.index];
590 XASSERTM(slot.generation <= key.generation, "Trying to insert into SecondaryMap with outdated key!");
591 slot.value = std::optional<V>(value);
592 slot.generation = key.generation;
593 }
594
595 bool contains_key(const Key& key)
596 {
597 return key.index < _slots.size() && _slots[key.index].generation == key.generation &&
598 _slots[key.index].value.has_value();
599 }
600
609 const V& operator[](const Key& key) const
610 {
611 ASSERTM(key.index < _slots.size(), "Invalid SecondaryMap key. Index exceeds number of slots.");
612 const Slot& slot = _slots[key.index];
613 ASSERTM(slot.value.has_value(), "Invalid SecondaryMap key. Slot is unoccupied.");
614 ASSERTM(key.generation == slot.generation, "Invalid SecondaryMap key. Generation mismatch.");
615 return slot.value.value();
616 }
617
626 V& operator[](const Key& key)
627 {
628 ASSERTM(key.index < _slots.size(), "Invalid SecondaryMap key. Index exceeds number of slots.");
629 Slot& slot = _slots[key.index];
630 ASSERTM(slot.value.has_value(), "Invalid SecondaryMap key. Slot is unoccupied.");
631 ASSERTM(key.generation == slot.generation, "Invalid SecondaryMap key. Generation mismatch.");
632 return slot.value.value();
633 }
634
635 private:
636 struct Slot
637 {
638 Slot() : generation(1), value(std::nullopt)
639 {
640 }
641 explicit Slot(V&& v) : generation(1), value(std::move(v))
642 {
643 }
644
645 std::uint32_t generation;
646 std::optional<V> value;
647 };
648
649 std::vector<Slot> _slots;
650 };
651} // namespace FEAT::Util
#define ASSERTM(expr, msg)
Debug-Assertion macro definition with custom message.
Definition: assertion.hpp:230
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
V & operator[](const Key &key)
Access Operator.
Definition: slotmap.hpp:626
const V & operator[](const Key &key) const
Access Operator.
Definition: slotmap.hpp:609
Const iterator type for the SlotMap.
Definition: slotmap.hpp:469
Iterator type for the SlotMap.
Definition: slotmap.hpp:426
High-performance associative container.
Definition: slotmap.hpp:149
const V & operator[](const Key &key) const
Access Operator.
Definition: slotmap.hpp:341
Key insert(V &&value)
Insert value into the SlotMap.
Definition: slotmap.hpp:177
void reserve(Index capacity)
Reserve at least capacity space in the SlotMap.
Definition: slotmap.hpp:368
V & operator[](const Key &key)
Access Operator.
Definition: slotmap.hpp:356
SlotMap()
Construct SlotMap with initial capacity zero.
Definition: slotmap.hpp:157
Key replace(Key &key, V &&value)
Replace the value stored at key with value.
Definition: slotmap.hpp:297
bool contains_key(const Key &key) const
Check if given key is still valid.
Definition: slotmap.hpp:328
void erase(const Key &key)
Remove a key from the SlotMap.
Definition: slotmap.hpp:255
Index size() const
Retrieve current size of the SlotMap.
Definition: slotmap.hpp:390
Key insert(const V &value)
Insert a value into the SlotMap.
Definition: slotmap.hpp:217
Key replace(Key &key, V &value)
Replace the value stored at key with value.
Definition: slotmap.hpp:314
Index capacity() const
Retrieve current capacity of the SlotMap.
Definition: slotmap.hpp:380
Slot & try_get_slot(const Key &key)
Helper method to retrieve a slot.
Definition: slotmap.hpp:549
@ value
specifies whether the space should supply basis function values
std::uint64_t Index
Index data type.
Return type for ConstSlotMapIterator.
Definition: slotmap.hpp:66
Return type for SlotMapIterator.
Definition: slotmap.hpp:48
Internal data structure for slots.
Definition: slotmap.hpp:529
Default key type for SlotMap.
Definition: slotmap.hpp:27