(Probably) better hashGeneric (#1187)

* Make hash function more complicated

* Change seed value for array and vector hashes

* Fix formatting issues

* Fix formatting issues

* Update src/server/terrain/biomes.zig
This commit is contained in:
Krzysztof Wiśniewski 2025-03-11 19:45:57 +01:00 committed by GitHub
parent b13e835a21
commit 0472091d7c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -142,16 +142,22 @@ const Stripe = struct { // MARK: Stripe
fn hashGeneric(input: anytype) u64 {
const T = @TypeOf(input);
return switch(@typeInfo(T)) {
.bool => @intFromBool(input),
.@"enum" => @intFromEnum(input),
.int, .float => @as(std.meta.Int(.unsigned, @bitSizeOf(T)), @bitCast(input)),
.bool => hashCombine(hashInt(@intFromBool(input)), 0xbf58476d1ce4e5b9),
.@"enum" => hashCombine(hashInt(@as(u64, @intFromEnum(input))), 0x94d049bb133111eb),
.int, .float => blk: {
const value = @as(std.meta.Int(.unsigned, @bitSizeOf(T)), @bitCast(input));
break :blk hashInt(@as(u64, value));
},
.@"struct" => blk: {
if(@hasDecl(T, "getHash")) {
break :blk input.getHash();
}
var result: u64 = 0;
var result: u64 = hashGeneric(@typeName(T));
inline for(@typeInfo(T).@"struct".fields) |field| {
result ^= hashGeneric(@field(input, field.name))*%hashGeneric(@as([]const u8, field.name));
const keyHash = hashGeneric(@as([]const u8, field.name));
const valueHash = hashGeneric(@field(input, field.name));
const keyValueHash = hashCombine(keyHash, valueHash);
result = hashCombine(result, keyValueHash);
}
break :blk result;
},
@ -164,25 +170,28 @@ fn hashGeneric(input: anytype) u64 {
break :blk hashGeneric(input.*);
},
.slice => blk: {
var result: u64 = 0;
var result: u64 = hashInt(input.len);
for(input) |val| {
result = result*%33 +% hashGeneric(val);
const valueHash = hashGeneric(val);
result = hashCombine(result, valueHash);
}
break :blk result;
},
else => @compileError("Unsupported type " ++ @typeName(T)),
},
.array => blk: {
var result: u64 = 0;
var result: u64 = 0xbf58476d1ce4e5b9;
for(input) |val| {
result = result*%33 +% hashGeneric(val);
const valueHash = hashGeneric(val);
result = hashCombine(result, valueHash);
}
break :blk result;
},
.vector => blk: {
var result: u64 = 0;
var result: u64 = 0x94d049bb133111eb;
inline for(0..@typeInfo(T).vector.len) |i| {
result = result*%33 +% hashGeneric(input[i]);
const valueHash = hashGeneric(input[i]);
result = hashCombine(result, valueHash);
}
break :blk result;
},
@ -190,6 +199,20 @@ fn hashGeneric(input: anytype) u64 {
};
}
// https://stackoverflow.com/questions/5889238/why-is-xor-the-default-way-to-combine-hashes
fn hashCombine(left: u64, right: u64) u64 {
return left ^ (right +% 0x517cc1b727220a95 +% (left << 6) +% (left >> 2));
}
// https://stackoverflow.com/questions/664014/what-integer-hash-function-are-good-that-accepts-an-integer-hash-key
fn hashInt(input: u64) u64 {
var x = input;
x = (x ^ (x >> 30))*%0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27))*%0x94d049bb133111eb;
x = x ^ (x >> 31);
return x;
}
pub const Interpolation = enum(u8) {
none,
linear,