// Expect retcode = 0 // 开放寻址 + tombstone 哈希表。键和值都按字节复制到表自己的存储。 // 键类型由 hash_fn / eq_fn 解释:回调收到指向键字节的指针(如 *usize / *ByteView)。 // 普通赋值只复制句柄(与 Vec 一样),所有权转移必须使用 take。 type .{ .Error, .Status, .make } = @import("error.spl"); type .{ .Vec } = @import("vec.spl"); type .{ .ByteView } = @import("byte_view.spl"); type .{ .hash_usize, .eq_usize, .hash_view, .eq_view } = @import("hash.spl"); type .{ .checked_add, .checked_mul, .grow_capacity } = @import("memory.spl"); type .{ .copy } = @import("../sys/mem.spl"); // 槽位状态:Empty/Occupied/Deleted(墓碑,仅在扩容重哈希时清除)。 // 负载因子 70/128。 type SlotState = enum { Empty, Occupied, Deleted, } fn state_code(state: SlotState): u8 { var s: SlotState = state; ret @enum2int(s) as u8; } // 查找/删除结果:显式区分命中与缺失,替代 bool 哨兵。 type LookupResult = enum { Found, NotFound, } type Map = @self; keys: Vec, vals: Vec, states: Vec, size: usize, key_size: usize, val_size: usize, hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool, fn blank(key_size: usize, key_align: usize, val_size: usize, val_align: usize, hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map { ret Map { .keys = Vec.blank(key_size, key_align), .vals = Vec.blank(val_size, val_align), .states = Vec.blank(1, 1), .size = 0, .key_size = key_size, .val_size = val_size, .hash_fn = hash_fn, .eq_fn = eq_fn, }; } fn new(key_size: usize, key_align: usize, val_size: usize, val_align: usize, hash_fn: fn(*u8): usize, eq_fn: fn(*u8, *u8): bool): Map { ret blank(key_size, key_align, val_size, val_align, hash_fn, eq_fn); } fn valid(self: Map): bool { if self.key_size == 0 || self.val_size == 0 { ret false; } if self.states.len != self.keys.len || self.states.len != self.vals.len { ret false; } if self.size > self.states.len { ret false; } ret true; } fn size(self: Map): usize { ret self.size; } fn capacity(self: Map): usize { ret self.states.cap; } // 迭代支持:遍历 [0, slot_count),slot_state 为 1 时 key_at/val_at 有效。 fn slot_count(self: Map): usize { ret self.states.len; } fn slot_state(self: Map, index: usize): SlotState { var code = self.states.data[index]; var occupied: SlotState = .Occupied; var deleted_state: SlotState = .Deleted; if code == state_code(occupied) { ret .Occupied; } if code == state_code(deleted_state) { ret .Deleted; } ret .Empty; } fn slot_occupied(state: SlotState): bool { match state { .Occupied => { ret true; }, .Empty => { ret false; }, .Deleted => { ret false; }, } ret false; } fn key_at(self: *Map, index: usize): *u8 { ret self.keys.data + index * self.key_size; } fn val_at(self: *Map, index: usize): *u8 { ret self.vals.data + index * self.val_size; } // 返回占用键的槽位;未找到返回 ~0。容量为 0 时直接未找到。 fn find_slot(self: *Map, key: *u8): usize { if self.states.cap == 0 { ret ~(0 as usize); } var mask = self.states.cap - 1; var i = self.hash_fn(key) & mask; var empty: SlotState = .Empty; var occupied: SlotState = .Occupied; while self.states.data[i] != state_code(empty) { if self.states.data[i] == state_code(occupied) && self.eq_fn(self.keys.data + i * self.key_size, key) { ret i; } i = (i + 1) & mask; } ret ~(0 as usize); } // 要求容量非零且键不存在,直接写入第一个空槽(重哈希专用)。 fn place(self: *Map, key: *u8, val: *u8) { var mask = self.states.cap - 1; var i = self.hash_fn(key) & mask; var empty: SlotState = .Empty; var occupied: SlotState = .Occupied; while self.states.data[i] != state_code(empty) { i = (i + 1) & mask; } copy((self.keys.data + i * self.key_size) as *any, key as *any, self.key_size); copy((self.vals.data + i * self.val_size) as *any, val as *any, self.val_size); self.states.data[i] = state_code(occupied); self.size = self.size + 1; } // 保证至少 need 个槽位。所有检查在旧表改动前完成,失败保持原表不变。 fn reserve(self: *Map, need: usize): Status { if !valid(self.*) { ret .Err(make(.InvalidArgument)); } if need <= self.states.cap { ret .Ok; } var new_cap = grow_capacity(self.states.cap, 8, need); var fresh = self.*; fresh.keys = Vec.blank(self.key_size, self.keys.elem_align); fresh.vals = Vec.blank(self.val_size, self.vals.elem_align); fresh.states = Vec.blank(1, 1); fresh.size = 0; match Vec.reserve(&fresh.keys, new_cap) { .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, } match Vec.reserve(&fresh.vals, new_cap) { .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, } match Vec.reserve(&fresh.states, new_cap) { .Ok => {}, .Err(error) => { drop(&fresh); ret .Err(error); }, } fresh.keys.len = new_cap; fresh.vals.len = new_cap; fresh.states.len = new_cap; var i: usize = 0; var empty: SlotState = .Empty; var occupied: SlotState = .Occupied; while i < new_cap { fresh.states.data[i] = state_code(empty); i = i + 1; } i = 0; while i < self.states.len { if self.states.data[i] == state_code(occupied) { place(&fresh, self.keys.data + i * self.key_size, self.vals.data + i * self.val_size); } i = i + 1; } var old_keys = self.keys; var old_vals = self.vals; var old_states = self.states; self.* = fresh; Vec.drop(&old_keys); Vec.drop(&old_vals); Vec.drop(&old_states); ret .Ok; } // 覆盖已存在的键;否则插入并可能扩容。 fn put(self: *Map, key: *u8, val: *u8): Status { if !valid(self.*) || key == null || val == null { ret .Err(make(.InvalidArgument)); } var existing = find_slot(self, key); if existing != ~(0 as usize) { copy((self.vals.data + existing * self.val_size) as *any, val as *any, self.val_size); ret .Ok; } if self.states.cap == 0 { match reserve(self, 8) { .Ok => {}, .Err(error) => { ret .Err(error); }, } } else { var next_size: usize = 0; match checked_add(self.size, 1) { .Ok(value) => { next_size = value; }, .Err(error) => { ret .Err(error); }, } var scaled: usize = 0; match checked_mul(next_size, 128) { .Ok(value) => { scaled = value; }, .Err(error) => { ret .Err(error); }, } if scaled / self.states.cap >= 70 { var need: usize = 0; match checked_mul(self.states.cap, 2) { .Ok(value) => { need = value; }, .Err(error) => { ret .Err(error); }, } match reserve(self, need) { .Ok => {}, .Err(error) => { ret .Err(error); }, } } } place(self, key, val); ret .Ok; } // 找到键则把值复制到 out 并返回 Found;否则 out 不变并返回 NotFound。 fn get(self: *Map, key: *u8, out: *u8): LookupResult { if !valid(self.*) || key == null || out == null { ret .NotFound; } var i = find_slot(self, key); if i == ~(0 as usize) { ret .NotFound; } copy(out as *any, (self.vals.data + i * self.val_size) as *any, self.val_size); ret .Found; } fn del(self: *Map, key: *u8): LookupResult { if !valid(self.*) || key == null { ret .NotFound; } var i = find_slot(self, key); if i == ~(0 as usize) { ret .NotFound; } var deleted_state: SlotState = .Deleted; self.states.data[i] = state_code(deleted_state); self.size = self.size - 1; ret .Found; } // 清空内容但保留容量。墓碑与负载计数一并复位。 fn clear(self: *Map) { var i: usize = 0; var empty: SlotState = .Empty; while i < self.states.len { self.states.data[i] = state_code(empty); i = i + 1; } self.size = 0; } fn drop(self: *Map) { Vec.drop(&self.keys); Vec.drop(&self.vals); Vec.drop(&self.states); self.size = 0; } fn take(source: *Map): Map { var result = source.*; source.keys = Vec.blank(source.key_size, source.keys.elem_align); source.vals = Vec.blank(source.val_size, source.vals.elem_align); source.states = Vec.blank(1, 1); source.size = 0; ret result; } // ---- 测试辅助 ---- fn expect_ok(result: Status) { match result { .Ok => {}, .Err(error) => { @assert(false, "unexpected Map error"); }, } } fn expect_invalid(result: Status) { match result { .Ok => { @assert(false, "expected Map error"); }, .Err(error) => { match error.kind { .InvalidArgument => {}, _ => { @assert(false, "unexpected Map error kind"); }, } }, } } fn count_occupied(m: *Map): usize { var n: usize = 0; var i: usize = 0; while i < slot_count(m.*) { if slot_occupied(slot_state(m.*, i)) { n = n + 1; } i = i + 1; } ret n; } fn found(m: *Map, key: *u8, out: *u8): bool { match get(m, key, out) { .Found => { ret true; }, .NotFound => { ret false; }, } ret false; } fn deleted(m: *Map, key: *u8): bool { match del(m, key) { .Found => { ret true; }, .NotFound => { ret false; }, } ret false; } fn make_int_map(): Map { ret new(@sizeof(usize), @alignof(usize), @sizeof(i32), @alignof(i32), hash_usize, eq_usize); } fn test_put_get_update() { var m = make_int_map(); @assert(size(m) == 0 && capacity(m) == 0, "empty map"); @assert(valid(m)); var key: usize = 0; var value: i32 = 0; var out: i32 = 0; expect_invalid(put(&m, null, &value as *u8)); expect_invalid(put(&m, &key as *u8, null)); @assert(size(m) == 0, "failed put preserves map"); key = 7; value = 70; expect_ok(put(&m, &key as *u8, &value as *u8)); @assert(size(m) == 1 && valid(m)); out = -1; @assert(found(&m, &key as *u8, &out as *u8) && out == 70, "insert then get"); value = 71; expect_ok(put(&m, &key as *u8, &value as *u8)); @assert(size(m) == 1, "update does not grow size"); out = -1; @assert(found(&m, &key as *u8, &out as *u8) && out == 71, "update overwrites"); key = 8; out = 123; @assert(!found(&m, &key as *u8, &out as *u8) && out == 123, "missing key preserves out"); // drop 后还有断言,故保持手动释放,不改为 defer。 drop(&m); @assert(size(m) == 0 && capacity(m) == 0, "drop resets map"); } fn test_growth_and_delete() { var m = make_int_map(); defer { drop(&m); } var i: i32 = 0; while i < 1000 { var key: usize = i as usize; var value: i32 = i * 3 - 7; expect_ok(put(&m, &key as *u8, &value as *u8)); i = i + 1; } @assert(size(m) == 1000); @assert(count_occupied(&m) == 1000, "all slots accounted"); i = 0; while i < 1000 { var key: usize = i as usize; var out: i32 = 0; @assert(found(&m, &key as *u8, &out as *u8), "get after growth"); @assert(out == i * 3 - 7, "value after growth"); i = i + 1; } i = 0; while i < 500 { var key: usize = i as usize; @assert(deleted(&m, &key as *u8)); i = i + 1; } @assert(size(m) == 500 && count_occupied(&m) == 500, "deleted slots not occupied"); i = 0; while i < 500 { var key: usize = i as usize; var out: i32 = 0; @assert(!found(&m, &key as *u8, &out as *u8), "deleted key missing"); i = i + 1; } // 墓碑槽位可被再次插入复用,且不会重复计数。 var key: usize = 12345; var value: i32 = 99; expect_ok(put(&m, &key as *u8, &value as *u8)); @assert(size(m) == 501); var out: i32 = 0; @assert(found(&m, &key as *u8, &out as *u8) && out == 99, "reuse tombstone"); @assert(deleted(&m, &key as *u8), "delete key"); @assert(size(m) == 500); @assert(!found(&m, &key as *u8, &out as *u8)); clear(&m); @assert(size(m) == 0 && count_occupied(&m) == 0, "clear empties"); } fn test_take_and_self_put() { var m = make_int_map(); defer { drop(&m); } var key: usize = 5; var value: i32 = 55; expect_ok(put(&m, &key as *u8, &value as *u8)); var moved = take(&m); defer { drop(&moved); } @assert(size(m) == 0 && capacity(m) == 0, "take leaves source empty"); var out: i32 = 0; @assert(found(&moved, &key as *u8, &out as *u8) && out == 55, "moved keeps content"); // 值指针可以指向表内已有的值(按字节移动,允许重叠)。 var alias: i32 = 0; @assert(found(&moved, &key as *u8, &alias as *u8)); expect_ok(put(&moved, &key as *u8, &alias as *u8)); @assert(found(&moved, &key as *u8, &out as *u8) && out == 55, "self value put"); } fn test_byte_view_keys() { var m = new(@sizeof(ByteView), @alignof(ByteView), @sizeof(i32), @alignof(i32), hash_view, eq_view); defer { drop(&m); } var alpha = ByteView.from_slice("alpha"); var beta = ByteView.from_slice("beta"); var gamma = ByteView.from_slice("gamma"); var one: i32 = 1; var two: i32 = 2; var three: i32 = 3; expect_ok(put(&m, &alpha as *u8, &one as *u8)); expect_ok(put(&m, &beta as *u8, &two as *u8)); expect_ok(put(&m, &gamma as *u8, &three as *u8)); @assert(size(m) == 3); var out: i32 = 0; @assert(found(&m, &alpha as *u8, &out as *u8) && out == 1, "alpha"); @assert(found(&m, &beta as *u8, &out as *u8) && out == 2, "beta"); @assert(found(&m, &gamma as *u8, &out as *u8) && out == 3, "gamma"); // 内容相同的不同 ByteView 视为同一个键。 var alpha2 = ByteView.from_slice("alpha"); out = 0; @assert(found(&m, &alpha2 as *u8, &out as *u8) && out == 1, "equal content key"); @assert(size(m) == 3, "equal content does not insert"); @assert(deleted(&m, &beta as *u8), "delete beta"); @assert(size(m) == 2); @assert(!found(&m, &beta as *u8, &out as *u8)); } fn main() { test_put_get_update(); test_growth_and_delete(); test_take_and_self_put(); test_byte_view_keys(); }