[util] Fix node_map growth

When node_map grew, it was not properly applying the fixup routine to
non-moved elements. This fixes the grow algorithm to:

1. Realloc the array and set all new slots to empty/invalid
2. Check each old slot and remove/reinsert the item if it exists and its
   optimal slot is later in the array than its current slot
3. Reverse-iterate the original slots and call fixup() on empty slots to
   keep items from being located after a more-optimal empty slot

Also fixed the fixup() function to not need to be called in a loop
anymore, as it's only used the one way - on a given empty slot, looping
until it hits an empty slot or optimally-placed item.
This commit is contained in:
Justin C. Miller
2022-10-10 20:58:31 -07:00
parent ba0ce13fe3
commit 531e160136

View File

@@ -42,7 +42,7 @@ public:
using node_type = V; using node_type = V;
static constexpr size_t max_load = 90; static constexpr size_t max_load = 90;
static constexpr size_t min_capacity = 8; static constexpr size_t min_capacity = 16;
inline size_t count() const { return m_count; } inline size_t count() const { return m_count; }
inline size_t capacity() const { return m_capacity; } inline size_t capacity() const { return m_capacity; }
@@ -100,7 +100,7 @@ public:
const node_type * find(const key_type &key) const { const node_type * find(const key_type &key) const {
size_t slot; size_t slot;
if (!lookup(key, slot)) if (!lookup(key, slot))
return false; return nullptr;
return &m_nodes[slot]; return &m_nodes[slot];
} }
@@ -111,17 +111,26 @@ public:
size_t slot = mod(hash(key)); size_t slot = mod(hash(key));
size_t dist = 0; size_t dist = 0;
bool found = false;
size_t inserted_at;
while (true) { while (true) {
node_type &node_at_slot = m_nodes[slot]; node_type &node_at_slot = m_nodes[slot];
key_type &key_at_slot = get_map_key(node_at_slot); key_type &key_at_slot = get_map_key(node_at_slot);
if (open(key_at_slot)) { if (open(key_at_slot)) {
node_at_slot = node; node_at_slot = node;
return node_at_slot; if (!found)
inserted_at = slot;
return m_nodes[inserted_at];
} }
size_t psl_at_slot = psl(key_at_slot, slot); size_t psl_at_slot = psl(key_at_slot, slot);
if (dist > psl_at_slot) { if (dist > psl_at_slot) {
if (!found) {
found = true;
inserted_at = slot;
}
std::swap(node, node_at_slot); std::swap(node, node_at_slot);
dist = psl_at_slot; dist = psl_at_slot;
} }
@@ -141,7 +150,7 @@ public:
get_map_key(node) = invalid_id; get_map_key(node) = invalid_id;
--m_count; --m_count;
while (fixup(slot++)); fixup(slot);
return true; return true;
} }
@@ -153,31 +162,33 @@ protected:
return mod(slot + m_capacity - mod(hash(key))); return mod(slot + m_capacity - mod(hash(key)));
} }
bool fixup(size_t slot) { void fixup(size_t slot) {
size_t next_slot = mod(slot+1); while (true) {
node_type &next = m_nodes[next_slot]; size_t next_slot = mod(slot+1);
key_type &next_key = get_map_key(next); node_type &next = m_nodes[next_slot];
key_type &next_key = get_map_key(next);
if (open(next_key) || psl(next_key, next_slot) == 0) if (open(next_key) || psl(next_key, next_slot) == 0)
return false; return;
m_nodes[slot] = std::move(next); m_nodes[slot] = std::move(next);
next.~node_type(); next.~node_type();
next_key = invalid_id; next_key = invalid_id;
return true; ++slot;
}
} }
void grow() { void grow() {
node_type *old_nodes = m_nodes;
size_t old_capacity = m_capacity; size_t old_capacity = m_capacity;
size_t new_capacity = m_capacity * 2; size_t new_capacity = m_capacity * 2;
if (new_capacity < min_capacity) if (new_capacity < min_capacity)
new_capacity = min_capacity; new_capacity = min_capacity;
m_nodes = reinterpret_cast<node_type*>( void *grown = alloc::realloc(m_nodes,
realloc(m_nodes, old_capacity * sizeof(node_type), old_capacity * sizeof(node_type),
new_capacity * sizeof(node_type))); new_capacity * sizeof(node_type));
m_nodes = reinterpret_cast<node_type*>(grown);
for (size_t i = old_capacity; i < new_capacity; ++i) for (size_t i = old_capacity; i < new_capacity; ++i)
get_map_key(m_nodes[i]) = invalid_id; get_map_key(m_nodes[i]) = invalid_id;
@@ -187,19 +198,21 @@ protected:
for (size_t slot = 0; slot < old_capacity; ++slot) { for (size_t slot = 0; slot < old_capacity; ++slot) {
node_type &node = m_nodes[slot]; node_type &node = m_nodes[slot];
key_type &key = get_map_key(node); key_type &key = get_map_key(node);
size_t target = mod(hash(key)); size_t target = mod(hash(key));
if (open(key) || target < old_capacity) if (open(key) || target <= slot)
continue; continue;
--m_count; --m_count;
insert(std::move(node)); insert(std::move(node));
node.~node_type(); node.~node_type();
key = invalid_id; key = invalid_id;
size_t fixer = slot;
while (fixup(fixer++) && fixer < old_capacity);
} }
for (size_t slot = old_capacity - 1; slot < old_capacity; --slot)
if (open(get_map_key(m_nodes[slot])))
fixup(slot);
} }
const bool lookup(const key_type &key, size_t &slot) const { const bool lookup(const key_type &key, size_t &slot) const {