完善V1.4踢乒乓球版本
This commit is contained in:
1 parent
d14fe6c37b
commit
72455a5e76
1864 files changed
+19294
-218
No files matched your search
@@ -0,0 +1,725 @@
|
||||
#pragma once
|
||||
|
||||
/*
|
||||
HashMap that is optimized for embedded devices. The hashmap
|
||||
will store upto N elements inline, and will spill over to a heap.
|
||||
This hashmap will try not to grow by detecting during rehash that
|
||||
the number of tombstones is greater than the number of elements.
|
||||
This will keep the memory from growing during multiple inserts
|
||||
and removals.
|
||||
*/
|
||||
|
||||
// #include <cstddef>
|
||||
// #include <iterator>
|
||||
|
||||
#include "fl/assert.h"
|
||||
#include "fl/bitset.h"
|
||||
#include "fl/clamp.h"
|
||||
#include "fl/hash.h"
|
||||
#include "fl/map_range.h"
|
||||
#include "fl/optional.h"
|
||||
#include "fl/pair.h"
|
||||
#include "fl/type_traits.h"
|
||||
#include "fl/vector.h"
|
||||
#include "fl/warn.h"
|
||||
#include "fl/align.h"
|
||||
#include "fl/compiler_control.h"
|
||||
#include "fl/math_macros.h"
|
||||
|
||||
#include "fl/compiler_control.h"
|
||||
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_WARNING_SHORTEN_64_TO_32
|
||||
|
||||
namespace fl {
|
||||
|
||||
// // begin using declarations for stl compatibility
|
||||
// use fl::equal_to;
|
||||
// use fl::hash_map;
|
||||
// use fl::unordered_map;
|
||||
// // end using declarations for stl compatibility
|
||||
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_WARNING_NULL_DEREFERENCE
|
||||
template <typename T> struct EqualTo {
|
||||
bool operator()(const T &a, const T &b) const { return a == b; }
|
||||
};
|
||||
FL_DISABLE_WARNING_POP
|
||||
|
||||
// -- HashMap class
|
||||
// ------------------------------------------------------------- Begin HashMap
|
||||
// class
|
||||
|
||||
#ifndef FASTLED_HASHMAP_INLINED_COUNT
|
||||
#define FASTLED_HASHMAP_INLINED_COUNT 8
|
||||
#endif
|
||||
|
||||
template <typename Key, typename T, typename Hash = Hash<Key>,
|
||||
typename KeyEqual = EqualTo<Key>,
|
||||
int INLINED_COUNT = FASTLED_HASHMAP_INLINED_COUNT>
|
||||
class FL_ALIGN HashMap {
|
||||
public:
|
||||
HashMap() : HashMap(FASTLED_HASHMAP_INLINED_COUNT, 0.7f) {}
|
||||
HashMap(fl::size initial_capacity) : HashMap(initial_capacity, 0.7f) {}
|
||||
|
||||
HashMap(fl::size initial_capacity, float max_load)
|
||||
: _buckets(next_power_of_two(initial_capacity)), _size(0),
|
||||
_tombstones(0), _occupied(next_power_of_two(initial_capacity)),
|
||||
_deleted(next_power_of_two(initial_capacity)) {
|
||||
setLoadFactor(max_load);
|
||||
}
|
||||
|
||||
void setLoadFactor(float f) {
|
||||
f = fl::clamp(f, 0.f, 1.f);
|
||||
mLoadFactor = fl::map_range<float, u8>(f, 0.f, 1.f, 0, 255);
|
||||
}
|
||||
|
||||
// Iterator support.
|
||||
struct iterator {
|
||||
// Standard iterator typedefs
|
||||
// using difference_type = std::ptrdiff_t;
|
||||
using value_type = pair<const Key, T>; // Keep const Key as per standard
|
||||
using pointer = value_type *;
|
||||
using reference = value_type &;
|
||||
// using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
iterator() : _map(nullptr), _idx(0) {}
|
||||
iterator(HashMap *m, fl::size idx) : _map(m), _idx(idx) {
|
||||
advance_to_occupied();
|
||||
}
|
||||
|
||||
value_type operator*() const {
|
||||
auto &e = _map->_buckets[_idx];
|
||||
return {e.key, e.value};
|
||||
}
|
||||
|
||||
pointer operator->() {
|
||||
// Use reinterpret_cast since pair<const Key, T> and pair<Key, T> are different types
|
||||
// but have the same memory layout, then destroy/reconstruct to avoid assignment issues
|
||||
using mutable_value_type = pair<Key, T>;
|
||||
auto& mutable_cached = *reinterpret_cast<mutable_value_type*>(&_cached_value);
|
||||
mutable_cached.~mutable_value_type();
|
||||
new (&mutable_cached) mutable_value_type(operator*());
|
||||
return &_cached_value;
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
// Use reinterpret_cast since pair<const Key, T> and pair<Key, T> are different types
|
||||
// but have the same memory layout, then destroy/reconstruct to avoid assignment issues
|
||||
using mutable_value_type = pair<Key, T>;
|
||||
auto& mutable_cached = *fl::bit_cast<mutable_value_type*>(&_cached_value);
|
||||
mutable_cached.~mutable_value_type();
|
||||
new (&mutable_cached) mutable_value_type(operator*());
|
||||
return &_cached_value;
|
||||
}
|
||||
|
||||
iterator &operator++() {
|
||||
++_idx;
|
||||
advance_to_occupied();
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) {
|
||||
iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const iterator &o) const {
|
||||
return _map == o._map && _idx == o._idx;
|
||||
}
|
||||
bool operator!=(const iterator &o) const { return !(*this == o); }
|
||||
|
||||
void advance_to_occupied() {
|
||||
if (!_map)
|
||||
return;
|
||||
fl::size cap = _map->_buckets.size();
|
||||
while (_idx < cap && !_map->is_occupied(_idx))
|
||||
++_idx;
|
||||
}
|
||||
|
||||
private:
|
||||
HashMap *_map;
|
||||
fl::size _idx;
|
||||
mutable value_type _cached_value;
|
||||
friend class HashMap;
|
||||
};
|
||||
|
||||
struct const_iterator {
|
||||
// Standard iterator typedefs
|
||||
// using difference_type = std::ptrdiff_t;
|
||||
using value_type = pair<const Key, T>;
|
||||
using pointer = const value_type *;
|
||||
using reference = const value_type &;
|
||||
// using iterator_category = std::forward_iterator_tag;
|
||||
|
||||
const_iterator() : _map(nullptr), _idx(0) {}
|
||||
const_iterator(const HashMap *m, fl::size idx) : _map(m), _idx(idx) {
|
||||
advance_to_occupied();
|
||||
}
|
||||
const_iterator(const iterator &it) : _map(it._map), _idx(it._idx) {}
|
||||
|
||||
value_type operator*() const {
|
||||
auto &e = _map->_buckets[_idx];
|
||||
return {e.key, e.value};
|
||||
}
|
||||
|
||||
pointer operator->() const {
|
||||
// Use reinterpret_cast since pair<const Key, T> and pair<Key, T> are different types
|
||||
// but have the same memory layout, then destroy/reconstruct to avoid assignment issues
|
||||
using mutable_value_type = pair<Key, T>;
|
||||
auto& mutable_cached = *reinterpret_cast<mutable_value_type*>(&_cached_value);
|
||||
mutable_cached.~mutable_value_type();
|
||||
new (&mutable_cached) mutable_value_type(operator*());
|
||||
return &_cached_value;
|
||||
}
|
||||
|
||||
const_iterator &operator++() {
|
||||
++_idx;
|
||||
advance_to_occupied();
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int) {
|
||||
const_iterator tmp = *this;
|
||||
++(*this);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator &o) const {
|
||||
return _map == o._map && _idx == o._idx;
|
||||
}
|
||||
bool operator!=(const const_iterator &o) const { return !(*this == o); }
|
||||
|
||||
void advance_to_occupied() {
|
||||
if (!_map)
|
||||
return;
|
||||
fl::size cap = _map->_buckets.size();
|
||||
while (_idx < cap && !_map->is_occupied(_idx))
|
||||
++_idx;
|
||||
}
|
||||
|
||||
friend class HashMap;
|
||||
|
||||
private:
|
||||
const HashMap *_map;
|
||||
fl::size _idx;
|
||||
mutable value_type _cached_value;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, _buckets.size()); }
|
||||
const_iterator begin() const { return const_iterator(this, 0); }
|
||||
const_iterator end() const { return const_iterator(this, _buckets.size()); }
|
||||
const_iterator cbegin() const { return const_iterator(this, 0); }
|
||||
const_iterator cend() const {
|
||||
return const_iterator(this, _buckets.size());
|
||||
}
|
||||
|
||||
static bool NeedsRehash(fl::size size, fl::size bucket_size, fl::size tombstones,
|
||||
u8 load_factor) {
|
||||
// (size + tombstones) << 8 : multiply numerator by 256
|
||||
// capacity * max_load : denominator * threshold
|
||||
u32 lhs = (size + tombstones) << 8;
|
||||
u32 rhs = (bucket_size * load_factor);
|
||||
return lhs > rhs;
|
||||
}
|
||||
|
||||
// returns true if (size + tombs)/capacity > _max_load/256
|
||||
bool needs_rehash() const {
|
||||
return NeedsRehash(_size, _buckets.size(), _tombstones, mLoadFactor);
|
||||
}
|
||||
|
||||
// insert or overwrite
|
||||
void insert(const Key &key, const T &value) {
|
||||
const bool will_rehash = needs_rehash();
|
||||
if (will_rehash) {
|
||||
// if half the buckets are tombstones, rehash inline to prevent
|
||||
// memory spill over into the heap.
|
||||
if (_tombstones > _size) {
|
||||
rehash_inline_no_resize();
|
||||
} else {
|
||||
rehash(_buckets.size() * 2);
|
||||
}
|
||||
}
|
||||
fl::size idx;
|
||||
bool is_new;
|
||||
fl::pair<fl::size, bool> p = find_slot(key);
|
||||
idx = p.first;
|
||||
is_new = p.second;
|
||||
if (is_new) {
|
||||
_buckets[idx].key = key;
|
||||
_buckets[idx].value = value;
|
||||
mark_occupied(idx);
|
||||
++_size;
|
||||
} else {
|
||||
FASTLED_ASSERT(idx != npos(), "HashMap::insert: invalid index at "
|
||||
<< idx << " which is " << npos());
|
||||
_buckets[idx].value = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Move version of insert
|
||||
void insert(Key &&key, T &&value) {
|
||||
const bool will_rehash = needs_rehash();
|
||||
if (will_rehash) {
|
||||
// if half the buckets are tombstones, rehash inline to prevent
|
||||
// memory spill over into the heap.
|
||||
if (_tombstones > _size) {
|
||||
rehash_inline_no_resize();
|
||||
} else {
|
||||
rehash(_buckets.size() * 2);
|
||||
}
|
||||
}
|
||||
fl::size idx;
|
||||
bool is_new;
|
||||
fl::pair<fl::size, bool> p = find_slot(key);
|
||||
idx = p.first;
|
||||
is_new = p.second;
|
||||
if (is_new) {
|
||||
_buckets[idx].key = fl::move(key);
|
||||
_buckets[idx].value = fl::move(value);
|
||||
mark_occupied(idx);
|
||||
++_size;
|
||||
} else {
|
||||
FASTLED_ASSERT(idx != npos(), "HashMap::insert: invalid index at "
|
||||
<< idx << " which is " << npos());
|
||||
_buckets[idx].value = fl::move(value);
|
||||
}
|
||||
}
|
||||
|
||||
// remove key; returns true if removed
|
||||
bool remove(const Key &key) {
|
||||
auto idx = find_index(key);
|
||||
if (idx == npos())
|
||||
return false;
|
||||
mark_deleted(idx);
|
||||
--_size;
|
||||
++_tombstones;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool erase(const Key &key) { return remove(key); }
|
||||
|
||||
// Iterator-based erase - more efficient when you already have the iterator position
|
||||
iterator erase(iterator it) {
|
||||
if (it == end() || it._map != this) {
|
||||
return end(); // Invalid iterator
|
||||
}
|
||||
|
||||
// Mark the current position as deleted
|
||||
mark_deleted(it._idx);
|
||||
--_size;
|
||||
++_tombstones;
|
||||
|
||||
// Advance to next valid element and return iterator to it
|
||||
++it._idx;
|
||||
it.advance_to_occupied();
|
||||
return it;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_buckets.assign(_buckets.size(), Entry{});
|
||||
_occupied.reset();
|
||||
_deleted.reset();
|
||||
_size = _tombstones = 0;
|
||||
}
|
||||
|
||||
// find pointer to value or nullptr
|
||||
T *find_value(const Key &key) {
|
||||
auto idx = find_index(key);
|
||||
return idx == npos() ? nullptr : &_buckets[idx].value;
|
||||
}
|
||||
|
||||
const T *find_value(const Key &key) const {
|
||||
auto idx = find_index(key);
|
||||
return idx == npos() ? nullptr : &_buckets[idx].value;
|
||||
}
|
||||
|
||||
iterator find(const Key &key) {
|
||||
auto idx = find_index(key);
|
||||
return idx == npos() ? end() : iterator(this, idx);
|
||||
}
|
||||
|
||||
const_iterator find(const Key &key) const {
|
||||
auto idx = find_index(key);
|
||||
return idx == npos() ? end() : const_iterator(this, idx);
|
||||
}
|
||||
|
||||
bool contains(const Key &key) const {
|
||||
auto idx = find_index(key);
|
||||
return idx != npos();
|
||||
}
|
||||
|
||||
// access or default-construct
|
||||
T &operator[](const Key &key) {
|
||||
fl::size idx;
|
||||
bool is_new;
|
||||
|
||||
fl::pair<fl::size, bool> p = find_slot(key);
|
||||
idx = p.first;
|
||||
is_new = p.second;
|
||||
|
||||
// Check if find_slot failed to find a valid slot (HashMap is full)
|
||||
if (idx == npos()) {
|
||||
// Need to resize to make room
|
||||
if (needs_rehash()) {
|
||||
// if half the buckets are tombstones, rehash inline to prevent
|
||||
// memory growth. Otherwise, double the size.
|
||||
if (_tombstones >= _buckets.size() / 2) {
|
||||
rehash_inline_no_resize();
|
||||
} else {
|
||||
rehash(_buckets.size() * 2);
|
||||
}
|
||||
} else {
|
||||
// Force a rehash with double size if needs_rehash() doesn't detect the issue
|
||||
rehash(_buckets.size() * 2);
|
||||
}
|
||||
|
||||
// Try find_slot again after resize
|
||||
p = find_slot(key);
|
||||
idx = p.first;
|
||||
is_new = p.second;
|
||||
|
||||
// If still npos() after resize, something is seriously wrong
|
||||
if (idx == npos()) {
|
||||
// This should never happen after a successful resize
|
||||
static T default_value{};
|
||||
return default_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_new) {
|
||||
_buckets[idx].key = key;
|
||||
_buckets[idx].value = T{};
|
||||
mark_occupied(idx);
|
||||
++_size;
|
||||
}
|
||||
return _buckets[idx].value;
|
||||
}
|
||||
|
||||
fl::size size() const { return _size; }
|
||||
bool empty() const { return _size == 0; }
|
||||
fl::size capacity() const { return _buckets.size(); }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
static fl::size npos() {
|
||||
return static_cast<fl::size>(-1);
|
||||
}
|
||||
|
||||
// Helper methods to check entry state
|
||||
bool is_occupied(fl::size idx) const { return _occupied.test(idx); }
|
||||
|
||||
bool is_deleted(fl::size idx) const { return _deleted.test(idx); }
|
||||
|
||||
bool is_empty(fl::size idx) const {
|
||||
return !is_occupied(idx) && !is_deleted(idx);
|
||||
}
|
||||
|
||||
void mark_occupied(fl::size idx) {
|
||||
_occupied.set(idx);
|
||||
_deleted.reset(idx);
|
||||
}
|
||||
|
||||
void mark_deleted(fl::size idx) {
|
||||
_occupied.reset(idx);
|
||||
_deleted.set(idx);
|
||||
}
|
||||
|
||||
void mark_empty(fl::size idx) {
|
||||
_occupied.reset(idx);
|
||||
_deleted.reset(idx);
|
||||
}
|
||||
|
||||
struct alignas(fl::max_align<Key, T>::value) Entry {
|
||||
Key key;
|
||||
T value;
|
||||
void swap(Entry &other) {
|
||||
fl::swap(key, other.key);
|
||||
fl::swap(value, other.value);
|
||||
}
|
||||
};
|
||||
|
||||
static fl::size next_power_of_two(fl::size n) {
|
||||
fl::size p = 1;
|
||||
while (p < n)
|
||||
p <<= 1;
|
||||
return p;
|
||||
}
|
||||
|
||||
pair<fl::size, bool> find_slot(const Key &key) const {
|
||||
const fl::size cap = _buckets.size();
|
||||
const fl::size mask = cap - 1;
|
||||
const fl::size h = _hash(key) & mask;
|
||||
fl::size first_tomb = npos();
|
||||
|
||||
if (cap <= 8) {
|
||||
// linear probing
|
||||
for (fl::size i = 0; i < cap; ++i) {
|
||||
const fl::size idx = (h + i) & mask;
|
||||
|
||||
if (is_empty(idx))
|
||||
return {first_tomb != npos() ? first_tomb : idx, true};
|
||||
if (is_deleted(idx)) {
|
||||
if (first_tomb == npos())
|
||||
first_tomb = idx;
|
||||
} else if (is_occupied(idx) && _equal(_buckets[idx].key, key)) {
|
||||
return {idx, false};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// quadratic probing up to 8 tries
|
||||
fl::size i = 0;
|
||||
for (; i < 8; ++i) {
|
||||
const fl::size idx = (h + i + i * i) & mask;
|
||||
|
||||
if (is_empty(idx))
|
||||
return {first_tomb != npos() ? first_tomb : idx, true};
|
||||
if (is_deleted(idx)) {
|
||||
if (first_tomb == npos())
|
||||
first_tomb = idx;
|
||||
} else if (is_occupied(idx) && _equal(_buckets[idx].key, key)) {
|
||||
return {idx, false};
|
||||
}
|
||||
}
|
||||
// fallback to linear for the rest
|
||||
for (; i < cap; ++i) {
|
||||
const fl::size idx = (h + i) & mask;
|
||||
|
||||
if (is_empty(idx))
|
||||
return {first_tomb != npos() ? first_tomb : idx, true};
|
||||
if (is_deleted(idx)) {
|
||||
if (first_tomb == npos())
|
||||
first_tomb = idx;
|
||||
} else if (is_occupied(idx) && _equal(_buckets[idx].key, key)) {
|
||||
return {idx, false};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {npos(), false};
|
||||
}
|
||||
|
||||
enum {
|
||||
kLinearProbingOnlySize = 8,
|
||||
kQuadraticProbingTries = 8,
|
||||
};
|
||||
|
||||
fl::size find_index(const Key &key) const {
|
||||
const fl::size cap = _buckets.size();
|
||||
const fl::size mask = cap - 1;
|
||||
const fl::size h = _hash(key) & mask;
|
||||
|
||||
if (cap <= kLinearProbingOnlySize) {
|
||||
// linear probing
|
||||
for (fl::size i = 0; i < cap; ++i) {
|
||||
const fl::size idx = (h + i) & mask;
|
||||
if (is_empty(idx))
|
||||
return npos();
|
||||
if (is_occupied(idx) && _equal(_buckets[idx].key, key))
|
||||
return idx;
|
||||
}
|
||||
} else {
|
||||
// quadratic probing up to 8 tries
|
||||
fl::size i = 0;
|
||||
for (; i < kQuadraticProbingTries; ++i) {
|
||||
const fl::size idx = (h + i + i * i) & mask;
|
||||
if (is_empty(idx))
|
||||
return npos();
|
||||
if (is_occupied(idx) && _equal(_buckets[idx].key, key))
|
||||
return idx;
|
||||
}
|
||||
// fallback to linear for the rest
|
||||
for (; i < cap; ++i) {
|
||||
const fl::size idx = (h + i) & mask;
|
||||
if (is_empty(idx))
|
||||
return npos();
|
||||
if (is_occupied(idx) && _equal(_buckets[idx].key, key))
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
|
||||
return npos();
|
||||
}
|
||||
|
||||
fl::size find_unoccupied_index_using_bitset(
|
||||
const Key &key, const fl::bitset<1024> &occupied_set) const {
|
||||
const fl::size cap = _buckets.size();
|
||||
const fl::size mask = cap - 1;
|
||||
const fl::size h = _hash(key) & mask;
|
||||
|
||||
if (cap <= kLinearProbingOnlySize) {
|
||||
// linear probing
|
||||
for (fl::size i = 0; i < cap; ++i) {
|
||||
const fl::size idx = (h + i) & mask;
|
||||
bool occupied = occupied_set.test(idx);
|
||||
if (occupied) {
|
||||
continue;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
} else {
|
||||
// quadratic probing up to 8 tries
|
||||
fl::size i = 0;
|
||||
for (; i < kQuadraticProbingTries; ++i) {
|
||||
const fl::size idx = (h + i + i * i) & mask;
|
||||
bool occupied = occupied_set.test(idx);
|
||||
if (occupied) {
|
||||
continue;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
// fallback to linear for the rest
|
||||
for (; i < cap; ++i) {
|
||||
const fl::size idx = (h + i) & mask;
|
||||
bool occupied = occupied_set.test(idx);
|
||||
if (occupied) {
|
||||
continue;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
}
|
||||
return npos();
|
||||
}
|
||||
|
||||
void rehash(fl::size new_cap) {
|
||||
new_cap = next_power_of_two(new_cap);
|
||||
fl::vector_inlined<Entry, INLINED_COUNT> old;
|
||||
fl::bitset<1024> old_occupied = _occupied;
|
||||
|
||||
_buckets.swap(old);
|
||||
_buckets.clear();
|
||||
_buckets.assign(new_cap, Entry{});
|
||||
|
||||
_occupied.reset();
|
||||
_occupied.resize(new_cap);
|
||||
_deleted.reset();
|
||||
_deleted.resize(new_cap);
|
||||
|
||||
_size = _tombstones = 0;
|
||||
|
||||
for (fl::size i = 0; i < old.size(); i++) {
|
||||
if (old_occupied.test(i))
|
||||
insert(fl::move(old[i].key), fl::move(old[i].value));
|
||||
}
|
||||
}
|
||||
|
||||
// Rehash the inline buckets without resizing
|
||||
FL_DISABLE_WARNING_PUSH
|
||||
FL_DISABLE_WARNING_NULL_DEREFERENCE
|
||||
void rehash_inline_no_resize() {
|
||||
// filter out tombstones and compact
|
||||
fl::size cap = _buckets.size();
|
||||
fl::size pos = 0;
|
||||
// compact live elements to the left.
|
||||
for (fl::size i = 0; i < cap; ++i) {
|
||||
if (is_occupied(i)) {
|
||||
if (pos != i) {
|
||||
_buckets[pos] = _buckets[i];
|
||||
mark_empty(i);
|
||||
mark_occupied(pos);
|
||||
}
|
||||
++pos;
|
||||
} else if (is_deleted(i)) {
|
||||
mark_empty(i);
|
||||
}
|
||||
}
|
||||
|
||||
fl::bitset<1024> occupied; // Preallocate a bitset of size 1024
|
||||
// swap the components, this will happen at most N times,
|
||||
// use the occupied bitset to track which entries are occupied
|
||||
// in the array rather than just copied in.
|
||||
fl::optional<Entry> tmp;
|
||||
for (fl::size i = 0; i < _size; ++i) {
|
||||
const bool already_finished = occupied.test(i);
|
||||
if (already_finished) {
|
||||
continue;
|
||||
}
|
||||
auto &e = _buckets[i];
|
||||
// FASTLED_ASSERT(e.state == EntryState::Occupied,
|
||||
// "HashMap::rehash_inline_no_resize: invalid
|
||||
// state");
|
||||
|
||||
fl::size idx = find_unoccupied_index_using_bitset(e.key, occupied);
|
||||
if (idx == npos()) {
|
||||
// no more space
|
||||
FASTLED_ASSERT(
|
||||
false, "HashMap::rehash_inline_no_resize: invalid index at "
|
||||
<< idx << " which is " << npos());
|
||||
return;
|
||||
}
|
||||
// if idx < pos then we are moving the entry to a new location
|
||||
FASTLED_ASSERT(!tmp,
|
||||
"HashMap::rehash_inline_no_resize: invalid tmp");
|
||||
if (idx >= _size) {
|
||||
// directly copy it now
|
||||
_buckets[idx] = e;
|
||||
continue;
|
||||
}
|
||||
tmp = e;
|
||||
occupied.set(idx);
|
||||
_buckets[idx] = *tmp.ptr();
|
||||
while (!tmp.empty()) {
|
||||
// we have to find a place for temp.
|
||||
// find new position for tmp.
|
||||
auto key = tmp.ptr()->key;
|
||||
fl::size new_idx =
|
||||
find_unoccupied_index_using_bitset(key, occupied);
|
||||
if (new_idx == npos()) {
|
||||
// no more space
|
||||
FASTLED_ASSERT(
|
||||
false,
|
||||
"HashMap::rehash_inline_no_resize: invalid index at "
|
||||
<< new_idx << " which is " << npos());
|
||||
return;
|
||||
}
|
||||
occupied.set(new_idx);
|
||||
if (new_idx < _size) {
|
||||
// we have to swap the entry at new_idx with tmp
|
||||
fl::optional<Entry> tmp2 = _buckets[new_idx];
|
||||
_buckets[new_idx] = *tmp.ptr();
|
||||
tmp = tmp2;
|
||||
} else {
|
||||
// we can just move tmp to new_idx
|
||||
_buckets[new_idx] = *tmp.ptr();
|
||||
tmp.reset();
|
||||
}
|
||||
}
|
||||
FASTLED_ASSERT(
|
||||
occupied.test(i),
|
||||
"HashMap::rehash_inline_no_resize: invalid occupied at " << i);
|
||||
FASTLED_ASSERT(
|
||||
tmp.empty(), "HashMap::rehash_inline_no_resize: invalid tmp at " << i);
|
||||
}
|
||||
// Reset tombstones count since we've cleared all deleted entries
|
||||
_tombstones = 0;
|
||||
}
|
||||
FL_DISABLE_WARNING_POP
|
||||
|
||||
fl::vector_inlined<Entry, INLINED_COUNT> _buckets;
|
||||
fl::size _size;
|
||||
fl::size _tombstones;
|
||||
u8 mLoadFactor;
|
||||
fl::bitset<1024> _occupied;
|
||||
fl::bitset<1024> _deleted;
|
||||
Hash _hash;
|
||||
KeyEqual _equal;
|
||||
};
|
||||
|
||||
// begin using declarations for stl compatibility
|
||||
template <typename T> using equal_to = EqualTo<T>;
|
||||
|
||||
template <typename Key, typename T, typename Hash = Hash<Key>,
|
||||
typename KeyEqual = equal_to<Key>>
|
||||
using hash_map = HashMap<Key, T, Hash, KeyEqual>;
|
||||
|
||||
template <typename Key, typename T, typename Hash = Hash<Key>,
|
||||
typename KeyEqual = equal_to<Key>>
|
||||
using unordered_map = HashMap<Key, T, Hash, KeyEqual>;
|
||||
// end using declarations for stl compatibility
|
||||
|
||||
} // namespace fl
|
||||
|
||||
FL_DISABLE_WARNING_POP
|
||||
Reference in new issue
Block a user