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
signature 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:...@@ -25,6 +25,7 @@ Here are some examples:
2525
26 * [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html)26 * [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html)
27 * [The Right Tool for the Right Job: Redis Modules & Zig](https://www.youtube.com/watch?v=eCHM8-_poZY)27 * [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
29Zig is a brand new language, with no advertising budget. Word of mouth is the30Zig is a brand new language, with no advertising budget. Word of mouth is the
30only way people find out about the project, and the more people hear about it,31only 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....@@ -45,8 +46,8 @@ The most highly regarded argument in such a discussion is a real world use case.
4546
46The issue label47The issue label
47[Contributor Friendly](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributor+friendly%22)48[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/or49exists to help you find issues that are **limited in scope and/or
49knowledge of Zig internals."50knowledge of Zig internals.**
5051
51### Editing Source Code52### Editing Source Code
5253
...@@ -61,8 +62,7 @@ To test changes, do the following from the build directory:...@@ -61,8 +62,7 @@ To test changes, do the following from the build directory:
6162
621. Run `make install` (on POSIX) or631. Run `make install` (on POSIX) or
63 `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows).64 `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows).
642. `bin/zig build --build-file ../build.zig test` (on POSIX) or652. `bin/zig build test` (on POSIX) or `bin\zig.exe build test` (on Windows).
65 `bin\zig.exe build --build-file ..\build.zig test` (on Windows).
6666
67That runs the whole test suite, which does a lot of extra testing that you67That runs the whole test suite, which does a lot of extra testing that you
68likely won't always need, and can take upwards of 2 hours. This is what the68likely 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,...@@ -79,8 +79,8 @@ Another example is choosing a different set of things to test. For example,
79not the other ones. Combining this suggestion with the previous one, you could79not the other ones. Combining this suggestion with the previous one, you could
80do this:80do this:
8181
82`bin/zig build --build-file ../build.zig test-std -Dskip-release` (on POSIX) or82`bin/zig build test-std -Dskip-release` (on POSIX) or
83`bin\zig.exe build --build-file ..\build.zig test-std -Dskip-release` (on Windows).83`bin\zig.exe build test-std -Dskip-release` (on Windows).
8484
85This will run only the standard library tests, in debug mode only, for all85This will run only the standard library tests, in debug mode only, for all
86targets (it will cross-compile the tests for non-native targets but not run86targets (it will cross-compile the tests for non-native targets but not run
doc/langref.html.in+16
...@@ -6330,6 +6330,22 @@ comptime {...@@ -6330,6 +6330,22 @@ comptime {
6330 TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe6330 TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe
6331 we can remove this restriction6331 we can remove this restriction
6332 </p>6332 </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>
6333 {#header_close#}6349 {#header_close#}
6334 {#header_open|@bitCast#}6350 {#header_open|@bitCast#}
6335 <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>6351 <pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
std/build.zig+16-16
...@@ -805,11 +805,7 @@ pub const Builder = struct {...@@ -805,11 +805,7 @@ pub const Builder = struct {
805 return name;805 return name;
806 }806 }
807 const full_path = try fs.path.join(self.allocator, [_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });807 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| {808 return fs.realpathAlloc(self.allocator, full_path) catch continue;
809 return real_path;
810 } else |_| {
811 continue;
812 }
813 }809 }
814 }810 }
815 if (self.env_map.get("PATH")) |PATH| {811 if (self.env_map.get("PATH")) |PATH| {
...@@ -817,14 +813,10 @@ pub const Builder = struct {...@@ -817,14 +813,10 @@ pub const Builder = struct {
817 if (fs.path.isAbsolute(name)) {813 if (fs.path.isAbsolute(name)) {
818 return name;814 return name;
819 }815 }
820 var it = mem.tokenize(PATH, []u8{fs.path.delimiter});816 var it = mem.tokenize(PATH, [_]u8{fs.path.delimiter});
821 while (it.next()) |path| {817 while (it.next()) |path| {
822 const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });818 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| {819 return fs.realpathAlloc(self.allocator, full_path) catch continue;
824 return real_path;
825 } else |_| {
826 continue;
827 }
828 }820 }
829 }821 }
830 }822 }
...@@ -834,11 +826,7 @@ pub const Builder = struct {...@@ -834,11 +826,7 @@ pub const Builder = struct {
834 }826 }
835 for (paths) |path| {827 for (paths) |path| {
836 const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });828 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| {829 return fs.realpathAlloc(self.allocator, full_path) catch continue;
838 return real_path;
839 } else |_| {
840 continue;
841 }
842 }830 }
843 }831 }
844 return error.FileNotFound;832 return error.FileNotFound;
...@@ -904,6 +892,15 @@ pub const Builder = struct {...@@ -904,6 +892,15 @@ pub const Builder = struct {
904 }892 }
905};893};
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
907pub const Version = struct {904pub const Version = struct {
908 major: u32,905 major: u32,
909 minor: u32,906 minor: u32,
...@@ -1122,6 +1119,9 @@ pub const Target = union(enum) {...@@ -1122,6 +1119,9 @@ pub const Target = union(enum) {
1122 }1119 }
11231120
1124 pub fn libPrefix(self: Target) []const u8 {1121 pub fn libPrefix(self: Target) []const u8 {
1122 if (self.isWasm()) {
1123 return "";
1124 }
1125 switch (self.getAbi()) {1125 switch (self.getAbi()) {
1126 .msvc => return "",1126 .msvc => return "",
1127 else => return "lib",1127 else => return "lib",
std/debug.zig+1-2
...@@ -1024,8 +1024,7 @@ pub fn openElfDebugInfo(...@@ -1024,8 +1024,7 @@ pub fn openElfDebugInfo(
1024 elf_seekable_stream: *DwarfSeekableStream,1024 elf_seekable_stream: *DwarfSeekableStream,
1025 elf_in_stream: *DwarfInStream,1025 elf_in_stream: *DwarfInStream,
1026) !DwarfInfo {1026) !DwarfInfo {
1027 var efile: elf.Elf = undefined;1027 var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream);
1028 try efile.openStream(allocator, elf_seekable_stream, elf_in_stream);
1029 errdefer efile.close();1028 errdefer efile.close();
10301029
1031 var di = DwarfInfo{1030 var di = DwarfInfo{
std/elf.zig+6-9
...@@ -356,7 +356,6 @@ pub const SectionHeader = struct {...@@ -356,7 +356,6 @@ pub const SectionHeader = struct {
356pub const Elf = struct {356pub const Elf = struct {
357 seekable_stream: *io.SeekableStream(anyerror, anyerror),357 seekable_stream: *io.SeekableStream(anyerror, anyerror),
358 in_stream: *io.InStream(anyerror),358 in_stream: *io.InStream(anyerror),
359 auto_close_stream: bool,
360 is_64: bool,359 is_64: bool,
361 endian: builtin.Endian,360 endian: builtin.Endian,
362 file_type: FileType,361 file_type: FileType,
...@@ -368,25 +367,23 @@ pub const Elf = struct {...@@ -368,25 +367,23 @@ pub const Elf = struct {
368 string_section: *SectionHeader,367 string_section: *SectionHeader,
369 section_headers: []SectionHeader,368 section_headers: []SectionHeader,
370 allocator: *mem.Allocator,369 allocator: *mem.Allocator,
371 prealloc_file: File,
372370
373 /// Call close when done.371 /// 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 {
375 @compileError("TODO implement");373 @compileError("TODO implement");
376 }374 }
377375
378 /// Call close when done.376 /// 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 {
380 @compileError("TODO implement");378 @compileError("TODO implement");
381 }379 }
382380
383 pub fn openStream(381 pub fn openStream(
384 elf: *Elf,
385 allocator: *mem.Allocator,382 allocator: *mem.Allocator,
386 seekable_stream: *io.SeekableStream(anyerror, anyerror),383 seekable_stream: *io.SeekableStream(anyerror, anyerror),
387 in: *io.InStream(anyerror),384 in: *io.InStream(anyerror),
388 ) !void {385 ) !Elf {
389 elf.auto_close_stream = false;386 var elf: Elf = undefined;
390 elf.allocator = allocator;387 elf.allocator = allocator;
391 elf.seekable_stream = seekable_stream;388 elf.seekable_stream = seekable_stream;
392 elf.in_stream = in;389 elf.in_stream = in;
...@@ -523,12 +520,12 @@ pub const Elf = struct {...@@ -523,12 +520,12 @@ pub const Elf = struct {
523 // not a string table520 // not a string table
524 return error.InvalidFormat;521 return error.InvalidFormat;
525 }522 }
523
524 return elf;
526 }525 }
527526
528 pub fn close(elf: *Elf) void {527 pub fn close(elf: *Elf) void {
529 elf.allocator.free(elf.section_headers);528 elf.allocator.free(elf.section_headers);
530
531 if (elf.auto_close_stream) elf.prealloc_file.close();
532 }529 }
533530
534 pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {531 pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
std/hash.zig+10
...@@ -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;
...@@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128;...@@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128;
1619
17pub const murmur = @import("hash/murmur.zig");20pub const murmur = @import("hash/murmur.zig");
18pub const Murmur2_32 = murmur.Murmur2_32;21pub const Murmur2_32 = murmur.Murmur2_32;
22
23
19pub const Murmur2_64 = murmur.Murmur2_64;24pub const Murmur2_64 = murmur.Murmur2_64;
20pub const Murmur3_32 = murmur.Murmur3_32;25pub const Murmur3_32 = murmur.Murmur3_32;
2126
...@@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig");...@@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig");
23pub const CityHash32 = cityhash.CityHash32;28pub const CityHash32 = cityhash.CityHash32;
24pub const CityHash64 = cityhash.CityHash64;29pub const CityHash64 = cityhash.CityHash64;
2530
31const wyhash = @import("hash/wyhash.zig");
32pub const Wyhash = wyhash.Wyhash;
33
26test "hash" {34test "hash" {
27 _ = @import("hash/adler.zig");35 _ = @import("hash/adler.zig");
36 _ = @import("hash/auto_hash.zig");
28 _ = @import("hash/crc.zig");37 _ = @import("hash/crc.zig");
29 _ = @import("hash/fnv.zig");38 _ = @import("hash/fnv.zig");
30 _ = @import("hash/siphash.zig");39 _ = @import("hash/siphash.zig");
31 _ = @import("hash/murmur.zig");40 _ = @import("hash/murmur.zig");
32 _ = @import("hash/cityhash.zig");41 _ = @import("hash/cityhash.zig");
42 _ = @import("hash/wyhash.zig");
33}43}
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;...@@ -4,6 +4,9 @@ const assert = debug.assert;
4const testing = std.testing;4const testing = std.testing;
5const math = std.math;5const math = std.math;
6const mem = std.mem;6const mem = std.mem;
7const meta = std.meta;
8const autoHash = std.hash.autoHash;
9const Wyhash = std.hash.Wyhash;
7const Allocator = mem.Allocator;10const Allocator = mem.Allocator;
8const builtin = @import("builtin");11const builtin = @import("builtin");
912
...@@ -448,15 +451,17 @@ test "iterator hash map" {...@@ -448,15 +451,17 @@ test "iterator hash map" {
448 try reset_map.putNoClobber(2, 22);451 try reset_map.putNoClobber(2, 22);
449 try reset_map.putNoClobber(3, 33);452 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.
451 var keys = [_]i32{456 var keys = [_]i32{
457 1,
452 3,458 3,
453 2,459 2,
454 1,
455 };460 };
456 var values = [_]i32{461 var values = [_]i32{
462 11,
457 33,463 33,
458 22,464 22,
459 11,
460 };465 };
461466
462 var it = reset_map.iterator();467 var it = reset_map.iterator();
...@@ -518,8 +523,9 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {...@@ -518,8 +523,9 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
518pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {523pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
519 return struct {524 return struct {
520 fn hash(key: K) u32 {525 fn hash(key: K) u32 {
521 comptime var rng = comptime std.rand.DefaultPrng.init(0);526 var hasher = Wyhash.init(0);
522 return autoHash(key, &rng.random, u32);527 autoHash(&hasher, key);
528 return @truncate(u32, hasher.final());
523 }529 }
524 }.hash;530 }.hash;
525}531}
...@@ -527,116 +533,7 @@ pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {...@@ -527,116 +533,7 @@ pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
527pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {533pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
528 return struct {534 return struct {
529 fn eql(a: K, b: K) bool {535 fn eql(a: K, b: K) bool {
530 return autoEql(a, b);536 return meta.eql(a, b);
531 }537 }
532 }.eql;538 }.eql;
533}539}
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" {...@@ -102,9 +102,19 @@ test "HeaderEntry" {
102 testing.expectEqualSlices(u8, "x", e.value);102 testing.expectEqualSlices(u8, "x", e.value);
103}103}
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
105const HeaderList = std.ArrayList(HeaderEntry);115const HeaderList = std.ArrayList(HeaderEntry);
106const HeaderIndexList = std.ArrayList(usize);116const HeaderIndexList = std.ArrayList(usize);
107const HeaderIndex = std.AutoHashMap([]const u8, HeaderIndexList);117const HeaderIndex = std.HashMap([]const u8, HeaderIndexList, stringHash, stringEql);
108118
109pub const Headers = struct {119pub const Headers = struct {
110 // the owned header field name is stored in the index as part of the key120 // 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 {...@@ -133,6 +133,11 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
133 const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);133 const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
134 defer close(fd);134 defer close(fd);
135135
136 const st = try fstat(fd);
137 if (!S_ISCHR(st.mode)) {
138 return error.NoDevice;
139 }
140
136 const stream = &std.fs.File.openHandle(fd).inStream().stream;141 const stream = &std.fs.File.openHandle(fd).inStream().stream;
137 stream.readNoEof(buf) catch return error.Unexpected;142 stream.readNoEof(buf) catch return error.Unexpected;
138}143}
std/os/bits/darwin.zig+59
...@@ -1116,3 +1116,62 @@ pub const stack_t = extern struct {...@@ -1116,3 +1116,62 @@ pub const stack_t = extern struct {
1116 ss_size: isize,1116 ss_size: isize,
1117 ss_flags: i32,1117 ss_flags: i32,
1118};1118};
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 {...@@ -876,3 +876,62 @@ pub const stack_t = extern struct {
876 ss_size: isize,876 ss_size: isize,
877 ss_flags: i32,877 ss_flags: i32,
878};878};
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) {...@@ -5,3 +5,4 @@ pub const is_the_target = switch (builtin.os) {
5 else => false,5 else => false,
6};6};
7pub usingnamespace std.c;7pub 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");...@@ -2,3 +2,4 @@ const std = @import("../std.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
3pub const is_the_target = builtin.os == .freebsd;3pub const is_the_target = builtin.os == .freebsd;
4pub usingnamespace std.c;4pub 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 {...@@ -234,10 +234,13 @@ pub const Tree = struct {
234 return null;234 return null;
235 }235 }
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.
237 pub fn lookup(tree: *Tree, key: *Node) ?*Node {241 pub fn lookup(tree: *Tree, key: *Node) ?*Node {
238 var parent: *Node = undefined;242 var parent: ?*Node = undefined;
239 var is_left: bool = undefined;243 var is_left: bool = undefined;
240
241 return doLookup(key, tree, &parent, &is_left);244 return doLookup(key, tree, &parent, &is_left);
242 }245 }
243246
...@@ -545,3 +548,47 @@ test "rb" {...@@ -545,3 +548,47 @@ test "rb" {
545 num = testGetNumber(num.node.next().?);548 num = testGetNumber(num.node.next().?);
546 }549 }
547}550}
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}