Lindenii Project Forge
Add a generic hashmap implementation
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // Deletes an item from a [[map]]. export fn del(m: *map, key: []u8) (*opaque | void) = {
let b = m.buckets[hash64(m, key) % m.n]; return map::del(b, key);
return map::del(m.inner, key);
};
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // Frees resources associated with a [[map]]. export fn finish(m: *map) void = {
for (let i = 0z; i < m.n; i += 1) { map::finish(m.buckets[i]); }; free(m.buckets);
map::finish(m.inner);
free(m); };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // Gets an item from a [[map]] by key, returning void if not found. export fn get(m: *map, key: []u8) (*opaque | void) = {
let b = m.buckets[hash64(m, key) % m.n]; return map::get(b, key);
return map::get(m.inner, key);
};
use hash; use hash::fnv;
fn hash64(m: *map, key: []u8) size = {
fn hash64(_params: nullable *opaque, key: []u8) size = {
let h = fnv::fnv64a(); hash::write(&h, key); return fnv::sum64(&h): size; };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map;
// A simple hash map from byte strings to opaque pointers, using SipHash for
// A simple hash map from byte strings to opaque pointers, using FNV for
// hashing and fallback maps for collision resolution. // // You are advised to create these with [[new]]. export type map = struct { vt: map::map,
n: size, buckets: []*map::map,
inner: *map::map,
}; const _vt: map::vtable = map::vtable { getter = &vt_get, setter = &vt_set, deleter = &vt_del, finisher = &vt_finish, }; fn vt_get(m: *map::map, key: []u8) (*opaque | void) = get(m: *map, key); fn vt_set(m: *map::map, key: []u8, v: *opaque) (void | nomem) = set(m: *map, key, v); fn vt_del(m: *map::map, key: []u8) (*opaque | void) = del(m: *map, key); fn vt_finish(m: *map::map) void = finish(m: *map);
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use errors; use ds::map;
use ds::map::map_hashmap;
// Creates a new [[map]] with the given number of buckets. // make_fallback is a function that creates per-bucket fallback maps.
export fn new(make_fallback: *fn() (*map::map | nomem), n: size) (*map | errors::invalid | nomem) = { if (n == 0) {
export fn new( make_fallback: *fn() (*map::map | nomem), n: size, ) (*map | errors::invalid | nomem) = { let inner = match (map_hashmap::new(make_fallback, n, &hash64, null)) { case let hm: *map_hashmap::map => yield (hm: *map::map); case errors::invalid =>
return errors::invalid;
}; let buckets: []*map::map = []; for (let i = 0z; i < n; i += 1) { let fb = match (make_fallback()) { case let p: *map::map => yield p; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; return nomem; }; match (append(buckets, fb)) { case void => yield; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; return nomem; };
case nomem => return nomem;
}; let m = match (alloc(map { vt = &_vt,
n = n, buckets = buckets,
inner = inner,
})) {
case let pm: *map => yield pm;
case let p: *map => yield p;
case nomem =>
for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; free(buckets);
map::finish(inner);
return nomem; }; return m; };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // Sets an item in a [[map]], replacing any existing item with the same key. export fn set(m: *map, key: []u8, value: *opaque) (void | nomem) = {
let b = m.buckets[hash64(m, key) % m.n]; return map::set(b, key, value);
return map::set(m.inner, key, value);
};
map_hashmap: key-value map implemented as a generic hashmap This module provides a simple implementation of a hashmap from []u8 to *opaque. The hash algorithm and fallback collision resolution strategy are both configurable.
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use bytes; use hash; use hash::siphash; use ds::map; // Deletes an item from a [[map]]. export fn del(m: *map, key: []u8) (*opaque | void) = { let b = m.buckets[m.hash64(m.hash_params, key) % m.n]; return map::del(b, key); };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // Frees resources associated with a [[map]]. export fn finish(m: *map) void = { for (let i = 0z; i < m.n; i += 1) { map::finish(m.buckets[i]); }; free(m.buckets); free(m); };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use bytes; use hash; use hash::siphash; use ds::map; // Gets an item from a [[map]] by key, returning void if not found. export fn get(m: *map, key: []u8) (*opaque | void) = { let b = m.buckets[m.hash64(m.hash_params, key) % m.n]; return map::get(b, key); };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // A simple hash map from []u8 to *opaque pointers, using the user-provided hash // and collision resolution function. // // You are advised to create these with [[new]]. export type map = struct { vt: map::map, n: size, buckets: []*map::map, hash64: *fn(hash_params: nullable *opaque, key: []u8) size, hash_params: nullable *opaque, }; const _vt: map::vtable = map::vtable { getter = &vt_get, setter = &vt_set, deleter = &vt_del, finisher = &vt_finish, }; fn vt_get(m: *map::map, key: []u8) (*opaque | void) = get(m: *map, key); fn vt_set(m: *map::map, key: []u8, v: *opaque) (void | nomem) = set(m: *map, key, v); fn vt_del(m: *map::map, key: []u8) (*opaque | void) = del(m: *map, key); fn vt_finish(m: *map::map) void = finish(m: *map);
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use bytes; use errors; use ds::map; // Creates a new [[map]] with a function that creates fallback maps, the number // of buckets, and the hash function and parameters. export fn new( make_fallback: *fn() (*map::map | nomem), n: size, hash64: *fn(hash_params: nullable *opaque, key: []u8) size, hash_params: nullable *opaque, ) (*map | errors::invalid | nomem) = { if (n == 0) { return errors::invalid; }; let buckets: []*map::map = []; for (let i = 0z; i < n; i += 1) { let fb = match (make_fallback()) { case let p: *map::map => yield p; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; return nomem; }; match (append(buckets, fb)) { case void => yield; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; return nomem; }; }; let m = match (alloc(map { vt = &_vt, n = n, buckets = buckets, hash64 = hash64, hash_params = hash_params, })) { case let pm: *map => yield pm; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; free(buckets); return nomem; }; return m; };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use bytes; use hash; use hash::siphash; use ds::map; // Sets an item in a [[map]], replacing any existing item with the same key. export fn set(m: *map, key: []u8, value: *opaque) (void | nomem) = { let b = m.buckets[m.hash64(m.hash_params, key) % m.n]; return map::set(b, key, value); };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>
use bytes; use hash; use hash::siphash;
use ds::map; // Deletes an item from a [[map]]. export fn del(m: *map, key: []u8) (*opaque | void) = {
let b = m.buckets[hash64(m, key) % m.n]; return map::del(b, key);
return map::del(m.inner, key);
};
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // Frees resources associated with a [[map]]. export fn finish(m: *map) void = {
for (let i = 0z; i < m.n; i += 1) { map::finish(m.buckets[i]); }; free(m.buckets);
map::finish(m.inner); free(m.key);
free(m); };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>
use bytes; use hash; use hash::siphash;
use ds::map; // Gets an item from a [[map]] by key, returning void if not found. export fn get(m: *map, key: []u8) (*opaque | void) = {
let b = m.buckets[hash64(m, key) % m.n]; return map::get(b, key);
return map::get(m.inner, key);
};
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>
use hash; use hash::siphash;
fn hash64(m: *map, key: []u8) size = { let h = siphash::siphash(2, 4, &m.siphash_key);
fn hash64(params: nullable *opaque, key: []u8) size = { let keyptr = match (params) { case null => abort("map_siphash: missing key"); case let p: *opaque => yield (p: *[16]u8); }; let h = siphash::siphash(2, 4, keyptr);
defer hash::close(&h); hash::write(&h, key); return siphash::sum(&h): size; };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org> use ds::map; // A simple hash map from byte strings to opaque pointers, using SipHash for // hashing and fallback maps for collision resolution. // // You are advised to create these with [[new]]. export type map = struct { vt: map::map,
n: size, siphash_key: [16]u8, buckets: []*map::map,
inner: *map::map, key: *[16]u8,
}; const _vt: map::vtable = map::vtable { getter = &vt_get, setter = &vt_set, deleter = &vt_del, finisher = &vt_finish, }; fn vt_get(m: *map::map, key: []u8) (*opaque | void) = get(m: *map, key); fn vt_set(m: *map::map, key: []u8, v: *opaque) (void | nomem) = set(m: *map, key, v); fn vt_del(m: *map::map, key: []u8) (*opaque | void) = del(m: *map, key); fn vt_finish(m: *map::map) void = finish(m: *map);
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>
use bytes;
use errors;
use hash; use hash::siphash;
use ds::map;
use ds::map::map_hashmap;
// Creates a new [[map]] with a function that creates fallback maps, the number // of buckets, and the SipHash key.
export fn new(make_fallback: *fn() (*map::map | nomem), n: size, siphash_key: [16]u8) (*map | errors::invalid | nomem) = { if (n == 0) { return errors::invalid;
export fn new( make_fallback: *fn() (*map::map | nomem), n: size, siphash_key: [16]u8, ) (*map | errors::invalid | nomem) = { let keybox = match (alloc(siphash_key)) { case let kp: *[16]u8 => yield kp; case nomem => return nomem;
};
let buckets: []*map::map = []; for (let i = 0z; i < n; i += 1) { let fb = match (make_fallback()) { case let p: *map::map => yield p; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; return nomem; }; match (append(buckets, fb)) { case void => yield; case nomem => for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; return nomem; };
let inner = match (map_hashmap::new( make_fallback, n, &hash64, (keybox: *opaque), )) { case let hm: *map_hashmap::map => yield (hm: *map::map); case errors::invalid => free(keybox); return errors::invalid; case nomem => free(keybox); return nomem;
}; let m = match (alloc(map { vt = &_vt,
n = n, siphash_key = siphash_key, buckets = buckets,
inner = inner, key = keybox,
})) {
case let pm: *map => yield pm;
case let p: *map => yield p;
case nomem =>
for (let j = 0z; j < len(buckets); j += 1) { map::finish(buckets[j]); }; free(buckets);
map::finish(inner); free(keybox);
return nomem; }; return m; };
// SPDX-License-Identifier: MPL-2.0 // SPDX-FileCopyrightText: 2024 Drew DeVault <drew@ddevault.org> // SPDX-FileCopyrightText: 2025 Runxi Yu <me@runxiyu.org>
use bytes; use hash; use hash::siphash;
use ds::map; // Sets an item in a [[map]], replacing any existing item with the same key. export fn set(m: *map, key: []u8, value: *opaque) (void | nomem) = {
let b = m.buckets[hash64(m, key) % m.n]; return map::set(b, key, value);
return map::set(m.inner, key, value);
};