pub fn valueWithHash(s: *Smith, T: type, hash: u32) T {
@disableInstrumentation();
return switch (@typeInfo(T)) {
.void => {},
.bool, .int, .float => full: {
var int: Backing(T) = 0;
comptime var biti = 0;
var rhash = hash; // 'running' hash
inline while (biti < @bitSizeOf(T)) {
const n = @min(@bitSizeOf(T) - biti, 64);
const P = @Int(.unsigned, n);
int |= @as(
@TypeOf(int),
s.valueWeightedWithHash(P, baselineWeights(P), rhash),
) << biti;
biti += n;
rhash = std.hash.int(rhash);
}
break :full @bitCast(int);
},
.@"enum" => |e| if (e.mode == .exhaustive) v: {
if (@bitSizeOf(e.tag_type) <= 64) {
break :v s.valueWeightedWithHash(T, baselineWeights(T), hash);
}
break :v std.enums.fromInt(T, s.valueWithHash(e.tag_type, hash)) orelse
@fromBackingInt(@intCast(e.field_values[0]));
} else @fromBackingInt(@intCast(s.valueWithHash(e.tag_type, hash))),
.optional => |o| if (s.valueWithHash(bool, hash))
null
else
s.valueWithHash(o.child, std.hash.int(hash)),
inline .array, .vector => |a| arr: {
var arr: [a.len]a.child = undefined; // `T` cannot be used due to the vector case
if (a.child != u8) {
for (&arr) |*v| {
v.* = s.valueWithHash(a.child, hash);
}
} else {
s.bytesWithHash(&arr, hash);
}
break :arr arr;
},
.@"struct" => |st| if (!allBitPatternsValid(T)) v: {
var v: T = undefined;
var rhash = hash;
inline for (st.field_names, st.field_types) |f_name, f_type| {
// rhash is incremented in the call so our rhash state is not reused (e.g. with
// two nested structs. note that xor cannot work for this case as the bit would
// be flipped back here)
@field(v, f_name) = s.valueWithHash(f_type, rhash +% 1);
rhash = std.hash.int(rhash);
}
break :v v;
} else @bitCast(s.valueWithHash(st.backing_integer.?, hash)),
.@"union" => if (!allBitPatternsValid(T))
switch (s.valueWithHash(
UnionTagWithoutUninitializable(T),
// hash is incremented in the call so our hash state is not reused for below
std.hash.int(hash +% 1),
)) {
inline else => |t| @unionInit(
T,
@tagName(t),
s.valueWithHash(@FieldType(T, @tagName(t)), hash),
),
}
else
@bitCast(s.valueWithHash(Backing(T), hash)),
else => @compileError("unexpected type '" ++ @typeName(T) ++ "'"),
};
}