mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 16:45:27 +00:00
Merge pull request #5932 from Sahnvour/hash
new trait `hasUniqueRepresentation` and hashmap speedup
This commit is contained in:
commit
5139aa7ba4
@ -56,9 +56,6 @@ pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy)
|
||||
pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
|
||||
switch (strat) {
|
||||
.Shallow => {
|
||||
// TODO detect via a trait when Key has no padding bits to
|
||||
// hash it as an array of bytes.
|
||||
// Otherwise, hash every element.
|
||||
for (key) |element| {
|
||||
hash(hasher, element, .Shallow);
|
||||
}
|
||||
@ -75,30 +72,34 @@ pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) vo
|
||||
/// Strategy is provided to determine if pointers should be followed or not.
|
||||
pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
|
||||
const Key = @TypeOf(key);
|
||||
|
||||
if (strat == .Shallow and comptime meta.trait.hasUniqueRepresentation(Key)) {
|
||||
@call(.{ .modifier = .always_inline }, hasher.update, .{mem.asBytes(&key)});
|
||||
return;
|
||||
}
|
||||
|
||||
switch (@typeInfo(Key)) {
|
||||
.NoReturn,
|
||||
.Opaque,
|
||||
.Undefined,
|
||||
.Void,
|
||||
.Null,
|
||||
.BoundFn,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
.Type,
|
||||
.EnumLiteral,
|
||||
.Frame,
|
||||
.Float,
|
||||
=> @compileError("cannot hash this type"),
|
||||
|
||||
// Help the optimizer see that hashing an int is easy by inlining!
|
||||
// TODO Check if the situation is better after #561 is resolved.
|
||||
.Int => @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)}),
|
||||
|
||||
.Float => |info| hash(hasher, @bitCast(std.meta.Int(false, info.bits), key), strat),
|
||||
|
||||
.Bool => hash(hasher, @boolToInt(key), strat),
|
||||
.Enum => hash(hasher, @enumToInt(key), strat),
|
||||
.ErrorSet => hash(hasher, @errorToInt(key), strat),
|
||||
.AnyFrame, .Fn => hash(hasher, @ptrToInt(key), strat),
|
||||
.AnyFrame, .BoundFn, .Fn => hash(hasher, @ptrToInt(key), strat),
|
||||
|
||||
.Pointer => @call(.{ .modifier = .always_inline }, hashPointer, .{ hasher, key, strat }),
|
||||
|
||||
@ -121,9 +122,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
|
||||
},
|
||||
|
||||
.Struct => |info| {
|
||||
// TODO detect via a trait when Key has no padding bits to
|
||||
// hash it as an array of bytes.
|
||||
// Otherwise, hash every field.
|
||||
inline for (info.fields) |field| {
|
||||
// We reuse the hash of the previous field as the seed for the
|
||||
// next one so that they're dependant.
|
||||
@ -266,12 +264,12 @@ test "hash slice deep" {
|
||||
test "hash struct deep" {
|
||||
const Foo = struct {
|
||||
a: u32,
|
||||
b: f64,
|
||||
b: u16,
|
||||
c: *bool,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: *mem.Allocator, a_: u32, b_: f64, c_: bool) !Self {
|
||||
pub fn init(allocator: *mem.Allocator, a_: u32, b_: u16, c_: bool) !Self {
|
||||
const ptr = try allocator.create(bool);
|
||||
ptr.* = c_;
|
||||
return Self{ .a = a_, .b = b_, .c = ptr };
|
||||
@ -279,9 +277,9 @@ test "hash struct deep" {
|
||||
};
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
const foo = try Foo.init(allocator, 123, 1.0, true);
|
||||
const bar = try Foo.init(allocator, 123, 1.0, true);
|
||||
const baz = try Foo.init(allocator, 123, 1.0, false);
|
||||
const foo = try Foo.init(allocator, 123, 10, true);
|
||||
const bar = try Foo.init(allocator, 123, 10, true);
|
||||
const baz = try Foo.init(allocator, 123, 10, false);
|
||||
defer allocator.destroy(foo.c);
|
||||
defer allocator.destroy(bar.c);
|
||||
defer allocator.destroy(baz.c);
|
||||
@ -338,12 +336,12 @@ test "testHash struct" {
|
||||
test "testHash union" {
|
||||
const Foo = union(enum) {
|
||||
A: u32,
|
||||
B: f32,
|
||||
B: bool,
|
||||
C: u32,
|
||||
};
|
||||
|
||||
const a = Foo{ .A = 18 };
|
||||
var b = Foo{ .B = 12.34 };
|
||||
var b = Foo{ .B = true };
|
||||
const c = Foo{ .C = 18 };
|
||||
testing.expect(testHash(a) == testHash(a));
|
||||
testing.expect(testHash(a) != testHash(b));
|
||||
|
@ -5,6 +5,7 @@ const testing = std.testing;
|
||||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
const trait = meta.trait;
|
||||
const autoHash = std.hash.autoHash;
|
||||
const Wyhash = std.hash.Wyhash;
|
||||
const Allocator = mem.Allocator;
|
||||
@ -1023,9 +1024,13 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
|
||||
pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
|
||||
return struct {
|
||||
fn hash(key: K) u32 {
|
||||
var hasher = Wyhash.init(0);
|
||||
autoHash(&hasher, key);
|
||||
return @truncate(u32, hasher.final());
|
||||
if (comptime trait.hasUniqueRepresentation(K)) {
|
||||
return @truncate(u32, Wyhash.hash(0, std.mem.asBytes(&key)));
|
||||
} else {
|
||||
var hasher = Wyhash.init(0);
|
||||
autoHash(&hasher, key);
|
||||
return @truncate(u32, hasher.final());
|
||||
}
|
||||
}
|
||||
}.hash;
|
||||
}
|
||||
|
@ -429,3 +429,71 @@ test "std.meta.trait.hasFunctions" {
|
||||
testing.expect(!hasFunctions(TestStruct2, .{ "a", "b", "c" }));
|
||||
testing.expect(!hasFunctions(TestStruct2, tuple));
|
||||
}
|
||||
|
||||
/// True if every value of the type `T` has a unique bit pattern representing it.
|
||||
/// In other words, `T` has no unused bits and no padding.
|
||||
pub fn hasUniqueRepresentation(comptime T: type) bool {
|
||||
switch (@typeInfo(T)) {
|
||||
else => return false, // TODO can we know if it's true for some of these types ?
|
||||
|
||||
.AnyFrame,
|
||||
.Bool,
|
||||
.BoundFn,
|
||||
.Enum,
|
||||
.ErrorSet,
|
||||
.Fn,
|
||||
.Int, // TODO check that it is still true
|
||||
.Pointer,
|
||||
=> return true,
|
||||
|
||||
.Array => |info| return comptime hasUniqueRepresentation(info.child),
|
||||
|
||||
.Struct => |info| {
|
||||
var sum_size = @as(usize, 0);
|
||||
|
||||
inline for (info.fields) |field| {
|
||||
const FieldType = field.field_type;
|
||||
if (comptime !hasUniqueRepresentation(FieldType)) return false;
|
||||
sum_size += @sizeOf(FieldType);
|
||||
}
|
||||
|
||||
return @sizeOf(T) == sum_size;
|
||||
},
|
||||
|
||||
.Vector => |info| return comptime hasUniqueRepresentation(info.child),
|
||||
}
|
||||
}
|
||||
|
||||
test "std.meta.trait.hasUniqueRepresentation" {
|
||||
const TestStruct1 = struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
};
|
||||
|
||||
testing.expect(hasUniqueRepresentation(TestStruct1));
|
||||
|
||||
const TestStruct2 = struct {
|
||||
a: u32,
|
||||
b: u16,
|
||||
};
|
||||
|
||||
testing.expect(!hasUniqueRepresentation(TestStruct2));
|
||||
|
||||
const TestStruct3 = struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
};
|
||||
|
||||
testing.expect(hasUniqueRepresentation(TestStruct3));
|
||||
|
||||
testing.expect(hasUniqueRepresentation(i1));
|
||||
testing.expect(hasUniqueRepresentation(u2));
|
||||
testing.expect(hasUniqueRepresentation(i3));
|
||||
testing.expect(hasUniqueRepresentation(u4));
|
||||
testing.expect(hasUniqueRepresentation(i5));
|
||||
testing.expect(hasUniqueRepresentation(u6));
|
||||
testing.expect(hasUniqueRepresentation(i7));
|
||||
testing.expect(hasUniqueRepresentation(u8));
|
||||
testing.expect(hasUniqueRepresentation(i9));
|
||||
testing.expect(hasUniqueRepresentation(u10));
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user