Lindenii Project Forge
Maps should own their keys
// SPDX-License-Identifier: MPL-2.0 // Frees resources associated with a [[map]]. export fn finish(m: *map) void = { node_finish(m.root); free(m); }; fn node_finish(n: *node) void = { if (!n.leaf) { for (let i = 0z; i < len(n.children); i += 1) { node_finish(n.children[i]); }; free(n.children); };
for (let i = 0z; i < len(n.keys); i += 1) { free(n.keys[i]); };
free(n.keys); free(n.vals); free(n); };
// SPDX-License-Identifier: MPL-2.0 use bytes; use sort; fn keycmp(a: []u8, b: []u8) int = { let n = if (len(a) < len(b)) len(a) else len(b); for (let i = 0z; i < n; i += 1) { if (a[i] < b[i]) return -1; if (a[i] > b[i]) return 1; }; if (len(a) < len(b)) return -1; if (len(a) > len(b)) return 1; return 0; }; fn cmp_u8slice(a: const *opaque, b: const *opaque) int = { let sa = *(a: *[]u8); let sb = *(b: *[]u8); return keycmp(sa, sb); }; fn node_new(t: size, leaf: bool) (*node | nomem) = { let capk = 2 * t - 1; let capc = if (leaf) 0z else 2z * t; let empty_keys: [][]u8 = []; let keys = alloc(empty_keys, capk)?; let empty_vals: []*opaque = []; let vals = alloc(empty_vals, capk)?; let children: []*node = if (leaf) { yield []; } else { let empty_children: []*node = []; yield alloc(empty_children, capc)?; }; let nd = alloc(node { leaf = leaf, keys = keys, vals = vals, children = children, })?; return nd; }; fn split_child(m: *map, x: *node, i: size) (void | nomem) = { const t = m.t; let y = x.children[i]; let z = node_new(t, y.leaf)?; let medk = y.keys[t - 1]; let medv = y.vals[t - 1]; append(z.keys, y.keys[t..]...)?; append(z.vals, y.vals[t..]...)?; if (!y.leaf) { append(z.children, y.children[t..]...)?; }; y.keys = y.keys[..t - 1]; y.vals = y.vals[..t - 1]; if (!y.leaf) { y.children = y.children[..t]; }; insert(x.keys[i], medk)?; insert(x.vals[i], medv)?; insert(x.children[i + 1], z)?; };
fn dup_u8(src: []u8) ([]u8 | nomem) = { return match (alloc(src, len(src))) { case let b: []u8 => yield b; case nomem => return nomem; }; };
fn insert_nonfull(m: *map, x: *node, key: []u8, val: *opaque) (void | nomem) = { let i = sort::lbisect((x.keys: []const opaque), size([]u8), (&key: const *opaque), &cmp_u8slice); if (i < len(x.keys) && bytes::equal(x.keys[i], key)) { x.vals[i] = val; return; }; if (x.leaf) {
insert(x.keys[i], key)?;
let kcopy = match (dup_u8(key)) { case let b: []u8 => yield b; case nomem => return nomem; }; insert(x.keys[i], kcopy)?;
insert(x.vals[i], val)?; return; }; if (len(x.children[i].keys) == 2 * m.t - 1) { split_child(m, x, i)?; let cmp = cmp_u8slice((&key: const *opaque), (&x.keys[i]: const *opaque)); if (cmp == 0) { x.vals[i] = val; return; }; if (cmp > 0) { i += 1; }; }; insert_nonfull(m, x.children[i], key, val)?; }; fn merge_children(m: *map, x: *node, i: size) void = { let left = x.children[i]; let right = x.children[i + 1]; insert(left.keys[len(left.keys)], x.keys[i])!; insert(left.vals[len(left.vals)], x.vals[i])!; append(left.keys, right.keys...)!; append(left.vals, right.vals...)!; if (!left.leaf) { append(left.children, right.children...)!; }; delete(x.keys[i]); delete(x.vals[i]); delete(x.children[i + 1]); }; fn ensure_child_has_space(m: *map, x: *node, i: size) void = { const t = m.t; let c = x.children[i]; if (len(c.keys) >= t) return; if (i > 0 && len(x.children[i - 1].keys) >= t) { let ls = x.children[i - 1]; insert(c.keys[0], x.keys[i - 1])!; insert(c.vals[0], x.vals[i - 1])!; if (!c.leaf) { let moved = ls.children[len(ls.children) - 1]; insert(c.children[0], moved)!; delete(ls.children[len(ls.children) - 1]); }; x.keys[i - 1] = ls.keys[len(ls.keys) - 1]; x.vals[i - 1] = ls.vals[len(ls.vals) - 1]; delete(ls.keys[len(ls.keys) - 1]); delete(ls.vals[len(ls.vals) - 1]); return; }; if (i + 1 < len(x.children) && len(x.children[i + 1].keys) >= t) { let rs = x.children[i + 1]; insert(c.keys[len(c.keys)], x.keys[i])!; insert(c.vals[len(c.vals)], x.vals[i])!; if (!c.leaf) { let moved = rs.children[0]; insert(c.children[len(c.children)], moved)!; delete(rs.children[0]); }; x.keys[i] = rs.keys[0]; x.vals[i] = rs.vals[0]; delete(rs.keys[0]); delete(rs.vals[0]); return; }; if (i + 1 < len(x.children)) { merge_children(m, x, i); } else { merge_children(m, x, i - 1); }; }; fn pop_max(m: *map, x: *node) ([]u8, *opaque) = { let cur = x; for (!cur.leaf) { let last_before = len(cur.children) - 1; ensure_child_has_space(m, cur, last_before); let last = len(cur.children) - 1; cur = cur.children[last]; }; let k = cur.keys[len(cur.keys) - 1]; let v = cur.vals[len(cur.vals) - 1]; delete(cur.keys[len(cur.keys) - 1]); delete(cur.vals[len(cur.vals) - 1]); return (k, v); }; fn pop_min(m: *map, x: *node) ([]u8, *opaque) = { let cur = x; for (!cur.leaf) { ensure_child_has_space(m, cur, 0); cur = cur.children[0]; }; let k = cur.keys[0]; let v = cur.vals[0]; delete(cur.keys[0]); delete(cur.vals[0]); return (k, v); }; fn delete_rec(m: *map, x: *node, key: []u8) (*opaque | void) = { let i = sort::lbisect((x.keys: []const opaque), size([]u8), (&key: const *opaque), &cmp_u8slice); if (i < len(x.keys) && bytes::equal(x.keys[i], key)) { if (x.leaf) { let ret = x.vals[i];
free(x.keys[i]);
delete(x.keys[i]); delete(x.vals[i]); return ret; }; const t = m.t; let y = x.children[i]; let z = x.children[i + 1]; if (len(y.keys) >= t) { let (pk, pv) = pop_max(m, y); let ret = x.vals[i];
let oldk = x.keys[i];
x.keys[i] = pk; x.vals[i] = pv;
free(oldk);
return ret; } else if (len(z.keys) >= t) { let (sk, sv) = pop_min(m, z); let ret = x.vals[i];
let oldk = x.keys[i];
x.keys[i] = sk; x.vals[i] = sv;
free(oldk);
return ret; } else { merge_children(m, x, i); return delete_rec(m, y, key); }; }; if (x.leaf) { return; }; ensure_child_has_space(m, x, i); if (i >= len(x.children)) { i = len(x.children) - 1; }; return delete_rec(m, x.children[i], key); };
// Deletes an item from a [[map]]. Returns the removed value or void. export fn del(m: *map, key: []u8) (*opaque | void) = { let z = find_node(m, key); match (z) { case null => return; case let nodez: *node => let ret = nodez.val; let y = nodez; let y_orig = y.color; let x: nullable *node = null; let p_for_fix: nullable *node = null; if (nodez.left == null) { x = nodez.right; p_for_fix = nodez.parent; transplant(m, nodez, nodez.right); } else if (nodez.right == null) { x = nodez.left; p_for_fix = nodez.parent; transplant(m, nodez, nodez.left); } else { let r = match (nodez.right) { case let rr: *node => yield rr; case null => abort("rb invariant violated: del: right is null"); }; let s = subtree_min(r); y = s; let yor = y.color; y_orig = yor; x = y.right; if (y.parent == (nodez: nullable *node)) { p_for_fix = y; set_parent(x, y); } else { p_for_fix = y.parent; transplant(m, y, y.right); y.right = nodez.right; set_parent(y.right, y); }; transplant(m, nodez, y); y.left = nodez.left; set_parent(y.left, y); y.color = nodez.color; };
free(nodez.key);
free(nodez); if (y_orig == color::BLACK) { delete_fixup(m, x, p_for_fix); }; return ret; }; };
fn free_subtree(n: nullable *node) void = { match (n) { case null => return; case let p: *node => free_subtree(p.left); free_subtree(p.right);
free(p.key);
free(p); }; }; // Frees resources associated with a [[map]]. export fn finish(m: *map) void = { free_subtree(m.root); free(m); };
// SPDX-License-Identifier: MPL-2.0
use bytes;
fn dup_u8(src: []u8) ([]u8 | nomem) = { return match (alloc(src, len(src))) { case let b: []u8 => yield b; case nomem => return nomem; }; };
export fn set(m: *map, key: []u8, value: *opaque) (void | nomem) = { match (find_node(m, key)) { case let ex: *node => ex.val = value; return; case null => void; };
let kcopy = match (dup_u8(key)) { case let b: []u8 => yield b; case nomem => return nomem; };
let z = alloc(node { color = color::RED,
key = key,
key = kcopy,
val = value, left = null, right = null, parent = null, })?; let y: nullable *node = null; let x = m.root; for (true) { match (x) { case null => break; case let xn: *node => y = xn; if (keycmp(z.key, xn.key) < 0) { x = xn.left; } else { x = xn.right; }; }; }; z.parent = y; match (y) { case null => m.root = z; case let yn: *node => if (keycmp(z.key, yn.key) < 0) { yn.left = z; } else { yn.right = z; }; }; insert_fixup(m, z); };
// SPDX-License-Identifier: MPL-2.0 use bytes; // Deletes an item from a [[map]]. Returns the removed value or void. export fn del(m: *map, key: []u8) (*opaque | void) = { for (let i = 0z; i < len(m.items); i += 1) { if (bytes::equal(m.items[i].0, key)) { let v = m.items[i].1;
free(m.items[i].0);
delete(m.items[i]); return v; }; }; };
// SPDX-License-Identifier: MPL-2.0 // Frees resources associated with a [[map]]. export fn finish(m: *map) void = {
for (let i = 0z; i < len(m.items); i += 1) { free(m.items[i].0); };
free(m.items); free(m); };
// SPDX-License-Identifier: MPL-2.0 use bytes;
fn dup_u8(src: []u8) ([]u8 | nomem) = { return match (alloc(src, len(src))) { case let b: []u8 => yield b; case nomem => return nomem; }; };
// 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) = { for (let i = 0z; i < len(m.items); i += 1) { if (bytes::equal(m.items[i].0, key)) { m.items[i].1 = value; return; }; };
append(m.items, (key, value))?;
let kb = match (dup_u8(key)) { case let b: []u8 => yield b; case nomem => return nomem; }; append(m.items, (kb, value))?;
};
use sort; // Deletes an item from a [[map]]. Returns the removed value or void. export fn del(m: *map, key: []u8) (*opaque | void) = { let dummy = 0; let probe = (key, (&dummy: *opaque)); match (sort::search((m.items: []const opaque), size(([]u8, *opaque)), (&probe: const *opaque), &cmp_kv)) { case let idx: size => let v = m.items[idx].1;
free(m.items[idx].0);
delete(m.items[idx]); return v; case void => return; }; };
// SPDX-License-Identifier: MPL-2.0
// Frees resources associated with a [[map]]. export fn finish(m: *map) void = {
for (let i = 0z; i < len(m.items); i += 1) { free(m.items[i].0); };
free(m.items); free(m); };
use sort;
fn dup_u8(src: []u8) ([]u8 | nomem) = { return match (alloc(src, len(src))) { case let b: []u8 => yield b; case nomem => return nomem; }; };
// 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 dummy = 0; let probe = (key, (&dummy: *opaque)); match (sort::search((m.items: []const opaque), size(([]u8, *opaque)), (&probe: const *opaque), &cmp_kv)) { case let idx: size => m.items[idx].1 = value; case void => let ins = sort::lbisect((m.items: []const opaque), size(([]u8, *opaque)), (&probe: const *opaque), &cmp_kv);
insert(m.items[ins], (key, value))?;
let kb = match (dup_u8(key)) { case let b: []u8 => yield b; case nomem => return nomem; }; insert(m.items[ins], (kb, value))?;
}; };
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 // SPDX-FileCopyrightText: 2024 The Cockroach Authors // SPDX-FileCopyrightText: 2025 Runxi Yu use bytes; // Deletes an item from a [[map]]. Returns the removed value or void. export fn del(m: *map, key: []u8) (*opaque | void) = { if (len(m.groups) == 0) return; let hv = m.hash64(m.hash_params, key): u64; let t = h2(hv); let mask = m.group_mask; let off: size = (h1(hv): size) & mask; let idx: size = 0; for (true) { let g = &m.groups[off]; for (let i = 0z; i < GROUP_SIZE; i += 1) { let c = g.ctrl[i]; if (is_full_ctrl(c) && c == t) { if (bytes::equal(g.keys[i], key)) { let v = g.vals[i]; g.ctrl[i] = CTRL_DELETED;
free(g.keys[i]);
g.keys[i] = []; g.vals[i] = null; m.used -= 1; m.tombs += 1;
// elide the tombstones if exceed 1/3 of the capacity
if (m.tombs * 3 >= capacity_slots(m)) { rehash_in_place(m); }; match (v) { case null => abort("map: null internal state escaped"); case let p: *opaque => return p; }; }; } else if (c == CTRL_EMPTY) { return; }; }; let next = probe_next(off, idx, mask); off = next.0; idx = next.1; }; };
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0 // SPDX-FileCopyrightText: 2024 The Cockroach Authors // SPDX-FileCopyrightText: 2025 Runxi Yu // Frees resources associated with a [[map]]. export fn finish(m: *map) void = { if (len(m.groups) != 0) {
for (let gi = 0z; gi <= m.group_mask; gi += 1) { let g = &m.groups[gi]; for (let si = 0z; si < GROUP_SIZE; si += 1) { let c = g.ctrl[si]; if (!is_full_ctrl(c)) continue; free(g.keys[si]); }; };
free(m.groups); }; free(m); };
// SPDX-License-Identifier: Apache-2.0 AND MPL-2.0
// SPDX-FileCopyrightText: 2024 The Cockroach Authors // SPDX-FileCopyrightText: 2025 Runxi Yu
use ds::map;
use bytes;
fn dup_u8(src: []u8) ([]u8 | nomem) = { return match (alloc(src, len(src))) { case let b: []u8 => yield b; case nomem => return nomem; }; };
// 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 need_insert = true; if (len(m.groups) != 0) { let hv0 = m.hash64(m.hash_params, key); let t0 = h2(hv0: u64); let mask0 = m.group_mask; let off0: size = (h1(hv0: u64): size) & mask0; let idx0: size = 0; need_insert = false; for (true) { let g = &m.groups[off0]; for (let i = 0z; i < GROUP_SIZE; i += 1) { let c = g.ctrl[i]; if (is_full_ctrl(c) && c == t0) { if (bytes::equal(g.keys[i], key)) { g.vals[i] = value; return; }; } else if (c == CTRL_EMPTY) { need_insert = true; break; }; }; if (need_insert) { break; }; let next = probe_next(off0, idx0, mask0); off0 = next.0; idx0 = next.1; }; } else { need_insert = true; }; if (!need_insert) { return; }; match (ensure_capacity_for_insert(m)) { case void => yield; case nomem => return nomem; }; let hv = m.hash64(m.hash_params, key): u64; let t = h2(hv); let mask = m.group_mask; let off: size = (h1(hv): size) & mask; let idx: size = 0; for (true) { let g = &m.groups[off]; let first_dead: (size | void) = void; for (let i = 0z; i < GROUP_SIZE; i += 1) { let c = g.ctrl[i]; if (is_full_ctrl(c)) { if (c == t && bytes::equal(g.keys[i], key)) { g.vals[i] = value; return; }; continue; } else if (c == CTRL_DELETED) { if (first_dead is void) first_dead = i; } else { let slot = match (first_dead) { case void => yield i; case let di: size => yield di; };
g.keys[slot] = key;
let kb = match (dup_u8(key)) { case let b: []u8 => yield b; case nomem => return nomem; }; g.keys[slot] = kb;
g.vals[slot] = value; g.ctrl[slot] = t; m.used += 1; if (slot != i) { m.tombs -= 1; }; return; }; }; let next = probe_next(off, idx, mask); off = next.0; idx = next.1; }; };