DoxigAlpha

rehash

Rehash the map, in-place.

Over time, due to the current tombstone-based implementation, a HashMap could become fragmented due to the buildup of tombstone entries that causes a performance degradation due to excessive probing. The kind of pattern that might cause this is a long-lived HashMap with repeated inserts and deletes.

After this function is called, there will be no tombstones in the HashMap, each of the entries is rehashed and any existing key/value pointers into the HashMap are invalidated.

Function parameters

Parameters

#
self:*Self
ctx:anytype

Type definitions in this namespace

Types

#

Functions in this namespace

Functions

#
StringHashMap
Builtin hashmap for strings as keys.
StringHashMapUnmanaged
Key memory is managed by the caller.
HashMap
General purpose hash table.
HashMapUnmanaged
A HashMap based on open addressing and linear probing.

= 80

Values

#

Source

Implementation

#
pub fn rehash(self: *Self, ctx: anytype) void {
    const mask = self.capacity() - 1;

    var metadata = self.metadata.?;
    var keys_ptr = self.keys();
    var values_ptr = self.values();
    var curr: Size = 0;

    // While we are re-hashing every slot, we will use the
    // fingerprint to mark used buckets as being used and either free
    // (needing to be rehashed) or tombstone (already rehashed).

    while (curr < self.capacity()) : (curr += 1) {
        metadata[curr].fingerprint = Metadata.free;
    }

    // Now iterate over all the buckets, rehashing them

    curr = 0;
    while (curr < self.capacity()) {
        if (!metadata[curr].isUsed()) {
            assert(metadata[curr].isFree());
            curr += 1;
            continue;
        }

        const hash = ctx.hash(keys_ptr[curr]);
        const fingerprint = Metadata.takeFingerprint(hash);
        var idx = @as(usize, @truncate(hash & mask));

        // For each bucket, rehash to an index:
        // 1) before the cursor, probed into a free slot, or
        // 2) equal to the cursor, no need to move, or
        // 3) ahead of the cursor, probing over already rehashed

        while ((idx < curr and metadata[idx].isUsed()) or
            (idx > curr and metadata[idx].fingerprint == Metadata.tombstone))
        {
            idx = (idx + 1) & mask;
        }

        if (idx < curr) {
            assert(metadata[idx].isFree());
            metadata[idx].fill(fingerprint);
            keys_ptr[idx] = keys_ptr[curr];
            values_ptr[idx] = values_ptr[curr];

            metadata[curr].used = 0;
            assert(metadata[curr].isFree());
            keys_ptr[curr] = undefined;
            values_ptr[curr] = undefined;

            curr += 1;
        } else if (idx == curr) {
            metadata[idx].fingerprint = fingerprint;
            curr += 1;
        } else {
            assert(metadata[idx].fingerprint != Metadata.tombstone);
            metadata[idx].fingerprint = Metadata.tombstone;
            if (metadata[idx].isUsed()) {
                std.mem.swap(K, &keys_ptr[curr], &keys_ptr[idx]);
                std.mem.swap(V, &values_ptr[curr], &values_ptr[idx]);
            } else {
                metadata[idx].used = 1;
                keys_ptr[idx] = keys_ptr[curr];
                values_ptr[idx] = values_ptr[curr];

                metadata[curr].fingerprint = Metadata.free;
                metadata[curr].used = 0;
                keys_ptr[curr] = undefined;
                values_ptr[curr] = undefined;

                curr += 1;
            }
        }
    }
}