Lindenii Project Forge
Add map_splice_{basic,sorted}
map_slice_basic: trivial key-value map backed by a single slice
// 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; delete(m.items[i]); return v; }; }; };
// SPDX-License-Identifier: MPL-2.0 // Frees resources associated with a [[map]]. export fn finish(m: *map) void = { free(m.items); free(m); };
// SPDX-License-Identifier: MPL-2.0 use bytes; // Gets an item from a [[map]] by key, returning void if not found. export fn get(m: *map, key: []u8) (*opaque | void) = { for (let i = 0z; i < len(m.items); i += 1) { if (bytes::equal(m.items[i].0, key)) { return m.items[i].1; }; }; };
// SPDX-License-Identifier: MPL-2.0 use ds::map; // Slice-backed map from []u8 to *opaque using linear scanning. // // You are advised to create these with [[new]]. export type map = struct { vt: map::map, items: []([]u8, *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 // Creates a new [[map]]. export fn new() (*map | nomem) = { let m = alloc(map { vt = &_vt, items = [], })?; return m; };
// SPDX-License-Identifier: MPL-2.0 use bytes; // 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))?; };
use errors; use strings; use ds::map; @test fn roundtrip() void = { const key: [16]u8 = [0...]; let m: *map = match (new()) { case let p: *map => yield p; case nomem => abort("unexpected nomem"); }; defer finish(m); let v1 = 1, v2 = 2, v3 = 3; let p1: *opaque = (&v1: *opaque); let p2: *opaque = (&v2: *opaque); let p3: *opaque = (&v3: *opaque); let k1 = strings::toutf8("alpha"); let k2 = strings::toutf8("beta"); let k3 = strings::toutf8("gamma"); match (map::set(m, k1, p1)) { case void => yield; case nomem => abort("unexpected nomem in set(k1,p1)"); }; match (map::get(m, k1)) { case let got: *opaque => assert(got == p1, "get(k1) must return p1"); case void => abort("get(k1) unexpectedly void"); }; match (map::set(m, k1, p2)) { case void => yield; case nomem => abort("unexpected nomem in replace"); }; match (map::get(m, k1)) { case let got: *opaque => assert(got == p2, "replace must overwrite prior value"); case void => abort("get(k1) void after replace"); }; match (map::set(m, k2, p3)) { case void => yield; case nomem => abort("unexpected nomem in set(k2,p3)"); }; match (map::get(m, k3)) { case void => yield; case *opaque => abort("get(k3) must be void for missing key"); }; match (map::del(m, k2)) { case let got: *opaque => assert(got == p3, "del(k2) must return stored value"); case void => abort("del(k2) unexpectedly void"); }; match (map::del(m, k2)) { case void => yield; case *opaque => abort("del(k2) must be void after prior delete"); }; };
map_slice_sorted: sorted slice key-value store with binary search
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; delete(m.items[idx]); resort(m); return v; case void => return; }; };
// Frees resources associated with a [[map]]. export fn finish(m: *map) void = { free(m.items); free(m); };
use sort; // Gets an item from a [[map]] by key, returning void if not found. export fn get(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 => return m.items[idx].1; case void => return; }; };
use sort; fn cmp_kv(a: const *opaque, b: const *opaque) int = { let ka = (*(a: *([]u8, *opaque))).0; let kb = (*(b: *([]u8, *opaque))).0; return keycmp(ka, kb); }; 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 resort(m: *map) void = { sort::inplace((m.items: []opaque), size(([]u8, *opaque)), &cmp_kv); };
// SPDX-License-Identifier: MPL-2.0 use ds::map; // Sorted slice backed map from []u8 to *opaque. // Keys are ordered byte-wize lexicgraphically. // // You are advised to create these with [[new]]. export type map = struct { vt: map::map, items: []([]u8, *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);
// Creates a new [[map]]. export fn new() (*map | nomem) = { let m = alloc(map { vt = &_vt, items = [], })?; return m; };
use sort; // 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 => append(m.items, (key, value))?; }; resort(m); };
use errors; use strings; use ds::map; @test fn roundtrip() void = { const key: [16]u8 = [0...]; let m: *map = match (new()) { case let p: *map => yield p; case nomem => abort("unexpected nomem"); }; defer finish(m); let v1 = 1, v2 = 2, v3 = 3; let p1: *opaque = (&v1: *opaque); let p2: *opaque = (&v2: *opaque); let p3: *opaque = (&v3: *opaque); let k1 = strings::toutf8("alpha"); let k2 = strings::toutf8("beta"); let k3 = strings::toutf8("gamma"); match (map::set(m, k1, p1)) { case void => yield; case nomem => abort("unexpected nomem in set(k1,p1)"); }; match (map::get(m, k1)) { case let got: *opaque => assert(got == p1, "get(k1) must return p1"); case void => abort("get(k1) unexpectedly void"); }; match (map::set(m, k1, p2)) { case void => yield; case nomem => abort("unexpected nomem in replace"); }; match (map::get(m, k1)) { case let got: *opaque => assert(got == p2, "replace must overwrite prior value"); case void => abort("get(k1) void after replace"); }; match (map::set(m, k2, p3)) { case void => yield; case nomem => abort("unexpected nomem in set(k2,p3)"); }; match (map::get(m, k3)) { case void => yield; case *opaque => abort("get(k3) must be void for missing key"); }; match (map::del(m, k2)) { case let got: *opaque => assert(got == p3, "del(k2) must return stored value"); case void => abort("del(k2) unexpectedly void"); }; match (map::del(m, k2)) { case void => yield; case *opaque => abort("del(k2) must be void after prior delete"); }; };