authorgravatar for sahnvour@pm.meSahnvour <sahnvour@pm.me> 2019-07-02 19:46:51+02:00
committergravatar for sahnvour@pm.meSahnvour <sahnvour@pm.me> 2019-08-04 12:34:37+02:00
log4b5172d2879742b98e3e34b90b05ac28da9f39fe
treeb054dd39dda26a33ef3ccc73ee9dcac64e1ea36d
parent5bf63bfbf113d3921101311f1e3040890b94e798

move autoHash into its own module since it can be used with any hash function implementing a streaming interface


3 files changed, 213 insertions(+), 200 deletions(-)

std/hash.zig+4
...@@ -1,6 +1,9 @@...@@ -1,6 +1,9 @@
1const adler = @import("hash/adler.zig");1const adler = @import("hash/adler.zig");
2pub const Adler32 = adler.Adler32;2pub const Adler32 = adler.Adler32;
33
4const auto_hash = @import("hash/auto_hash.zig");
5pub const autoHash = auto_hash.autoHash;
6
4// pub for polynomials + generic crc32 construction7// pub for polynomials + generic crc32 construction
5pub const crc = @import("hash/crc.zig");8pub const crc = @import("hash/crc.zig");
6pub const Crc32 = crc.Crc32;9pub const Crc32 = crc.Crc32;
...@@ -30,6 +33,7 @@ pub const Wyhash = wyhash.Wyhash;...@@ -30,6 +33,7 @@ pub const Wyhash = wyhash.Wyhash;
3033
31test "hash" {34test "hash" {
32 _ = @import("hash/adler.zig");35 _ = @import("hash/adler.zig");
36 _ = @import("hash/auto_hash.zig");
33 _ = @import("hash/crc.zig");37 _ = @import("hash/crc.zig");
34 _ = @import("hash/fnv.zig");38 _ = @import("hash/fnv.zig");
35 _ = @import("hash/siphash.zig");39 _ = @import("hash/siphash.zig");
std/hash/auto_hash.zig created+208
...@@ -0,0 +1,208 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const meta = std.meta;
5
6/// Provides generic hashing for any eligible type.
7/// Only hashes `key` itself, pointers are not followed.
8pub fn autoHash(hasher: var, key: var) void {
9 const Key = @typeOf(key);
10 switch (@typeInfo(Key)) {
11 builtin.TypeId.NoReturn,
12 builtin.TypeId.Opaque,
13 builtin.TypeId.Undefined,
14 builtin.TypeId.ArgTuple,
15 builtin.TypeId.Void,
16 builtin.TypeId.Null,
17 builtin.TypeId.BoundFn,
18 builtin.TypeId.ComptimeFloat,
19 builtin.TypeId.ComptimeInt,
20 builtin.TypeId.Type,
21 builtin.TypeId.EnumLiteral,
22 => @compileError("cannot hash this type"),
23
24 builtin.TypeId.Int => hasher.update(std.mem.asBytes(&key)),
25
26 builtin.TypeId.Float => |info| autoHash(hasher, @bitCast(@IntType(false, info.bits), key)),
27
28 builtin.TypeId.Bool => autoHash(hasher, @boolToInt(key)),
29 builtin.TypeId.Enum => autoHash(hasher, @enumToInt(key)),
30 builtin.TypeId.ErrorSet => autoHash(hasher, @errorToInt(key)),
31 builtin.TypeId.Promise, builtin.TypeId.Fn => autoHash(hasher, @ptrToInt(key)),
32
33 builtin.TypeId.Pointer => |info| switch (info.size) {
34 builtin.TypeInfo.Pointer.Size.One,
35 builtin.TypeInfo.Pointer.Size.Many,
36 builtin.TypeInfo.Pointer.Size.C,
37 => autoHash(hasher, @ptrToInt(key)),
38
39 builtin.TypeInfo.Pointer.Size.Slice => {
40 autoHash(hasher, key.ptr);
41 autoHash(hasher, key.len);
42 },
43 },
44
45 builtin.TypeId.Optional => if (key) |k| autoHash(hasher, k),
46
47 builtin.TypeId.Array => {
48 // TODO detect via a trait when Key has no padding bits to
49 // hash it as an array of bytes.
50 // Otherwise, hash every element.
51 for (key) |element| {
52 autoHash(hasher, element);
53 }
54 },
55
56 builtin.TypeId.Vector => |info| {
57 if (info.child.bit_count % 8 == 0) {
58 // If there's no unused bits in the child type, we can just hash
59 // this as an array of bytes.
60 hasher.update(mem.asBytes(&key));
61 } else {
62 // Otherwise, hash every element.
63 // TODO remove the copy to an array once field access is done.
64 const array: [info.len]info.child = key;
65 comptime var i: u32 = 0;
66 inline while (i < info.len) : (i += 1) {
67 autoHash(hasher, array[i]);
68 }
69 }
70 },
71
72 builtin.TypeId.Struct => |info| {
73 // TODO detect via a trait when Key has no padding bits to
74 // hash it as an array of bytes.
75 // Otherwise, hash every field.
76 inline for (info.fields) |field| {
77 // We reuse the hash of the previous field as the seed for the
78 // next one so that they're dependant.
79 autoHash(hasher, @field(key, field.name));
80 }
81 },
82
83 builtin.TypeId.Union => |info| blk: {
84 if (info.tag_type) |tag_type| {
85 const tag = meta.activeTag(key);
86 const s = autoHash(hasher, tag);
87 inline for (info.fields) |field| {
88 const enum_field = field.enum_field.?;
89 if (enum_field.value == @enumToInt(tag)) {
90 autoHash(hasher, @field(key, enum_field.name));
91 // TODO use a labelled break when it does not crash the compiler.
92 // break :blk;
93 return;
94 }
95 }
96 unreachable;
97 } else @compileError("cannot hash untagged union type: " ++ @typeName(Key) ++ ", provide your own hash function");
98 },
99
100 builtin.TypeId.ErrorUnion => blk: {
101 const payload = key catch |err| {
102 autoHash(hasher, err);
103 break :blk;
104 };
105 autoHash(hasher, payload);
106 },
107 }
108}
109
110const testing = std.testing;
111const Wyhash = std.hash.Wyhash;
112
113fn testAutoHash(key: var) u64 {
114 // Any hash could be used here, for testing autoHash.
115 var hasher = Wyhash.init(0);
116 autoHash(&hasher, key);
117 return hasher.final();
118}
119
120test "autoHash slice" {
121 // Allocate one array dynamically so that we're assured it is not merged
122 // with the other by the optimization passes.
123 const array1 = try std.heap.direct_allocator.create([6]u32);
124 defer std.heap.direct_allocator.destroy(array1);
125 array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 };
126 const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 };
127 const a = array1[0..];
128 const b = array2[0..];
129 const c = array1[0..3];
130 testing.expect(testAutoHash(a) == testAutoHash(a));
131 testing.expect(testAutoHash(a) != testAutoHash(array1));
132 testing.expect(testAutoHash(a) != testAutoHash(b));
133 testing.expect(testAutoHash(a) != testAutoHash(c));
134}
135
136test "testAutoHash optional" {
137 const a: ?u32 = 123;
138 const b: ?u32 = null;
139 testing.expectEqual(testAutoHash(a), testAutoHash(u32(123)));
140 testing.expect(testAutoHash(a) != testAutoHash(b));
141 testing.expectEqual(testAutoHash(b), 0);
142}
143
144test "testAutoHash array" {
145 const a = [_]u32{ 1, 2, 3 };
146 const h = testAutoHash(a);
147 var hasher = Wyhash.init(0);
148 autoHash(&hasher, u32(1));
149 autoHash(&hasher, u32(2));
150 autoHash(&hasher, u32(3));
151 testing.expectEqual(h, hasher.final());
152}
153
154test "testAutoHash struct" {
155 const Foo = struct {
156 a: u32 = 1,
157 b: u32 = 2,
158 c: u32 = 3,
159 };
160 const f = Foo{};
161 const h = testAutoHash(f);
162 var hasher = Wyhash.init(0);
163 autoHash(&hasher, u32(1));
164 autoHash(&hasher, u32(2));
165 autoHash(&hasher, u32(3));
166 testing.expectEqual(h, hasher.final());
167}
168
169test "testAutoHash union" {
170 const Foo = union(enum) {
171 A: u32,
172 B: f32,
173 C: u32,
174 };
175
176 const a = Foo{ .A = 18 };
177 var b = Foo{ .B = 12.34 };
178 const c = Foo{ .C = 18 };
179 testing.expect(testAutoHash(a) == testAutoHash(a));
180 testing.expect(testAutoHash(a) != testAutoHash(b));
181 testing.expect(testAutoHash(a) != testAutoHash(c));
182
183 b = Foo{ .A = 18 };
184 testing.expect(testAutoHash(a) == testAutoHash(b));
185}
186
187test "testAutoHash vector" {
188 const a: @Vector(4, u32) = [_]u32{ 1, 2, 3, 4 };
189 const b: @Vector(4, u32) = [_]u32{ 1, 2, 3, 5 };
190 const c: @Vector(4, u31) = [_]u31{ 1, 2, 3, 4 };
191 testing.expect(testAutoHash(a) == testAutoHash(a));
192 testing.expect(testAutoHash(a) != testAutoHash(b));
193 testing.expect(testAutoHash(a) != testAutoHash(c));
194}
195
196test "testAutoHash error union" {
197 const Errors = error{Test};
198 const Foo = struct {
199 a: u32 = 1,
200 b: u32 = 2,
201 c: u32 = 3,
202 };
203 const f = Foo{};
204 const g: Errors!Foo = Errors.Test;
205 testing.expect(testAutoHash(f) != testAutoHash(g));
206 testing.expect(testAutoHash(f) == testAutoHash(Foo{}));
207 testing.expect(testAutoHash(g) == testAutoHash(Errors.Test));
208}
std/hash_map.zig+1-200
...@@ -5,6 +5,7 @@ const testing = std.testing;...@@ -5,6 +5,7 @@ const testing = std.testing;
5const math = std.math;5const math = std.math;
6const mem = std.mem;6const mem = std.mem;
7const meta = std.meta;7const meta = std.meta;
8const autoHash = std.hash.autoHash;
8const Wyhash = std.hash.Wyhash;9const Wyhash = std.hash.Wyhash;
9const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
10const builtin = @import("builtin");11const builtin = @import("builtin");
...@@ -536,203 +537,3 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {...@@ -536,203 +537,3 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
536 }537 }
537 }.eql;538 }.eql;
538}539}
539
540/// Provides generic hashing for any eligible type.
541/// Only hashes `key` itself, pointers are not followed.
542pub fn autoHash(hasher: var, key: var) void {
543 const Key = @typeOf(key);
544 switch (@typeInfo(Key)) {
545 builtin.TypeId.NoReturn,
546 builtin.TypeId.Opaque,
547 builtin.TypeId.Undefined,
548 builtin.TypeId.ArgTuple,
549 builtin.TypeId.Void,
550 builtin.TypeId.Null,
551 builtin.TypeId.BoundFn,
552 builtin.TypeId.ComptimeFloat,
553 builtin.TypeId.ComptimeInt,
554 builtin.TypeId.Type,
555 builtin.TypeId.EnumLiteral,
556 => @compileError("cannot hash this type"),
557
558 builtin.TypeId.Int => hasher.update(std.mem.asBytes(&key)),
559
560 builtin.TypeId.Float => |info| autoHash(hasher, @bitCast(@IntType(false, info.bits), key)),
561
562 builtin.TypeId.Bool => autoHash(hasher, @boolToInt(key)),
563 builtin.TypeId.Enum => autoHash(hasher, @enumToInt(key)),
564 builtin.TypeId.ErrorSet => autoHash(hasher, @errorToInt(key)),
565 builtin.TypeId.Promise, builtin.TypeId.Fn => autoHash(hasher, @ptrToInt(key)),
566
567 builtin.TypeId.Pointer => |info| switch (info.size) {
568 builtin.TypeInfo.Pointer.Size.One,
569 builtin.TypeInfo.Pointer.Size.Many,
570 builtin.TypeInfo.Pointer.Size.C,
571 => autoHash(hasher, @ptrToInt(key)),
572
573 builtin.TypeInfo.Pointer.Size.Slice => {
574 autoHash(hasher, key.ptr);
575 autoHash(hasher, key.len);
576 },
577 },
578
579 builtin.TypeId.Optional => if (key) |k| autoHash(hasher, k),
580
581 builtin.TypeId.Array => {
582 // TODO detect via a trait when Key has no padding bits to
583 // hash it as an array of bytes.
584 // Otherwise, hash every element.
585 for (key) |element| {
586 autoHash(hasher, element);
587 }
588 },
589
590 builtin.TypeId.Vector => |info| {
591 if (info.child.bit_count % 8 == 0) {
592 // If there's no unused bits in the child type, we can just hash
593 // this as an array of bytes.
594 hasher.update(mem.asBytes(&key));
595 } else {
596 // Otherwise, hash every element.
597 // TODO remove the copy to an array once field access is done.
598 const array: [info.len]info.child = key;
599 comptime var i: u32 = 0;
600 inline while (i < info.len) : (i += 1) {
601 autoHash(hasher, array[i]);
602 }
603 }
604 },
605
606 builtin.TypeId.Struct => |info| {
607 // TODO detect via a trait when Key has no padding bits to
608 // hash it as an array of bytes.
609 // Otherwise, hash every field.
610 inline for (info.fields) |field| {
611 // We reuse the hash of the previous field as the seed for the
612 // next one so that they're dependant.
613 autoHash(hasher, @field(key, field.name));
614 }
615 },
616
617 builtin.TypeId.Union => |info| blk: {
618 if (info.tag_type) |tag_type| {
619 const tag = meta.activeTag(key);
620 const s = autoHash(hasher, tag);
621 inline for (info.fields) |field| {
622 const enum_field = field.enum_field.?;
623 if (enum_field.value == @enumToInt(tag)) {
624 autoHash(hasher, @field(key, enum_field.name));
625 // TODO use a labelled break when it does not crash the compiler.
626 // break :blk;
627 return;
628 }
629 }
630 unreachable;
631 } else @compileError("cannot hash untagged union type: " ++ @typeName(Key) ++ ", provide your own hash function");
632 },
633
634 builtin.TypeId.ErrorUnion => blk: {
635 const payload = key catch |err| {
636 autoHash(hasher, err);
637 break :blk;
638 };
639 autoHash(hasher, payload);
640 },
641 }
642}
643
644fn testAutoHash(key: var) u64 {
645 var hasher = Wyhash.init(0);
646 autoHash(&hasher, key);
647 return hasher.final();
648}
649
650test "autoHash slice" {
651 // Allocate one array dynamically so that we're assured it is not merged
652 // with the other by the optimization passes.
653 const array1 = try std.heap.direct_allocator.create([6]u32);
654 defer std.heap.direct_allocator.destroy(array1);
655 array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 };
656 const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 };
657 const a = array1[0..];
658 const b = array2[0..];
659 const c = array1[0..3];
660 testing.expect(testAutoHash(a) == testAutoHash(a));
661 testing.expect(testAutoHash(a) != testAutoHash(array1));
662 testing.expect(testAutoHash(a) != testAutoHash(b));
663 testing.expect(testAutoHash(a) != testAutoHash(c));
664}
665
666test "testAutoHash optional" {
667 const a: ?u32 = 123;
668 const b: ?u32 = null;
669 testing.expectEqual(testAutoHash(a), testAutoHash(u32(123)));
670 testing.expect(testAutoHash(a) != testAutoHash(b));
671 testing.expectEqual(testAutoHash(b), 0);
672}
673
674test "testAutoHash array" {
675 const a = [_]u32{ 1, 2, 3 };
676 const h = testAutoHash(a);
677 var hasher = Wyhash.init(0);
678 autoHash(&hasher, u32(1));
679 autoHash(&hasher, u32(2));
680 autoHash(&hasher, u32(3));
681 testing.expectEqual(h, hasher.final());
682}
683
684test "testAutoHash struct" {
685 const Foo = struct {
686 a: u32 = 1,
687 b: u32 = 2,
688 c: u32 = 3,
689 };
690 const f = Foo{};
691 const h = testAutoHash(f);
692 var hasher = Wyhash.init(0);
693 autoHash(&hasher, u32(1));
694 autoHash(&hasher, u32(2));
695 autoHash(&hasher, u32(3));
696 testing.expectEqual(h, hasher.final());
697}
698
699test "testAutoHash union" {
700 const Foo = union(enum) {
701 A: u32,
702 B: f32,
703 C: u32,
704 };
705
706 const a = Foo{ .A = 18 };
707 var b = Foo{ .B = 12.34 };
708 const c = Foo{ .C = 18 };
709 testing.expect(testAutoHash(a) == testAutoHash(a));
710 testing.expect(testAutoHash(a) != testAutoHash(b));
711 testing.expect(testAutoHash(a) != testAutoHash(c));
712
713 b = Foo{ .A = 18 };
714 testing.expect(testAutoHash(a) == testAutoHash(b));
715}
716
717test "testAutoHash vector" {
718 const a: @Vector(4, u32) = [_]u32{ 1, 2, 3, 4 };
719 const b: @Vector(4, u32) = [_]u32{ 1, 2, 3, 5 };
720 const c: @Vector(4, u31) = [_]u31{ 1, 2, 3, 4 };
721 testing.expect(testAutoHash(a) == testAutoHash(a));
722 testing.expect(testAutoHash(a) != testAutoHash(b));
723 testing.expect(testAutoHash(a) != testAutoHash(c));
724}
725
726test "testAutoHash error union" {
727 const Errors = error{Test};
728 const Foo = struct {
729 a: u32 = 1,
730 b: u32 = 2,
731 c: u32 = 3,
732 };
733 const f = Foo{};
734 const g: Errors!Foo = Errors.Test;
735 testing.expect(testAutoHash(f) != testAutoHash(g));
736 testing.expect(testAutoHash(f) == testAutoHash(Foo{}));
737 testing.expect(testAutoHash(g) == testAutoHash(Errors.Test));
738}