# hashtable - 开放寻址哈希表库 # 使用线性探测处理冲突,支持自动扩容和墓碑删除 # 键类型固定为 str (i8*),值类型为 void* (通用指针) import t, c import string import memhub from stdint import * # 空槽标记 (hash == 0) _HT_EMPTY: t.CDefine = 0 # 墓碑标记 (hash == 1) _HT_TOMBSTONE: t.CDefine = 1 # 默认初始容量 (必须是 2 的幂) _HT_INIT_CAP: t.CDefine = 16 # 槽大小: hash(8) + key(8) + value(8) = 24 字节 _HT_SLOT_SIZE: t.CDefine = 24 # FNV-1a 64-bit 常量 _FNV_OFFSET: t.CUInt64T = 0xcbf29ce484222325 _FNV_PRIME: t.CUInt64T = 0x100000001b3 def _fnv1a_hash(key: str) -> t.CUInt64T: """FNV-1a 64-bit 哈希函数""" h: t.CUInt64T = _FNV_OFFSET k: str = key while k[0] != 0: h = h ^ t.CUInt64T(k[0]) h = h * _FNV_PRIME k += 1 # 保证 h >= 2 (避开空槽 0 和墓碑 1) if h < 2: h = 2 return h class HashTable: """开放寻址哈希表,使用线性探测处理冲突。 键类型固定为 str (i8*),值类型为 void* (通用指针)。 使用 mbuddy 分配器管理内存。负载因子超过 0.75 时自动扩容。 用法: mb: memhub.MemBuddy = memhub.MemBuddy(arena, arena_size) ht: HashTable = HashTable(mb) ht.set_int("x", 42) p: t.CPtr = ht["x"] v: int = c.Deref(p) """ __slots__: t.CVoid | t.CPtr # 槽位数组基址 __count__: t.CSizeT = 0 # 已用条目数 __capacity__: t.CSizeT = 16 # 槽位总数 (2 的幂) __tombstones__: t.CSizeT = 0 # 墓碑数 __mbuddy__: memhub.MemBuddy | t.CPtr __iter_index__: t.CSizeT = 0 def __new__(self, mb: memhub.MemBuddy | t.CPtr) -> t.CPtr: # 堆分配 HashTable 对象 (6 字段 x 8 字节 = 48 字节) return mb.alloc(48) def __init__(self, mb: memhub.MemBuddy | t.CPtr): self.__mbuddy__ = mb self.__capacity__ = _HT_INIT_CAP self.__count__ = 0 self.__tombstones__ = 0 self.__slots__ = mb.alloc(self.__capacity__ * _HT_SLOT_SIZE) # 初始化所有槽为空 (hash = 0) string.memset(self.__slots__, 0, self.__capacity__ * _HT_SLOT_SIZE) def _slot_hash(self, idx: t.CSizeT) -> t.CUInt64T: """读取槽位的 hash 值""" ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) return ptr[0] def _slot_key(self, idx: t.CSizeT) -> str: """读取槽位的 key 指针""" ptr: str | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 8) return ptr[0] def _slot_value(self, idx: t.CSizeT) -> t.CPtr: """读取槽位的 value 指针""" ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 16) return ptr[0] def _set_slot(self, idx: t.CSizeT, h: t.CUInt64T, key: str, val: t.CPtr): """设置槽位的三个字段""" h_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) h_ptr[0] = h k_ptr: str | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 8) k_ptr[0] = key v_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE + 16) v_ptr[0] = val def _find_slot(self, key: str, h: t.CUInt64T) -> t.CSizeT: """查找 key 应该插入或已存在的槽位索引。 返回: 如果找到 key,返回其索引;否则返回第一个可插入的空槽/墓碑索引。 """ mask: t.CSizeT = self.__capacity__ - 1 idx: t.CSizeT = t.CSizeT(h) & mask first_tombstone: t.CSizeT = self.__capacity__ i: t.CSizeT = 0 while i < self.__capacity__: sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY: if first_tombstone < self.__capacity__: return first_tombstone return idx if sh == _HT_TOMBSTONE: if first_tombstone >= self.__capacity__: first_tombstone = idx else: if sh == h: sk: str = self._slot_key(idx) if sk == key: return idx idx = (idx + 1) & mask i += 1 if first_tombstone < self.__capacity__: return first_tombstone return self.__capacity__ def _resize(self, new_cap: t.CSizeT): """扩容并重新哈希所有条目""" old_slots: t.CVoid | t.CPtr = self.__slots__ old_cap: t.CSizeT = self.__capacity__ new_slots: t.CVoid | t.CPtr = self.__mbuddy__.alloc(new_cap * _HT_SLOT_SIZE) if new_slots == None: return string.memset(new_slots, 0, new_cap * _HT_SLOT_SIZE) self.__slots__ = new_slots self.__capacity__ = new_cap self.__count__ = 0 self.__tombstones__ = 0 i: t.CSizeT = 0 while i < old_cap: old_h_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE) h: t.CUInt64T = old_h_ptr[0] if h != _HT_EMPTY and h != _HT_TOMBSTONE: old_k_ptr: str | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE + 8) old_v_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(old_slots) + i * _HT_SLOT_SIZE + 16) k: str = old_k_ptr[0] v: t.CPtr = old_v_ptr[0] idx: t.CSizeT = self._find_slot(k, h) self._set_slot(idx, h, k, v) self.__count__ += 1 i += 1 def __setitem__(self, key: str, val: t.CNeedPtr): """插入或更新键值对。val 自动取地址 (t.CNeedPtr: 已是指针则不取)。""" h: t.CUInt64T = _fnv1a_hash(key) idx: t.CSizeT = self._find_slot(key, h) if idx >= self.__capacity__: return sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY or sh == _HT_TOMBSTONE: if sh == _HT_TOMBSTONE: self.__tombstones__ -= 1 self._set_slot(idx, h, key, val) self.__count__ += 1 # 负载因子 > 0.75 时扩容 load: t.CSizeT = self.__count__ + self.__tombstones__ if load * 4 >= self.__capacity__ * 3: self._resize(self.__capacity__ * 2) else: self._set_slot(idx, h, key, val) def __getitem__(self, key: str) -> t.CPtr: """查找 key,返回值指针。不存在返回 None。""" h: t.CUInt64T = _fnv1a_hash(key) idx: t.CSizeT = self._find_slot(key, h) if idx >= self.__capacity__: return None sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY or sh == _HT_TOMBSTONE: return None return self._slot_value(idx) def __contains__(self, key: str) -> t.CInt: """检查 key 是否存在。""" h: t.CUInt64T = _fnv1a_hash(key) idx: t.CSizeT = self._find_slot(key, h) if idx >= self.__capacity__: return 0 sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY or sh == _HT_TOMBSTONE: return 0 return 1 def __delitem__(self, key: str) -> t.CInt: """删除 key。成功返回 1,key 不存在返回 0。""" h: t.CUInt64T = _fnv1a_hash(key) idx: t.CSizeT = self._find_slot(key, h) if idx >= self.__capacity__: return 0 sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY or sh == _HT_TOMBSTONE: return 0 h_ptr: t.CUInt64T | t.CPtr = (t.CVoid | t.CPtr)(t.CUInt64T(self.__slots__) + idx * _HT_SLOT_SIZE) h_ptr[0] = _HT_TOMBSTONE self.__count__ -= 1 self.__tombstones__ += 1 return 1 def __len__(self) -> t.CSizeT: return self.__count__ def __iter__(self) -> HashTable | t.CPtr: self.__iter_index__ = 0 return self def __next__(self) -> str: while self.__iter_index__ < self.__capacity__: idx: t.CSizeT = self.__iter_index__ self.__iter_index__ = idx + 1 sh: t.CUInt64T = self._slot_hash(idx) if sh != _HT_EMPTY and sh != _HT_TOMBSTONE: return self._slot_key(idx) raise StopIteration def get(self, key: str, default: t.CPtr) -> t.CPtr: """返回值指针,不存在则返回 default。""" h: t.CUInt64T = _fnv1a_hash(key) idx: t.CSizeT = self._find_slot(key, h) if idx >= self.__capacity__: return default sh: t.CUInt64T = self._slot_hash(idx) if sh == _HT_EMPTY or sh == _HT_TOMBSTONE: return default return self._slot_value(idx) def set_int(self, key: str, val: int): """存储 int 值。值复制到 mbuddy 持久存储。""" storage: t.CVoid | t.CPtr = self.__mbuddy__.alloc(8) int_ptr: int | t.CPtr = t.CVoid(t.CUInt64T(storage), t.CPtr) int_ptr[0] = val self[key] = storage def set_str(self, key: str, val: str): """存储 str 值。直接存储字符串指针。""" self[key] = val