authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-04 19:08:44-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-04 19:10:36-04:00
logb7c94be6881e68fe40b432778163f1e9bca43e3d
tree9c30ae943bfca71087e9f4587e41887f250b3866
parentfbf21efd24bf812e0fd52a5917708a4c45f05b5e
parent30466bccefedd5e795b72b422f98b8a58e786289
signaturelock-open Commit is signed but in an unrecognized format.

Merge remote-tracking branch 'origin/master' into rewrite-coroutines


17 files changed, 745 insertions(+), 150 deletions(-)

CONTRIBUTING.md+6-6
......@@ -25,6 +25,7 @@ Here are some examples:
2525
2626 * [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html)
2727 * [The Right Tool for the Right Job: Redis Modules & Zig](https://www.youtube.com/watch?v=eCHM8-_poZY)
28 * [Writing a small ray tracer in Rust and Zig](https://nelari.us/post/raytracer_with_rust_and_zig/)
2829
2930Zig is a brand new language, with no advertising budget. Word of mouth is the
3031only way people find out about the project, and the more people hear about it,
......@@ -45,8 +46,8 @@ The most highly regarded argument in such a discussion is a real world use case.
4546
4647The issue label
4748[Contributor Friendly](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributor+friendly%22)
48exists to help contributors find issues that are "limited in scope and/or
49knowledge of Zig internals."
49exists to help you find issues that are **limited in scope and/or
50knowledge of Zig internals.**
5051
5152### Editing Source Code
5253
......@@ -61,8 +62,7 @@ To test changes, do the following from the build directory:
6162
62631. Run `make install` (on POSIX) or
6364 `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows).
642. `bin/zig build --build-file ../build.zig test` (on POSIX) or
65 `bin\zig.exe build --build-file ..\build.zig test` (on Windows).
652. `bin/zig build test` (on POSIX) or `bin\zig.exe build test` (on Windows).
6666
6767That runs the whole test suite, which does a lot of extra testing that you
6868likely won't always need, and can take upwards of 2 hours. This is what the
......@@ -79,8 +79,8 @@ Another example is choosing a different set of things to test. For example,
7979not the other ones. Combining this suggestion with the previous one, you could
8080do this:
8181
82`bin/zig build --build-file ../build.zig test-std -Dskip-release` (on POSIX) or
83`bin\zig.exe build --build-file ..\build.zig test-std -Dskip-release` (on Windows).
82`bin/zig build test-std -Dskip-release` (on POSIX) or
83`bin\zig.exe build test-std -Dskip-release` (on Windows).
8484
8585This will run only the standard library tests, in debug mode only, for all
8686targets (it will cross-compile the tests for non-native targets but not run
doc/langref.html.in+16
......@@ -6330,6 +6330,22 @@ comptime {
63306330 TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
63316331 we can remove this restriction
63326332 </p>
6333 <p>
6334 Supported operations:
6335 </p>
6336 <ul>
6337 <li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.</li>
6338 <li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition.
6339 Also supports {#link|Floats#}.</li>
6340 <li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction.
6341 Also supports {#link|Floats#}.</li>
6342 <li>{#syntax#}.And{#endsyntax#} - bitwise and</li>
6343 <li>{#syntax#}.Nand{#endsyntax#} - bitwise nand</li>
6344 <li>{#syntax#}.Or{#endsyntax#} - bitwise or</li>
6345 <li>{#syntax#}.Xor{#endsyntax#} - bitwise xor</li>
6346 <li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li>
6347 <li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li>
6348 </ul>
63336349 {#header_close#}
63346350 {#header_open|@bitCast#}
63356351 <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
std/build.zig+16-16
......@@ -805,11 +805,7 @@ pub const Builder = struct {
805805 return name;
806806 }
807807 const full_path = try fs.path.join(self.allocator, [_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });
808 if (fs.path.real(self.allocator, full_path)) |real_path| {
809 return real_path;
810 } else |_| {
811 continue;
812 }
808 return fs.realpathAlloc(self.allocator, full_path) catch continue;
813809 }
814810 }
815811 if (self.env_map.get("PATH")) |PATH| {
......@@ -817,14 +813,10 @@ pub const Builder = struct {
817813 if (fs.path.isAbsolute(name)) {
818814 return name;
819815 }
820 var it = mem.tokenize(PATH, []u8{fs.path.delimiter});
816 var it = mem.tokenize(PATH, [_]u8{fs.path.delimiter});
821817 while (it.next()) |path| {
822818 const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
823 if (fs.path.real(self.allocator, full_path)) |real_path| {
824 return real_path;
825 } else |_| {
826 continue;
827 }
819 return fs.realpathAlloc(self.allocator, full_path) catch continue;
828820 }
829821 }
830822 }
......@@ -834,11 +826,7 @@ pub const Builder = struct {
834826 }
835827 for (paths) |path| {
836828 const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
837 if (fs.path.real(self.allocator, full_path)) |real_path| {
838 return real_path;
839 } else |_| {
840 continue;
841 }
829 return fs.realpathAlloc(self.allocator, full_path) catch continue;
842830 }
843831 }
844832 return error.FileNotFound;
......@@ -904,6 +892,15 @@ pub const Builder = struct {
904892 }
905893};
906894
895test "builder.findProgram compiles" {
896 //allocator: *Allocator,
897 //zig_exe: []const u8,
898 //build_root: []const u8,
899 //cache_root: []const u8,
900 const builder = try Builder.create(std.heap.direct_allocator, "zig", "zig-cache", "zig-cache");
901 _ = builder.findProgram([_][]const u8{}, [_][]const u8{}) catch null;
902}
903
907904pub const Version = struct {
908905 major: u32,
909906 minor: u32,
......@@ -1122,6 +1119,9 @@ pub const Target = union(enum) {
11221119 }
11231120
11241121 pub fn libPrefix(self: Target) []const u8 {
1122 if (self.isWasm()) {
1123 return "";
1124 }
11251125 switch (self.getAbi()) {
11261126 .msvc => return "",
11271127 else => return "lib",
std/debug.zig+1-2
......@@ -1024,8 +1024,7 @@ pub fn openElfDebugInfo(
10241024 elf_seekable_stream: *DwarfSeekableStream,
10251025 elf_in_stream: *DwarfInStream,
10261026) !DwarfInfo {
1027 var efile: elf.Elf = undefined;
1028 try efile.openStream(allocator, elf_seekable_stream, elf_in_stream);
1027 var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream);
10291028 errdefer efile.close();
10301029
10311030 var di = DwarfInfo{
std/elf.zig+6-9
......@@ -356,7 +356,6 @@ pub const SectionHeader = struct {
356356pub const Elf = struct {
357357 seekable_stream: *io.SeekableStream(anyerror, anyerror),
358358 in_stream: *io.InStream(anyerror),
359 auto_close_stream: bool,
360359 is_64: bool,
361360 endian: builtin.Endian,
362361 file_type: FileType,
......@@ -368,25 +367,23 @@ pub const Elf = struct {
368367 string_section: *SectionHeader,
369368 section_headers: []SectionHeader,
370369 allocator: *mem.Allocator,
371 prealloc_file: File,
372370
373371 /// Call close when done.
374 pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void {
372 pub fn openPath(allocator: *mem.Allocator, path: []const u8) !Elf {
375373 @compileError("TODO implement");
376374 }
377375
378376 /// Call close when done.
379 pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: File) !void {
377 pub fn openFile(allocator: *mem.Allocator, file: File) !Elf {
380378 @compileError("TODO implement");
381379 }
382380
383381 pub fn openStream(
384 elf: *Elf,
385382 allocator: *mem.Allocator,
386383 seekable_stream: *io.SeekableStream(anyerror, anyerror),
387384 in: *io.InStream(anyerror),
388 ) !void {
389 elf.auto_close_stream = false;
385 ) !Elf {
386 var elf: Elf = undefined;
390387 elf.allocator = allocator;
391388 elf.seekable_stream = seekable_stream;
392389 elf.in_stream = in;
......@@ -523,12 +520,12 @@ pub const Elf = struct {
523520 // not a string table
524521 return error.InvalidFormat;
525522 }
523
524 return elf;
526525 }
527526
528527 pub fn close(elf: *Elf) void {
529528 elf.allocator.free(elf.section_headers);
530
531 if (elf.auto_close_stream) elf.prealloc_file.close();
532529 }
533530
534531 pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
std/hash.zig+10
......@@ -1,6 +1,9 @@
11const adler = @import("hash/adler.zig");
22pub const Adler32 = adler.Adler32;
33
4const auto_hash = @import("hash/auto_hash.zig");
5pub const autoHash = auto_hash.autoHash;
6
47// pub for polynomials + generic crc32 construction
58pub const crc = @import("hash/crc.zig");
69pub const Crc32 = crc.Crc32;
......@@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128;
1619
1720pub const murmur = @import("hash/murmur.zig");
1821pub const Murmur2_32 = murmur.Murmur2_32;
22
23
1924pub const Murmur2_64 = murmur.Murmur2_64;
2025pub const Murmur3_32 = murmur.Murmur3_32;
2126
......@@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig");
2328pub const CityHash32 = cityhash.CityHash32;
2429pub const CityHash64 = cityhash.CityHash64;
2530
31const wyhash = @import("hash/wyhash.zig");
32pub const Wyhash = wyhash.Wyhash;
33
2634test "hash" {
2735 _ = @import("hash/adler.zig");
36 _ = @import("hash/auto_hash.zig");
2837 _ = @import("hash/crc.zig");
2938 _ = @import("hash/fnv.zig");
3039 _ = @import("hash/siphash.zig");
3140 _ = @import("hash/murmur.zig");
3241 _ = @import("hash/cityhash.zig");
42 _ = @import("hash/wyhash.zig");
3343}
std/hash/auto_hash.zig created+211
......@@ -0,0 +1,211 @@
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 .NoReturn,
12 .Opaque,
13 .Undefined,
14 .ArgTuple,
15 .Void,
16 .Null,
17 .BoundFn,
18 .ComptimeFloat,
19 .ComptimeInt,
20 .Type,
21 .EnumLiteral,
22 .Frame,
23 => @compileError("cannot hash this type"),
24
25 // Help the optimizer see that hashing an int is easy by inlining!
26 // TODO Check if the situation is better after #561 is resolved.
27 .Int => @inlineCall(hasher.update, std.mem.asBytes(&key)),
28
29 .Float => |info| autoHash(hasher, @bitCast(@IntType(false, info.bits), key)),
30
31 .Bool => autoHash(hasher, @boolToInt(key)),
32 .Enum => autoHash(hasher, @enumToInt(key)),
33 .ErrorSet => autoHash(hasher, @errorToInt(key)),
34 .AnyFrame, .Fn => autoHash(hasher, @ptrToInt(key)),
35
36 .Pointer => |info| switch (info.size) {
37 builtin.TypeInfo.Pointer.Size.One,
38 builtin.TypeInfo.Pointer.Size.Many,
39 builtin.TypeInfo.Pointer.Size.C,
40 => autoHash(hasher, @ptrToInt(key)),
41
42 builtin.TypeInfo.Pointer.Size.Slice => {
43 autoHash(hasher, key.ptr);
44 autoHash(hasher, key.len);
45 },
46 },
47
48 .Optional => if (key) |k| autoHash(hasher, k),
49
50 .Array => {
51 // TODO detect via a trait when Key has no padding bits to
52 // hash it as an array of bytes.
53 // Otherwise, hash every element.
54 for (key) |element| {
55 autoHash(hasher, element);
56 }
57 },
58
59 .Vector => |info| {
60 if (info.child.bit_count % 8 == 0) {
61 // If there's no unused bits in the child type, we can just hash
62 // this as an array of bytes.
63 hasher.update(mem.asBytes(&key));
64 } else {
65 // Otherwise, hash every element.
66 // TODO remove the copy to an array once field access is done.
67 const array: [info.len]info.child = key;
68 comptime var i: u32 = 0;
69 inline while (i < info.len) : (i += 1) {
70 autoHash(hasher, array[i]);
71 }
72 }
73 },
74
75 .Struct => |info| {
76 // TODO detect via a trait when Key has no padding bits to
77 // hash it as an array of bytes.
78 // Otherwise, hash every field.
79 inline for (info.fields) |field| {
80 // We reuse the hash of the previous field as the seed for the
81 // next one so that they're dependant.
82 autoHash(hasher, @field(key, field.name));
83 }
84 },
85
86 .Union => |info| blk: {
87 if (info.tag_type) |tag_type| {
88 const tag = meta.activeTag(key);
89 const s = autoHash(hasher, tag);
90 inline for (info.fields) |field| {
91 const enum_field = field.enum_field.?;
92 if (enum_field.value == @enumToInt(tag)) {
93 autoHash(hasher, @field(key, enum_field.name));
94 // TODO use a labelled break when it does not crash the compiler.
95 // break :blk;
96 return;
97 }
98 }
99 unreachable;
100 } else @compileError("cannot hash untagged union type: " ++ @typeName(Key) ++ ", provide your own hash function");
101 },
102
103 .ErrorUnion => blk: {
104 const payload = key catch |err| {
105 autoHash(hasher, err);
106 break :blk;
107 };
108 autoHash(hasher, payload);
109 },
110 }
111}
112
113const testing = std.testing;
114const Wyhash = std.hash.Wyhash;
115
116fn testAutoHash(key: var) u64 {
117 // Any hash could be used here, for testing autoHash.
118 var hasher = Wyhash.init(0);
119 autoHash(&hasher, key);
120 return hasher.final();
121}
122
123test "autoHash slice" {
124 // Allocate one array dynamically so that we're assured it is not merged
125 // with the other by the optimization passes.
126 const array1 = try std.heap.direct_allocator.create([6]u32);
127 defer std.heap.direct_allocator.destroy(array1);
128 array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 };
129 const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 };
130 const a = array1[0..];
131 const b = array2[0..];
132 const c = array1[0..3];
133 testing.expect(testAutoHash(a) == testAutoHash(a));
134 testing.expect(testAutoHash(a) != testAutoHash(array1));
135 testing.expect(testAutoHash(a) != testAutoHash(b));
136 testing.expect(testAutoHash(a) != testAutoHash(c));
137}
138
139test "testAutoHash optional" {
140 const a: ?u32 = 123;
141 const b: ?u32 = null;
142 testing.expectEqual(testAutoHash(a), testAutoHash(u32(123)));
143 testing.expect(testAutoHash(a) != testAutoHash(b));
144 testing.expectEqual(testAutoHash(b), 0);
145}
146
147test "testAutoHash array" {
148 const a = [_]u32{ 1, 2, 3 };
149 const h = testAutoHash(a);
150 var hasher = Wyhash.init(0);
151 autoHash(&hasher, u32(1));
152 autoHash(&hasher, u32(2));
153 autoHash(&hasher, u32(3));
154 testing.expectEqual(h, hasher.final());
155}
156
157test "testAutoHash struct" {
158 const Foo = struct {
159 a: u32 = 1,
160 b: u32 = 2,
161 c: u32 = 3,
162 };
163 const f = Foo{};
164 const h = testAutoHash(f);
165 var hasher = Wyhash.init(0);
166 autoHash(&hasher, u32(1));
167 autoHash(&hasher, u32(2));
168 autoHash(&hasher, u32(3));
169 testing.expectEqual(h, hasher.final());
170}
171
172test "testAutoHash union" {
173 const Foo = union(enum) {
174 A: u32,
175 B: f32,
176 C: u32,
177 };
178
179 const a = Foo{ .A = 18 };
180 var b = Foo{ .B = 12.34 };
181 const c = Foo{ .C = 18 };
182 testing.expect(testAutoHash(a) == testAutoHash(a));
183 testing.expect(testAutoHash(a) != testAutoHash(b));
184 testing.expect(testAutoHash(a) != testAutoHash(c));
185
186 b = Foo{ .A = 18 };
187 testing.expect(testAutoHash(a) == testAutoHash(b));
188}
189
190test "testAutoHash vector" {
191 const a: @Vector(4, u32) = [_]u32{ 1, 2, 3, 4 };
192 const b: @Vector(4, u32) = [_]u32{ 1, 2, 3, 5 };
193 const c: @Vector(4, u31) = [_]u31{ 1, 2, 3, 4 };
194 testing.expect(testAutoHash(a) == testAutoHash(a));
195 testing.expect(testAutoHash(a) != testAutoHash(b));
196 testing.expect(testAutoHash(a) != testAutoHash(c));
197}
198
199test "testAutoHash error union" {
200 const Errors = error{Test};
201 const Foo = struct {
202 a: u32 = 1,
203 b: u32 = 2,
204 c: u32 = 3,
205 };
206 const f = Foo{};
207 const g: Errors!Foo = Errors.Test;
208 testing.expect(testAutoHash(f) != testAutoHash(g));
209 testing.expect(testAutoHash(f) == testAutoHash(Foo{}));
210 testing.expect(testAutoHash(g) == testAutoHash(Errors.Test));
211}
std/hash/throughput_test.zig created+148
......@@ -0,0 +1,148 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const time = std.time;
4const Timer = time.Timer;
5const hash = std.hash;
6
7const KiB = 1024;
8const MiB = 1024 * KiB;
9const GiB = 1024 * MiB;
10
11var prng = std.rand.DefaultPrng.init(0);
12
13const Hash = struct {
14 ty: type,
15 name: []const u8,
16 init_u8s: ?[]const u8 = null,
17 init_u64: ?u64 = null,
18};
19
20const siphash_key = "0123456789abcdef";
21
22const hashes = [_]Hash{
23 Hash{ .ty = hash.Wyhash, .name = "wyhash", .init_u64 = 0 },
24 Hash{ .ty = hash.SipHash64(1, 3), .name = "siphash(1,3)", .init_u8s = siphash_key },
25 Hash{ .ty = hash.SipHash64(2, 4), .name = "siphash(2,4)", .init_u8s = siphash_key },
26 Hash{ .ty = hash.Fnv1a_64, .name = "fnv1a" },
27 Hash{ .ty = hash.Crc32, .name = "crc32" },
28};
29
30const Result = struct {
31 hash: u64,
32 throughput: u64,
33};
34
35pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {
36 var h = blk: {
37 if (H.init_u8s) |init| {
38 break :blk H.ty.init(init);
39 }
40 if (H.init_u64) |init| {
41 break :blk H.ty.init(init);
42 }
43 break :blk H.ty.init();
44 };
45
46 var block: [8192]u8 = undefined;
47 prng.random.bytes(block[0..]);
48
49 var offset: usize = 0;
50 var timer = try Timer.start();
51 const start = timer.lap();
52 while (offset < bytes) : (offset += block.len) {
53 h.update(block[0..]);
54 }
55 const end = timer.read();
56
57 const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
58 const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s);
59
60 return Result{
61 .hash = h.final(),
62 .throughput = throughput,
63 };
64}
65
66fn usage() void {
67 std.debug.warn(
68 \\throughput_test [options]
69 \\
70 \\Options:
71 \\ --filter [test-name]
72 \\ --seed [int]
73 \\ --count [int]
74 \\ --help
75 \\
76 );
77}
78
79fn mode(comptime x: comptime_int) comptime_int {
80 return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
81}
82
83// TODO(#1358): Replace with builtin formatted padding when available.
84fn printPad(stdout: var, s: []const u8) !void {
85 var i: usize = 0;
86 while (i < 12 - s.len) : (i += 1) {
87 try stdout.print(" ");
88 }
89 try stdout.print("{}", s);
90}
91
92pub fn main() !void {
93 var stdout_file = try std.io.getStdOut();
94 var stdout_out_stream = stdout_file.outStream();
95 const stdout = &stdout_out_stream.stream;
96
97 var buffer: [1024]u8 = undefined;
98 var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
99 const args = try std.process.argsAlloc(&fixed.allocator);
100
101 var filter: ?[]u8 = "";
102 var count: usize = mode(128 * MiB);
103
104 var i: usize = 1;
105 while (i < args.len) : (i += 1) {
106 if (std.mem.eql(u8, args[i], "--seed")) {
107 i += 1;
108 if (i == args.len) {
109 usage();
110 std.os.exit(1);
111 }
112
113 const seed = try std.fmt.parseUnsigned(u32, args[i], 10);
114 prng.seed(seed);
115 } else if (std.mem.eql(u8, args[i], "--filter")) {
116 i += 1;
117 if (i == args.len) {
118 usage();
119 std.os.exit(1);
120 }
121
122 filter = args[i];
123 } else if (std.mem.eql(u8, args[i], "--count")) {
124 i += 1;
125 if (i == args.len) {
126 usage();
127 std.os.exit(1);
128 }
129
130 const c = try std.fmt.parseUnsigned(u32, args[i], 10);
131 count = c * MiB;
132 } else if (std.mem.eql(u8, args[i], "--help")) {
133 usage();
134 return;
135 } else {
136 usage();
137 std.os.exit(1);
138 }
139 }
140
141 inline for (hashes) |H| {
142 if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
143 const result = try benchmarkHash(H, count);
144 try printPad(stdout, H.name);
145 try stdout.print(": {:4} MiB/s [{:16}]\n", result.throughput / (1 * MiB), result.hash);
146 }
147 }
148}
std/hash/wyhash.zig created+135
......@@ -0,0 +1,135 @@
1const std = @import("std");
2const mem = std.mem;
3
4const primes = [_]u64{
5 0xa0761d6478bd642f,
6 0xe7037ed1a0b428db,
7 0x8ebc6af09c88c6e3,
8 0x589965cc75374cc3,
9 0x1d8e4e27c47d124f,
10};
11
12fn read_bytes(comptime bytes: u8, data: []const u8) u64 {
13 return mem.readVarInt(u64, data[0..bytes], .Little);
14}
15
16fn read_8bytes_swapped(data: []const u8) u64 {
17 return (read_bytes(4, data) << 32 | read_bytes(4, data[4..]));
18}
19
20fn mum(a: u64, b: u64) u64 {
21 var r = std.math.mulWide(u64, a, b);
22 r = (r >> 64) ^ r;
23 return @truncate(u64, r);
24}
25
26fn mix0(a: u64, b: u64, seed: u64) u64 {
27 return mum(a ^ seed ^ primes[0], b ^ seed ^ primes[1]);
28}
29
30fn mix1(a: u64, b: u64, seed: u64) u64 {
31 return mum(a ^ seed ^ primes[2], b ^ seed ^ primes[3]);
32}
33
34pub const Wyhash = struct {
35 seed: u64,
36 msg_len: usize,
37
38 pub fn init(seed: u64) Wyhash {
39 return Wyhash{
40 .seed = seed,
41 .msg_len = 0,
42 };
43 }
44
45 fn round(self: *Wyhash, b: []const u8) void {
46 std.debug.assert(b.len == 32);
47
48 self.seed = mix0(
49 read_bytes(8, b[0..]),
50 read_bytes(8, b[8..]),
51 self.seed,
52 ) ^ mix1(
53 read_bytes(8, b[16..]),
54 read_bytes(8, b[24..]),
55 self.seed,
56 );
57 }
58
59 fn partial(self: *Wyhash, b: []const u8) void {
60 const rem_key = b;
61 const rem_len = b.len;
62
63 var seed = self.seed;
64 seed = switch (@intCast(u5, rem_len)) {
65 0 => seed,
66 1 => mix0(read_bytes(1, rem_key), primes[4], seed),
67 2 => mix0(read_bytes(2, rem_key), primes[4], seed),
68 3 => mix0((read_bytes(2, rem_key) << 8) | read_bytes(1, rem_key[2..]), primes[4], seed),
69 4 => mix0(read_bytes(4, rem_key), primes[4], seed),
70 5 => mix0((read_bytes(4, rem_key) << 8) | read_bytes(1, rem_key[4..]), primes[4], seed),
71 6 => mix0((read_bytes(4, rem_key) << 16) | read_bytes(2, rem_key[4..]), primes[4], seed),
72 7 => mix0((read_bytes(4, rem_key) << 24) | (read_bytes(2, rem_key[4..]) << 8) | read_bytes(1, rem_key[6..]), primes[4], seed),
73 8 => mix0(read_8bytes_swapped(rem_key), primes[4], seed),
74 9 => mix0(read_8bytes_swapped(rem_key), read_bytes(1, rem_key[8..]), seed),
75 10 => mix0(read_8bytes_swapped(rem_key), read_bytes(2, rem_key[8..]), seed),
76 11 => mix0(read_8bytes_swapped(rem_key), (read_bytes(2, rem_key[8..]) << 8) | read_bytes(1, rem_key[10..]), seed),
77 12 => mix0(read_8bytes_swapped(rem_key), read_bytes(4, rem_key[8..]), seed),
78 13 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 8) | read_bytes(1, rem_key[12..]), seed),
79 14 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 16) | read_bytes(2, rem_key[12..]), seed),
80 15 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 24) | (read_bytes(2, rem_key[12..]) << 8) | read_bytes(1, rem_key[14..]), seed),
81 16 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed),
82 17 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(1, rem_key[16..]), primes[4], seed),
83 18 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(2, rem_key[16..]), primes[4], seed),
84 19 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(2, rem_key[16..]) << 8) | read_bytes(1, rem_key[18..]), primes[4], seed),
85 20 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(4, rem_key[16..]), primes[4], seed),
86 21 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(4, rem_key[16..]) << 8) | read_bytes(1, rem_key[20..]), primes[4], seed),
87 22 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(4, rem_key[16..]) << 16) | read_bytes(2, rem_key[20..]), primes[4], seed),
88 23 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1((read_bytes(4, rem_key[16..]) << 24) | (read_bytes(2, rem_key[20..]) << 8) | read_bytes(1, rem_key[22..]), primes[4], seed),
89 24 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), primes[4], seed),
90 25 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), read_bytes(1, rem_key[24..]), seed),
91 26 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), read_bytes(2, rem_key[24..]), seed),
92 27 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(2, rem_key[24..]) << 8) | read_bytes(1, rem_key[26..]), seed),
93 28 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), read_bytes(4, rem_key[24..]), seed),
94 29 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(4, rem_key[24..]) << 8) | read_bytes(1, rem_key[28..]), seed),
95 30 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(4, rem_key[24..]) << 16) | read_bytes(2, rem_key[28..]), seed),
96 31 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), (read_bytes(4, rem_key[24..]) << 24) | (read_bytes(2, rem_key[28..]) << 8) | read_bytes(1, rem_key[30..]), seed),
97 };
98 self.seed = seed;
99 }
100
101 pub fn update(self: *Wyhash, b: []const u8) void {
102 var off: usize = 0;
103
104 // Full middle blocks.
105 while (off + 32 <= b.len) : (off += 32) {
106 @inlineCall(self.round, b[off .. off + 32]);
107 }
108
109 self.partial(b[off..]);
110 self.msg_len += b.len;
111 }
112
113 pub fn final(self: *Wyhash) u64 {
114 return mum(self.seed ^ self.msg_len, primes[4]);
115 }
116
117 pub fn hash(seed: u64, input: []const u8) u64 {
118 var c = Wyhash.init(seed);
119 c.update(input);
120 return c.final();
121 }
122};
123
124test "test vectors" {
125 const expectEqual = std.testing.expectEqual;
126 const hash = Wyhash.hash;
127
128 expectEqual(hash(0, ""), 0x0);
129 expectEqual(hash(1, "a"), 0xbed235177f41d328);
130 expectEqual(hash(2, "abc"), 0xbe348debe59b27c3);
131 expectEqual(hash(3, "message digest"), 0x37320f657213a290);
132 expectEqual(hash(4, "abcdefghijklmnopqrstuvwxyz"), 0xd0b270e1d8a7019c);
133 expectEqual(hash(5, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x602a1894d3bbfe7f);
134 expectEqual(hash(6, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x829e9c148b75970e);
135}
std/hash_map.zig+11-114
......@@ -4,6 +4,9 @@ const assert = debug.assert;
44const testing = std.testing;
55const math = std.math;
66const mem = std.mem;
7const meta = std.meta;
8const autoHash = std.hash.autoHash;
9const Wyhash = std.hash.Wyhash;
710const Allocator = mem.Allocator;
811const builtin = @import("builtin");
912
......@@ -448,15 +451,17 @@ test "iterator hash map" {
448451 try reset_map.putNoClobber(2, 22);
449452 try reset_map.putNoClobber(3, 33);
450453
454 // TODO this test depends on the hashing algorithm, because it assumes the
455 // order of the elements in the hashmap. This should not be the case.
451456 var keys = [_]i32{
457 1,
452458 3,
453459 2,
454 1,
455460 };
456461 var values = [_]i32{
462 11,
457463 33,
458464 22,
459 11,
460465 };
461466
462467 var it = reset_map.iterator();
......@@ -518,8 +523,9 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
518523pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
519524 return struct {
520525 fn hash(key: K) u32 {
521 comptime var rng = comptime std.rand.DefaultPrng.init(0);
522 return autoHash(key, &rng.random, u32);
526 var hasher = Wyhash.init(0);
527 autoHash(&hasher, key);
528 return @truncate(u32, hasher.final());
523529 }
524530 }.hash;
525531}
......@@ -527,116 +533,7 @@ pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
527533pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
528534 return struct {
529535 fn eql(a: K, b: K) bool {
530 return autoEql(a, b);
536 return meta.eql(a, b);
531537 }
532538 }.eql;
533539}
534
535// TODO improve these hash functions
536pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
537 switch (@typeInfo(@typeOf(key))) {
538 .NoReturn,
539 .Opaque,
540 .Undefined,
541 .ArgTuple,
542 .Frame,
543 .AnyFrame,
544 => @compileError("cannot hash this type"),
545
546 .Void,
547 .Null,
548 => return 0,
549
550 .Int => |info| {
551 const unsigned_x = @bitCast(@IntType(false, info.bits), key);
552 if (info.bits <= HashInt.bit_count) {
553 return HashInt(unsigned_x) ^ comptime rng.scalar(HashInt);
554 } else {
555 return @truncate(HashInt, unsigned_x ^ comptime rng.scalar(@typeOf(unsigned_x)));
556 }
557 },
558
559 .Float => |info| {
560 return autoHash(@bitCast(@IntType(false, info.bits), key), rng, HashInt);
561 },
562 .Bool => return autoHash(@boolToInt(key), rng, HashInt),
563 .Enum => return autoHash(@enumToInt(key), rng, HashInt),
564 .ErrorSet => return autoHash(@errorToInt(key), rng, HashInt),
565 .Fn => return autoHash(@ptrToInt(key), rng, HashInt),
566
567 .BoundFn,
568 .ComptimeFloat,
569 .ComptimeInt,
570 .Type,
571 .EnumLiteral,
572 => return 0,
573
574 .Pointer => |info| switch (info.size) {
575 .One => @compileError("TODO auto hash for single item pointers"),
576 .Many => @compileError("TODO auto hash for many item pointers"),
577 .C => @compileError("TODO auto hash C pointers"),
578 .Slice => {
579 const interval = std.math.max(1, key.len / 256);
580 var i: usize = 0;
581 var h = comptime rng.scalar(HashInt);
582 while (i < key.len) : (i += interval) {
583 h ^= autoHash(key[i], rng, HashInt);
584 }
585 return h;
586 },
587 },
588
589 .Optional => @compileError("TODO auto hash for optionals"),
590 .Array => @compileError("TODO auto hash for arrays"),
591 .Vector => @compileError("TODO auto hash for vectors"),
592 .Struct => @compileError("TODO auto hash for structs"),
593 .Union => @compileError("TODO auto hash for unions"),
594 .ErrorUnion => @compileError("TODO auto hash for unions"),
595 }
596}
597
598pub fn autoEql(a: var, b: @typeOf(a)) bool {
599 switch (@typeInfo(@typeOf(a))) {
600 .NoReturn,
601 .Opaque,
602 .Undefined,
603 .ArgTuple,
604 => @compileError("cannot test equality of this type"),
605 .Void,
606 .Null,
607 => return true,
608 .Bool,
609 .Int,
610 .Float,
611 .ComptimeFloat,
612 .ComptimeInt,
613 .EnumLiteral,
614 .Promise,
615 .Enum,
616 .BoundFn,
617 .Fn,
618 .ErrorSet,
619 .Type,
620 => return a == b,
621
622 .Pointer => |info| switch (info.size) {
623 .One => @compileError("TODO auto eql for single item pointers"),
624 .Many => @compileError("TODO auto eql for many item pointers"),
625 .C => @compileError("TODO auto eql for C pointers"),
626 .Slice => {
627 if (a.len != b.len) return false;
628 for (a) |a_item, i| {
629 if (!autoEql(a_item, b[i])) return false;
630 }
631 return true;
632 },
633 },
634
635 .Optional => @compileError("TODO auto eql for optionals"),
636 .Array => @compileError("TODO auto eql for arrays"),
637 .Struct => @compileError("TODO auto eql for structs"),
638 .Union => @compileError("TODO auto eql for unions"),
639 .ErrorUnion => @compileError("TODO auto eql for unions"),
640 .Vector => @compileError("TODO auto eql for vectors"),
641 }
642}
std/http/headers.zig+11-1
......@@ -102,9 +102,19 @@ test "HeaderEntry" {
102102 testing.expectEqualSlices(u8, "x", e.value);
103103}
104104
105fn stringEql(a: []const u8, b: []const u8) bool {
106 if (a.len != b.len) return false;
107 if (a.ptr == b.ptr) return true;
108 return mem.compare(u8, a, b) == .Equal;
109}
110
111fn stringHash(s: []const u8) u32 {
112 return @truncate(u32, std.hash.Wyhash.hash(0, s));
113}
114
105115const HeaderList = std.ArrayList(HeaderEntry);
106116const HeaderIndexList = std.ArrayList(usize);
107const HeaderIndex = std.AutoHashMap([]const u8, HeaderIndexList);
117const HeaderIndex = std.HashMap([]const u8, HeaderIndexList, stringHash, stringEql);
108118
109119pub const Headers = struct {
110120 // the owned header field name is stored in the index as part of the key
std/os.zig+5
......@@ -133,6 +133,11 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
133133 const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
134134 defer close(fd);
135135
136 const st = try fstat(fd);
137 if (!S_ISCHR(st.mode)) {
138 return error.NoDevice;
139 }
140
136141 const stream = &std.fs.File.openHandle(fd).inStream().stream;
137142 stream.readNoEof(buf) catch return error.Unexpected;
138143}
std/os/bits/darwin.zig+59
......@@ -1116,3 +1116,62 @@ pub const stack_t = extern struct {
11161116 ss_size: isize,
11171117 ss_flags: i32,
11181118};
1119
1120pub const S_IFMT = 0o170000;
1121
1122pub const S_IFIFO = 0o010000;
1123pub const S_IFCHR = 0o020000;
1124pub const S_IFDIR = 0o040000;
1125pub const S_IFBLK = 0o060000;
1126pub const S_IFREG = 0o100000;
1127pub const S_IFLNK = 0o120000;
1128pub const S_IFSOCK = 0o140000;
1129pub const S_IFWHT = 0o160000;
1130
1131pub const S_ISUID = 0o4000;
1132pub const S_ISGID = 0o2000;
1133pub const S_ISVTX = 0o1000;
1134pub const S_IRWXU = 0o700;
1135pub const S_IRUSR = 0o400;
1136pub const S_IWUSR = 0o200;
1137pub const S_IXUSR = 0o100;
1138pub const S_IRWXG = 0o070;
1139pub const S_IRGRP = 0o040;
1140pub const S_IWGRP = 0o020;
1141pub const S_IXGRP = 0o010;
1142pub const S_IRWXO = 0o007;
1143pub const S_IROTH = 0o004;
1144pub const S_IWOTH = 0o002;
1145pub const S_IXOTH = 0o001;
1146
1147pub fn S_ISFIFO(m: u32) bool {
1148 return m & S_IFMT == S_IFIFO;
1149}
1150
1151pub fn S_ISCHR(m: u32) bool {
1152 return m & S_IFMT == S_IFCHR;
1153}
1154
1155pub fn S_ISDIR(m: u32) bool {
1156 return m & S_IFMT == S_IFDIR;
1157}
1158
1159pub fn S_ISBLK(m: u32) bool {
1160 return m & S_IFMT == S_IFBLK;
1161}
1162
1163pub fn S_ISREG(m: u32) bool {
1164 return m & S_IFMT == S_IFREG;
1165}
1166
1167pub fn S_ISLNK(m: u32) bool {
1168 return m & S_IFMT == S_IFLNK;
1169}
1170
1171pub fn S_ISSOCK(m: u32) bool {
1172 return m & S_IFMT == S_IFSOCK;
1173}
1174
1175pub fn S_IWHT(m: u32) bool {
1176 return m & S_IFMT == S_IFWHT;
1177}
std/os/bits/freebsd.zig+59
......@@ -876,3 +876,62 @@ pub const stack_t = extern struct {
876876 ss_size: isize,
877877 ss_flags: i32,
878878};
879
880pub const S_IFMT = 0o170000;
881
882pub const S_IFIFO = 0o010000;
883pub const S_IFCHR = 0o020000;
884pub const S_IFDIR = 0o040000;
885pub const S_IFBLK = 0o060000;
886pub const S_IFREG = 0o100000;
887pub const S_IFLNK = 0o120000;
888pub const S_IFSOCK = 0o140000;
889pub const S_IFWHT = 0o160000;
890
891pub const S_ISUID = 0o4000;
892pub const S_ISGID = 0o2000;
893pub const S_ISVTX = 0o1000;
894pub const S_IRWXU = 0o700;
895pub const S_IRUSR = 0o400;
896pub const S_IWUSR = 0o200;
897pub const S_IXUSR = 0o100;
898pub const S_IRWXG = 0o070;
899pub const S_IRGRP = 0o040;
900pub const S_IWGRP = 0o020;
901pub const S_IXGRP = 0o010;
902pub const S_IRWXO = 0o007;
903pub const S_IROTH = 0o004;
904pub const S_IWOTH = 0o002;
905pub const S_IXOTH = 0o001;
906
907pub fn S_ISFIFO(m: u32) bool {
908 return m & S_IFMT == S_IFIFO;
909}
910
911pub fn S_ISCHR(m: u32) bool {
912 return m & S_IFMT == S_IFCHR;
913}
914
915pub fn S_ISDIR(m: u32) bool {
916 return m & S_IFMT == S_IFDIR;
917}
918
919pub fn S_ISBLK(m: u32) bool {
920 return m & S_IFMT == S_IFBLK;
921}
922
923pub fn S_ISREG(m: u32) bool {
924 return m & S_IFMT == S_IFREG;
925}
926
927pub fn S_ISLNK(m: u32) bool {
928 return m & S_IFMT == S_IFLNK;
929}
930
931pub fn S_ISSOCK(m: u32) bool {
932 return m & S_IFMT == S_IFSOCK;
933}
934
935pub fn S_IWHT(m: u32) bool {
936 return m & S_IFMT == S_IFWHT;
937}
std/os/darwin.zig+1
......@@ -5,3 +5,4 @@ pub const is_the_target = switch (builtin.os) {
55 else => false,
66};
77pub usingnamespace std.c;
8pub usingnamespace @import("bits.zig");
\ No newline at end of file
std/os/freebsd.zig+1
......@@ -2,3 +2,4 @@ const std = @import("../std.zig");
22const builtin = @import("builtin");
33pub const is_the_target = builtin.os == .freebsd;
44pub usingnamespace std.c;
5pub usingnamespace @import("bits.zig");
\ No newline at end of file
std/rb.zig+49-2
......@@ -234,10 +234,13 @@ pub const Tree = struct {
234234 return null;
235235 }
236236
237 /// lookup searches for the value of key, using binary search. It will
238 /// return a pointer to the node if it is there, otherwise it will return null.
239 /// Complexity guaranteed O(log n), where n is the number of nodes book-kept
240 /// by tree.
237241 pub fn lookup(tree: *Tree, key: *Node) ?*Node {
238 var parent: *Node = undefined;
242 var parent: ?*Node = undefined;
239243 var is_left: bool = undefined;
240
241244 return doLookup(key, tree, &parent, &is_left);
242245 }
243246
......@@ -545,3 +548,47 @@ test "rb" {
545548 num = testGetNumber(num.node.next().?);
546549 }
547550}
551
552
553test "inserting and looking up" {
554 var tree: Tree = undefined;
555 tree.init(testCompare);
556 var number: testNumber = undefined;
557 number.value = 1000;
558 _ = tree.insert(&number.node);
559 var dup: testNumber = undefined;
560 //Assert that tuples with identical value fields finds the same pointer
561 dup.value = 1000;
562 assert(tree.lookup(&dup.node) == &number.node);
563 //Assert that tuples with identical values do not clobber when inserted.
564 _ = tree.insert(&dup.node);
565 assert(tree.lookup(&dup.node) == &number.node);
566 assert(tree.lookup(&number.node) != &dup.node);
567 assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value);
568 //Assert that if looking for a non-existing value, return null.
569 var non_existing_value: testNumber = undefined;
570 non_existing_value.value = 1234;
571 assert(tree.lookup(&non_existing_value.node) == null);
572}
573
574test "multiple inserts, followed by calling first and last" {
575 var tree: Tree = undefined;
576 tree.init(testCompare);
577 var zeroth: testNumber = undefined;
578 zeroth.value = 0;
579 var first: testNumber = undefined;
580 first.value = 1;
581 var second: testNumber = undefined;
582 second.value = 2;
583 var third: testNumber = undefined;
584 third.value = 3;
585 _ = tree.insert(&zeroth.node);
586 _ = tree.insert(&first.node);
587 _ = tree.insert(&second.node);
588 _ = tree.insert(&third.node);
589 assert(testGetNumber(tree.first().?).value == 0);
590 assert(testGetNumber(tree.last().?).value == 3);
591 var lookupNode: testNumber = undefined;
592 lookupNode.value = 3;
593 assert(tree.lookup(&lookupNode.node) == &third.node);
594}