Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
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");
};
};