Files
spl/stage2/utils/hash.spl
T

90 lines
2.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Expect retcode = 0
// 哈希工具:FNV-1a 字节哈希 + 整型混合;并给出可直接交给 map 的回调
// (map 的 hash/eq 回调接收指向键字节的指针,这里按 usize / ByteView 解释)。
// 仅提供确定性哈希与等值,不承诺密码学性质。
type .{ .ByteView } = @import("byte_view.spl");
// FNV-1a 64 位,逐字节;空输入返回 offset basis。
fn hash_bytes(data: *u8, len: usize): usize {
var h: usize = 14695981039346656037; // FNV-1a 64 位 offset basis
var prime: usize = 1099511628211; // FNV 质数
var i: usize = 0;
while i < len {
h = (h ^ (data[i] as usize)) * prime;
i = i + 1;
}
ret h;
}
fn hash_byte_view(view: ByteView): usize {
ret hash_bytes(view.ptr, view.len);
}
// 整型混合(splitmix 风格的移位-乘法),保证非零与良好扩散。
fn hash_usize_value(value: usize): usize {
var prime: usize = 1099511628211; // splitmix 风格的混合乘子
var h = value;
h = (h ^ (h >> 30)) * prime;
h = h ^ (h >> 27);
h = h * prime;
h = h ^ (h >> 31);
if h == 0 { ret prime; } // 0 映射到非零
ret h;
}
// 键按 usize 解释。
fn hash_usize(key: *u8): usize {
ret hash_usize_value((key as *usize).*);
}
fn eq_usize(a: *u8, b: *u8): bool {
ret (a as *usize).* == (b as *usize).*;
}
// 键按 ByteView 解释,哈希其指向的字节。
fn hash_view(key: *u8): usize {
var view = (key as *ByteView).*;
ret hash_byte_view(view);
}
fn eq_view(a: *u8, b: *u8): bool {
ret ByteView.eq((a as *ByteView).*, (b as *ByteView).*);
}
fn expect_hash(view: []u8, expected: usize) {
var got = hash_bytes(view.ptr, view.len);
@assert(got == expected, "hash value");
}
fn test_fnv_vectors() {
expect_hash("", 14695981039346656037);
expect_hash("a", 12638187200555641996);
expect_hash("foobar", 9625390261332436968);
expect_hash("hello", 11831194018420276491);
// 同一输入稳定;不同输入不同。
var s1: []u8 = "abc";
var s2: []u8 = "abd";
@assert(hash_bytes(s1.ptr, 3) == hash_bytes(s1.ptr, 3), "deterministic");
@assert(hash_bytes(s1.ptr, 3) != hash_bytes(s2.ptr, 3), "distinct");
// ByteView 与 bytes 一致。
var v = ByteView.from_slice("foobar");
@assert(hash_byte_view(v) == hash_bytes(v.ptr, v.len), "view == bytes");
// 整型混合:0 不保持 0,且稳定/区分。
@assert(hash_usize_value(0) != 0, "zero mixed");
@assert(hash_usize_value(7) == hash_usize_value(7), "int stable");
@assert(hash_usize_value(7) != hash_usize_value(8), "int distinct");
}
// map 回调(hash_usize/eq_usize/hash_view/eq_view)由 map.spl 在自己的测试里验证。
fn main() {
test_fnv_vectors();
}