Lindenii Project Forge
Add tests
use ds::map; use errors; @test fn test() void = { const ts: [3]size = [2z, 3z, 8z]; for (let i = 0z; i < len(ts); i += 1) { let m: *map = match (new(ts[i])) { case let p: *map => yield p; case errors::invalid => abort("btree: invalid t"); case nomem => abort("btree: nomem"); }; defer finish(m); map::stress_test(m, 200000); }; };
use crypto::random; use ds::map; use ds::map::slice_basic; use ds::map::slice_sorted; use ds::map::btree; use ds::map::rbtree; use ds::map::swiss_siphash; use errors; fn mk_slice_basic() (*map::map | nomem) = { match (slice_basic::new()) { case let p: *slice_basic::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_slice_sorted() (*map::map | nomem) = { match (slice_sorted::new()) { case let p: *slice_sorted::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_btree2() (*map::map | nomem) = { match (btree::new(2)) { case let p: *btree::map => return (p: *map::map); case errors::invalid => abort("btree(2) invalid"); case nomem => return nomem; }; }; fn mk_rbtree() (*map::map | nomem) = { match (rbtree::new()) { case let p: *rbtree::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_swiss() (*map::map | nomem) = { let key: [16]u8 = [0...]; random::buffer(&key); match (swiss_siphash::new(1, key)) { case let p: *swiss_siphash::map => return (p: *map::map); case errors::invalid => abort("swiss: invalid"); case nomem => return nomem; }; }; @test fn test() void = { const buckets: [2]size = [128z, 256z]; const makers: [5]*fn() (*map::map | nomem) = [&mk_slice_basic, &mk_slice_sorted, &mk_btree2, &mk_rbtree, &mk_swiss]; for (let bi = 0z; bi < len(buckets); bi += 1) { for (let mi = 0z; mi < len(makers); mi += 1) { let m: *map = match (new(makers[mi], buckets[bi])) { case let p: *map => yield p; case errors::invalid => abort("fnv: invalid"); case nomem => abort("fnv: nomem"); }; defer finish(m); map::stress_test(m, 20000); }; }; };
use crypto::random; use ds::map; use ds::map::slice_basic; use ds::map::slice_sorted; use ds::map::btree; use ds::map::rbtree; use ds::map::swiss_siphash; use errors; use hash; use hash::fnv; use hash::siphash; fn test_fnv64(_params: nullable *opaque, key: []u8) size = { let h = fnv::fnv64a(); hash::write(&h, key); return fnv::sum64(&h): size; }; fn test_siphash64(params: nullable *opaque, key: []u8) size = { let k = match (params) { case null => abort("siphash test hash: missing key"); case let p: *opaque => yield (p: *[16]u8); }; let h = siphash::siphash(2, 4, k); defer hash::close(&h); hash::write(&h, key); return siphash::sum(&h): size; }; fn mk_slice_basic() (*map::map | nomem) = { match (slice_basic::new()) { case let p: *slice_basic::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_slice_sorted() (*map::map | nomem) = { match (slice_sorted::new()) { case let p: *slice_sorted::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_btree2() (*map::map | nomem) = { match (btree::new(2)) { case let p: *btree::map => return (p: *map::map); case errors::invalid => abort("btree(2) invalid"); case nomem => return nomem; }; }; fn mk_rbtree() (*map::map | nomem) = { match (rbtree::new()) { case let p: *rbtree::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_swiss() (*map::map | nomem) = { let key: [16]u8 = [0...]; random::buffer(&key); match (swiss_siphash::new(1, key)) { case let p: *swiss_siphash::map => return (p: *map::map); case errors::invalid => abort("swiss: invalid"); case nomem => return nomem; }; }; @test fn test() void = { const buckets: [2]size = [128z, 256z]; const makers: [5]*fn() (*map::map | nomem) = [&mk_slice_basic, &mk_slice_sorted, &mk_btree2, &mk_rbtree, &mk_swiss]; let skey1: [16]u8 = [0...]; let skey2: [16]u8 = [0...]; random::buffer(&skey1); random::buffer(&skey2); const hf: [2]*fn(hash_params: nullable *opaque, key: []u8) size = [&test_fnv64, &test_siphash64]; const hp: [2]nullable *opaque = [null, (&skey1: *opaque)]; for (let bi = 0z; bi < len(buckets); bi += 1) { for (let hi = 0z; hi < len(hf); hi += 1) { for (let mi = 0z; mi < len(makers); mi += 1) { let m: *map = match (new(makers[mi], buckets[bi], hf[hi], hp[hi])) { case let p: *map => yield p; case errors::invalid => abort("hashmap: invalid"); case nomem => abort("hashmap: nomem"); }; defer finish(m); map::stress_test(m, 20000); }; }; }; };
use ds::map; @test fn test() void = { let m: *map = match (new()) { case let p: *map => yield p; case nomem => abort("rbtree: nomem"); }; defer finish(m); map::stress_test(m, 200000); };
use crypto::random; use ds::map; use ds::map::slice_basic; use ds::map::slice_sorted; use ds::map::btree; use ds::map::rbtree; use ds::map::swiss_siphash; use errors; fn mk_slice_basic() (*map::map | nomem) = { match (slice_basic::new()) { case let p: *slice_basic::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_slice_sorted() (*map::map | nomem) = { match (slice_sorted::new()) { case let p: *slice_sorted::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_btree2() (*map::map | nomem) = { match (btree::new(2)) { case let p: *btree::map => return (p: *map::map); case errors::invalid => abort("btree(2) invalid"); case nomem => return nomem; }; }; fn mk_rbtree() (*map::map | nomem) = { match (rbtree::new()) { case let p: *rbtree::map => return (p: *map::map); case nomem => return nomem; }; }; fn mk_swiss() (*map::map | nomem) = { let key: [16]u8 = [0...]; random::buffer(&key); match (swiss_siphash::new(1, key)) { case let p: *swiss_siphash::map => return (p: *map::map); case errors::invalid => abort("swiss: invalid"); case nomem => return nomem; }; }; @test fn test() void = { const buckets: [2]size = [128z, 256z]; const makers: [5]*fn() (*map::map | nomem) = [&mk_slice_basic, &mk_slice_sorted, &mk_btree2, &mk_rbtree, &mk_swiss]; let key1: [16]u8 = [0...]; let key2: [16]u8 = [0...]; random::buffer(&key1); random::buffer(&key2); const keys: [2]*[16]u8 = [&key1, &key2]; for (let bi = 0z; bi < len(buckets); bi += 1) { for (let ki = 0z; ki < len(keys); ki += 1) { for (let mi = 0z; mi < len(makers); mi += 1) { let m: *map = match (new(makers[mi], buckets[bi], *keys[ki])) { case let p: *map => yield p; case errors::invalid => abort("siphash: invalid"); case nomem => abort("siphash: nomem"); }; defer finish(m); map::stress_test(m, 20000); }; }; }; };
use ds::map; @test fn test() void = { let m: *map = match (new()) { case let p: *map => yield p; case nomem => abort("slice_basic: nomem"); }; defer finish(m); map::stress_test(m, 2000); };
use ds::map; @test fn test() void = { let m: *map = match (new()) { case let p: *map => yield p; case nomem => abort("slice_sorted: nomem"); }; defer finish(m); map::stress_test(m, 1000); };
use crypto::random; use ds::map; use errors; @test fn test() void = { let key1: [16]u8 = [0...]; let key2: [16]u8 = [0...]; random::buffer(&key1); random::buffer(&key2); const groups: [2]size = [1z, 32z]; const keys: [2]*[16]u8 = [&key1, &key2]; for (let gi = 0z; gi < len(groups); gi += 1) { for (let ki = 0z; ki < len(keys); ki += 1) { let m: *map = match (new(groups[gi], *keys[ki])) { case let p: *map => yield p; case errors::invalid => abort("swiss_siphash: invalid groups"); case nomem => abort("swiss_siphash: nomem"); }; defer finish(m); map::stress_test(m, 200000); }; }; };
// SPDX-License-Identifier: MPL-2.0 def KEY_LEN: size = 16z; fn put_le64(dst: []u8, x: u64) void = { for (let i = 0z; i < 8z; i += 1) { dst[i] = ((x >> (8u64 * (i: u64))) & 0xFFu64): u8; }; }; type oracle = []nullable *opaque; fn must_set(m: *map, key: []u8, v: *opaque) void = { match (set(m, key, v)) { case void => void; case nomem => abort("set: out of memory"); }; }; fn must_get(m: *map, key: []u8) (*opaque | void) = get(m, key); fn must_del(m: *map, key: []u8) (*opaque | void) = del(m, key); export fn stress_test(m: *map, key_space: size) void = { let empty: [KEY_LEN]u8 = [0...]; let keybufs: [][KEY_LEN]u8 = alloc([empty...], key_space)!; let keys: [][]u8 = alloc([[0...]...], key_space)!; for (let i = 0z; i < key_space; i += 1) { for (let j = 8z; j < KEY_LEN; j += 1) keybufs[i][j] = 0xABu8; put_le64((keybufs[i][..8]), (i: u64)); keys[i] = keybufs[i][..]; }; let vals: []int = alloc([0...], key_space)!; for (let i = 0z; i < key_space; i += 1) vals[i] = (i: int); let exp: oracle = alloc([null...], key_space)!; // Sequential inserts with immediate verification for (let i = 0z; i < key_space; i += 1) { let vp: *opaque = (&vals[i]: *opaque); must_set(m, keys[i], vp); exp[i] = vp; match (must_get(m, keys[i])) { case let got: *opaque => assert(got == vp, "phase1: get != set"); case void => abort("phase1: get void after set"); }; }; // Forward read sweep (all should be present) for (let i = 0z; i < key_space; i += 1) { match (must_get(m, keys[i])) { case let got: *opaque => let want = match (exp[i]) { case null => abort("phase2: expect null but found value"); case let p: *opaque => yield p; }; assert(got == want, "phase2: value mismatch"); case void => abort("phase2: get void but expect value"); }; }; // Strided overwrites with immediate verification for (let step = 0z; step < key_space; step += 1) { let i = (step * 7z) % key_space; let new_ix = (i + 12345z) % key_space; let vp: *opaque = (&vals[new_ix]: *opaque); must_set(m, keys[i], vp); exp[i] = vp; match (must_get(m, keys[i])) { case let got: *opaque => assert(got == vp, "phase3: replace mismatch"); case void => abort("phase3: get void after replace"); }; }; // Sparse deletes, every 3rd key for (let i = 0z; i < key_space; i += 1) { if (i % 3z != 0z) continue; let want = exp[i]; match (must_del(m, keys[i])) { case let ret: *opaque => match (want) { case null => abort("phase4: del returned value but expect null"); case let p: *opaque => assert(ret == p, "phase4: del value mismatch"); }; exp[i] = null; match (must_del(m, keys[i])) { case void => void; case *opaque => abort("phase4: second delete returned value"); }; case void => match (want) { case null => void; case *opaque => abort("phase4: del void but expect value"); }; }; }; // Insert again every 6th key for (let i = 0z; i < key_space; i += 1) { if (i % 6z != 0z) continue; let vp: *opaque = (&vals[(i * 5z + 1z) % key_space]: *opaque); must_set(m, keys[i], vp); exp[i] = vp; }; // Even indices read, odd indices write for (let i = 0z; i < key_space; i += 1) { if ((i & 1z) == 0z) { // Read check match (must_get(m, keys[i])) { case let got: *opaque => match (exp[i]) { case null => abort("phase6: even get found value; expect null"); case let p: *opaque => assert(got == p, "phase6: even mismatch"); }; case void => match (exp[i]) { case null => void; case *opaque => abort("phase6: even get void; expect value"); }; }; } else { // Write (overwrite or create) let vp: *opaque = (&vals[(i * 3z + 7z) % key_space]: *opaque); must_set(m, keys[i], vp); exp[i] = vp; }; }; // Reading in reverse order for (let r = key_space; r > 0z; r -= 1) { let i = r - 1z; match (must_get(m, keys[i])) { case let got: *opaque => match (exp[i]) { case null => abort("phase7: get found value; expect null"); case let p: *opaque => assert(got == p, "phase7: reverse mismatch"); }; case void => match (exp[i]) { case null => void; case *opaque => abort("phase7: reverse get void; expect value"); }; }; }; // Clear in reverse order for (let r = key_space; r > 0z; r -= 1) { let i = r - 1z; match (must_del(m, keys[i])) { case let ret: *opaque => match (exp[i]) { case null => abort("phase8: del returned value; expect null"); case let p: *opaque => assert(ret == p, "phase8: final del mismatch"); }; exp[i] = null; case void => match (exp[i]) { case null => void; case *opaque => abort("phase8: del void; expect value"); }; }; }; // Read sweep ensure empty for (let i = 0z; i < key_space; i += 1) { match (must_get(m, keys[i])) { case void => void; case *opaque => abort("final: get returned value after full clear"); }; match (must_del(m, keys[i])) { case void => void; case *opaque => abort("final: del returned value after full clear"); }; }; };