| author | |
| committer | |
| log | b7c94be6881e68fe40b432778163f1e9bca43e3d |
| tree | 9c30ae943bfca71087e9f4587e41887f250b3866 |
| parent | fbf21efd24bf812e0fd52a5917708a4c45f05b5e |
| parent | 30466bccefedd5e795b72b422f98b8a58e786289 |
| signature | Commit is signed but in an unrecognized format. |
17 files changed, 745 insertions(+), 150 deletions(-)
CONTRIBUTING.md+6-6| ... | ... | @@ -25,6 +25,7 @@ Here are some examples: |
| 25 | 25 | |
| 26 | 26 | * [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html) |
| 27 | 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/) | |
| 28 | 29 | |
| 29 | 30 | Zig is a brand new language, with no advertising budget. Word of mouth is the |
| 30 | 31 | only 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 | 46 | |
| 46 | 47 | The issue label |
| 47 | 48 | [Contributor Friendly](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributor+friendly%22) |
| 48 | exists to help contributors find issues that are "limited in scope and/or | |
| 49 | knowledge of Zig internals." | |
| 49 | exists to help you find issues that are **limited in scope and/or | |
| 50 | knowledge of Zig internals.** | |
| 50 | 51 | |
| 51 | 52 | ### Editing Source Code |
| 52 | 53 | |
| ... | ... | @@ -61,8 +62,7 @@ To test changes, do the following from the build directory: |
| 61 | 62 | |
| 62 | 63 | 1. Run `make install` (on POSIX) or |
| 63 | 64 | `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows). |
| 64 | 2. `bin/zig build --build-file ../build.zig test` (on POSIX) or | |
| 65 | `bin\zig.exe build --build-file ..\build.zig test` (on Windows). | |
| 65 | 2. `bin/zig build test` (on POSIX) or `bin\zig.exe build test` (on Windows). | |
| 66 | 66 | |
| 67 | 67 | That runs the whole test suite, which does a lot of extra testing that you |
| 68 | 68 | likely 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 | 79 | not the other ones. Combining this suggestion with the previous one, you could |
| 80 | 80 | do this: |
| 81 | 81 | |
| 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). | |
| 84 | 84 | |
| 85 | 85 | This will run only the standard library tests, in debug mode only, for all |
| 86 | 86 | targets (it will cross-compile the tests for non-native targets but not run |
doc/langref.html.in+16| ... | ... | @@ -6330,6 +6330,22 @@ comptime { |
| 6330 | 6330 | TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe |
| 6331 | 6331 | we can remove this restriction |
| 6332 | 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 | 6349 | {#header_close#} |
| 6334 | 6350 | {#header_open|@bitCast#} |
| 6335 | 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 | 805 | return name; |
| 806 | 806 | } |
| 807 | 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| { | |
| 809 | return real_path; | |
| 810 | } else |_| { | |
| 811 | continue; | |
| 812 | } | |
| 808 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 813 | 809 | } |
| 814 | 810 | } |
| 815 | 811 | if (self.env_map.get("PATH")) |PATH| { |
| ... | ... | @@ -817,14 +813,10 @@ pub const Builder = struct { |
| 817 | 813 | if (fs.path.isAbsolute(name)) { |
| 818 | 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 | 817 | while (it.next()) |path| { |
| 822 | 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| { | |
| 824 | return real_path; | |
| 825 | } else |_| { | |
| 826 | continue; | |
| 827 | } | |
| 819 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 828 | 820 | } |
| 829 | 821 | } |
| 830 | 822 | } |
| ... | ... | @@ -834,11 +826,7 @@ pub const Builder = struct { |
| 834 | 826 | } |
| 835 | 827 | for (paths) |path| { |
| 836 | 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| { | |
| 838 | return real_path; | |
| 839 | } else |_| { | |
| 840 | continue; | |
| 841 | } | |
| 829 | return fs.realpathAlloc(self.allocator, full_path) catch continue; | |
| 842 | 830 | } |
| 843 | 831 | } |
| 844 | 832 | return error.FileNotFound; |
| ... | ... | @@ -904,6 +892,15 @@ pub const Builder = struct { |
| 904 | 892 | } |
| 905 | 893 | }; |
| 906 | 894 | |
| 895 | test "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 | ||
| 907 | 904 | pub const Version = struct { |
| 908 | 905 | major: u32, |
| 909 | 906 | minor: u32, |
| ... | ... | @@ -1122,6 +1119,9 @@ pub const Target = union(enum) { |
| 1122 | 1119 | } |
| 1123 | 1120 | |
| 1124 | 1121 | pub fn libPrefix(self: Target) []const u8 { |
| 1122 | if (self.isWasm()) { | |
| 1123 | return ""; | |
| 1124 | } | |
| 1125 | 1125 | switch (self.getAbi()) { |
| 1126 | 1126 | .msvc => return "", |
| 1127 | 1127 | else => return "lib", |
std/debug.zig+1-2| ... | ... | @@ -1024,8 +1024,7 @@ pub fn openElfDebugInfo( |
| 1024 | 1024 | elf_seekable_stream: *DwarfSeekableStream, |
| 1025 | 1025 | elf_in_stream: *DwarfInStream, |
| 1026 | 1026 | ) !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); | |
| 1029 | 1028 | errdefer efile.close(); |
| 1030 | 1029 | |
| 1031 | 1030 | var di = DwarfInfo{ |
std/elf.zig+6-9| ... | ... | @@ -356,7 +356,6 @@ pub const SectionHeader = struct { |
| 356 | 356 | pub const Elf = struct { |
| 357 | 357 | seekable_stream: *io.SeekableStream(anyerror, anyerror), |
| 358 | 358 | in_stream: *io.InStream(anyerror), |
| 359 | auto_close_stream: bool, | |
| 360 | 359 | is_64: bool, |
| 361 | 360 | endian: builtin.Endian, |
| 362 | 361 | file_type: FileType, |
| ... | ... | @@ -368,25 +367,23 @@ pub const Elf = struct { |
| 368 | 367 | string_section: *SectionHeader, |
| 369 | 368 | section_headers: []SectionHeader, |
| 370 | 369 | allocator: *mem.Allocator, |
| 371 | prealloc_file: File, | |
| 372 | 370 | |
| 373 | 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 | 373 | @compileError("TODO implement"); |
| 376 | 374 | } |
| 377 | 375 | |
| 378 | 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 | 378 | @compileError("TODO implement"); |
| 381 | 379 | } |
| 382 | 380 | |
| 383 | 381 | pub fn openStream( |
| 384 | elf: *Elf, | |
| 385 | 382 | allocator: *mem.Allocator, |
| 386 | 383 | seekable_stream: *io.SeekableStream(anyerror, anyerror), |
| 387 | 384 | in: *io.InStream(anyerror), |
| 388 | ) !void { | |
| 389 | elf.auto_close_stream = false; | |
| 385 | ) !Elf { | |
| 386 | var elf: Elf = undefined; | |
| 390 | 387 | elf.allocator = allocator; |
| 391 | 388 | elf.seekable_stream = seekable_stream; |
| 392 | 389 | elf.in_stream = in; |
| ... | ... | @@ -523,12 +520,12 @@ pub const Elf = struct { |
| 523 | 520 | // not a string table |
| 524 | 521 | return error.InvalidFormat; |
| 525 | 522 | } |
| 523 | ||
| 524 | return elf; | |
| 526 | 525 | } |
| 527 | 526 | |
| 528 | 527 | pub fn close(elf: *Elf) void { |
| 529 | 528 | elf.allocator.free(elf.section_headers); |
| 530 | ||
| 531 | if (elf.auto_close_stream) elf.prealloc_file.close(); | |
| 532 | 529 | } |
| 533 | 530 | |
| 534 | 531 | pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader { |
std/hash.zig+10| ... | ... | @@ -1,6 +1,9 @@ |
| 1 | 1 | const adler = @import("hash/adler.zig"); |
| 2 | 2 | pub const Adler32 = adler.Adler32; |
| 3 | 3 | |
| 4 | const auto_hash = @import("hash/auto_hash.zig"); | |
| 5 | pub const autoHash = auto_hash.autoHash; | |
| 6 | ||
| 4 | 7 | // pub for polynomials + generic crc32 construction |
| 5 | 8 | pub const crc = @import("hash/crc.zig"); |
| 6 | 9 | pub const Crc32 = crc.Crc32; |
| ... | ... | @@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128; |
| 16 | 19 | |
| 17 | 20 | pub const murmur = @import("hash/murmur.zig"); |
| 18 | 21 | pub const Murmur2_32 = murmur.Murmur2_32; |
| 22 | ||
| 23 | ||
| 19 | 24 | pub const Murmur2_64 = murmur.Murmur2_64; |
| 20 | 25 | pub const Murmur3_32 = murmur.Murmur3_32; |
| 21 | 26 | |
| ... | ... | @@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig"); |
| 23 | 28 | pub const CityHash32 = cityhash.CityHash32; |
| 24 | 29 | pub const CityHash64 = cityhash.CityHash64; |
| 25 | 30 | |
| 31 | const wyhash = @import("hash/wyhash.zig"); | |
| 32 | pub const Wyhash = wyhash.Wyhash; | |
| 33 | ||
| 26 | 34 | test "hash" { |
| 27 | 35 | _ = @import("hash/adler.zig"); |
| 36 | _ = @import("hash/auto_hash.zig"); | |
| 28 | 37 | _ = @import("hash/crc.zig"); |
| 29 | 38 | _ = @import("hash/fnv.zig"); |
| 30 | 39 | _ = @import("hash/siphash.zig"); |
| 31 | 40 | _ = @import("hash/murmur.zig"); |
| 32 | 41 | _ = @import("hash/cityhash.zig"); |
| 42 | _ = @import("hash/wyhash.zig"); | |
| 33 | 43 | } |
std/hash/auto_hash.zig created+211| ... | ... | @@ -0,0 +1,211 @@ |
| 1 | const std = @import("std"); | |
| 2 | const builtin = @import("builtin"); | |
| 3 | const mem = std.mem; | |
| 4 | const meta = std.meta; | |
| 5 | ||
| 6 | /// Provides generic hashing for any eligible type. | |
| 7 | /// Only hashes `key` itself, pointers are not followed. | |
| 8 | pub 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 | ||
| 113 | const testing = std.testing; | |
| 114 | const Wyhash = std.hash.Wyhash; | |
| 115 | ||
| 116 | fn 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 | ||
| 123 | test "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 | ||
| 139 | test "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 | ||
| 147 | test "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 | ||
| 157 | test "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 | ||
| 172 | test "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 | ||
| 190 | test "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 | ||
| 199 | test "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 @@ |
| 1 | const builtin = @import("builtin"); | |
| 2 | const std = @import("std"); | |
| 3 | const time = std.time; | |
| 4 | const Timer = time.Timer; | |
| 5 | const hash = std.hash; | |
| 6 | ||
| 7 | const KiB = 1024; | |
| 8 | const MiB = 1024 * KiB; | |
| 9 | const GiB = 1024 * MiB; | |
| 10 | ||
| 11 | var prng = std.rand.DefaultPrng.init(0); | |
| 12 | ||
| 13 | const Hash = struct { | |
| 14 | ty: type, | |
| 15 | name: []const u8, | |
| 16 | init_u8s: ?[]const u8 = null, | |
| 17 | init_u64: ?u64 = null, | |
| 18 | }; | |
| 19 | ||
| 20 | const siphash_key = "0123456789abcdef"; | |
| 21 | ||
| 22 | const 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 | ||
| 30 | const Result = struct { | |
| 31 | hash: u64, | |
| 32 | throughput: u64, | |
| 33 | }; | |
| 34 | ||
| 35 | pub 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 | ||
| 66 | fn 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 | ||
| 79 | fn 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. | |
| 84 | fn 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 | ||
| 92 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const mem = std.mem; | |
| 3 | ||
| 4 | const primes = [_]u64{ | |
| 5 | 0xa0761d6478bd642f, | |
| 6 | 0xe7037ed1a0b428db, | |
| 7 | 0x8ebc6af09c88c6e3, | |
| 8 | 0x589965cc75374cc3, | |
| 9 | 0x1d8e4e27c47d124f, | |
| 10 | }; | |
| 11 | ||
| 12 | fn read_bytes(comptime bytes: u8, data: []const u8) u64 { | |
| 13 | return mem.readVarInt(u64, data[0..bytes], .Little); | |
| 14 | } | |
| 15 | ||
| 16 | fn read_8bytes_swapped(data: []const u8) u64 { | |
| 17 | return (read_bytes(4, data) << 32 | read_bytes(4, data[4..])); | |
| 18 | } | |
| 19 | ||
| 20 | fn 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 | ||
| 26 | fn mix0(a: u64, b: u64, seed: u64) u64 { | |
| 27 | return mum(a ^ seed ^ primes[0], b ^ seed ^ primes[1]); | |
| 28 | } | |
| 29 | ||
| 30 | fn mix1(a: u64, b: u64, seed: u64) u64 { | |
| 31 | return mum(a ^ seed ^ primes[2], b ^ seed ^ primes[3]); | |
| 32 | } | |
| 33 | ||
| 34 | pub 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 | ||
| 124 | test "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 | 4 | const testing = std.testing; |
| 5 | 5 | const math = std.math; |
| 6 | 6 | const mem = std.mem; |
| 7 | const meta = std.meta; | |
| 8 | const autoHash = std.hash.autoHash; | |
| 9 | const Wyhash = std.hash.Wyhash; | |
| 7 | 10 | const Allocator = mem.Allocator; |
| 8 | 11 | const builtin = @import("builtin"); |
| 9 | 12 | |
| ... | ... | @@ -448,15 +451,17 @@ test "iterator hash map" { |
| 448 | 451 | try reset_map.putNoClobber(2, 22); |
| 449 | 452 | try reset_map.putNoClobber(3, 33); |
| 450 | 453 | |
| 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 | 456 | var keys = [_]i32{ |
| 457 | 1, | |
| 452 | 458 | 3, |
| 453 | 459 | 2, |
| 454 | 1, | |
| 455 | 460 | }; |
| 456 | 461 | var values = [_]i32{ |
| 462 | 11, | |
| 457 | 463 | 33, |
| 458 | 464 | 22, |
| 459 | 11, | |
| 460 | 465 | }; |
| 461 | 466 | |
| 462 | 467 | var it = reset_map.iterator(); |
| ... | ... | @@ -518,8 +523,9 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) { |
| 518 | 523 | pub fn getAutoHashFn(comptime K: type) (fn (K) u32) { |
| 519 | 524 | return struct { |
| 520 | 525 | 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()); | |
| 523 | 529 | } |
| 524 | 530 | }.hash; |
| 525 | 531 | } |
| ... | ... | @@ -527,116 +533,7 @@ pub fn getAutoHashFn(comptime K: type) (fn (K) u32) { |
| 527 | 533 | pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { |
| 528 | 534 | return struct { |
| 529 | 535 | fn eql(a: K, b: K) bool { |
| 530 | return autoEql(a, b); | |
| 536 | return meta.eql(a, b); | |
| 531 | 537 | } |
| 532 | 538 | }.eql; |
| 533 | 539 | } |
| 534 | ||
| 535 | // TODO improve these hash functions | |
| 536 | pub 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 | ||
| 598 | pub 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 | 102 | testing.expectEqualSlices(u8, "x", e.value); |
| 103 | 103 | } |
| 104 | 104 | |
| 105 | fn 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 | ||
| 111 | fn stringHash(s: []const u8) u32 { | |
| 112 | return @truncate(u32, std.hash.Wyhash.hash(0, s)); | |
| 113 | } | |
| 114 | ||
| 105 | 115 | const HeaderList = std.ArrayList(HeaderEntry); |
| 106 | 116 | const HeaderIndexList = std.ArrayList(usize); |
| 107 | const HeaderIndex = std.AutoHashMap([]const u8, HeaderIndexList); | |
| 117 | const HeaderIndex = std.HashMap([]const u8, HeaderIndexList, stringHash, stringEql); | |
| 108 | 118 | |
| 109 | 119 | pub const Headers = struct { |
| 110 | 120 | // 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 | 133 | const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0); |
| 134 | 134 | defer close(fd); |
| 135 | 135 | |
| 136 | const st = try fstat(fd); | |
| 137 | if (!S_ISCHR(st.mode)) { | |
| 138 | return error.NoDevice; | |
| 139 | } | |
| 140 | ||
| 136 | 141 | const stream = &std.fs.File.openHandle(fd).inStream().stream; |
| 137 | 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 | 1116 | ss_size: isize, |
| 1117 | 1117 | ss_flags: i32, |
| 1118 | 1118 | }; |
| 1119 | ||
| 1120 | pub const S_IFMT = 0o170000; | |
| 1121 | ||
| 1122 | pub const S_IFIFO = 0o010000; | |
| 1123 | pub const S_IFCHR = 0o020000; | |
| 1124 | pub const S_IFDIR = 0o040000; | |
| 1125 | pub const S_IFBLK = 0o060000; | |
| 1126 | pub const S_IFREG = 0o100000; | |
| 1127 | pub const S_IFLNK = 0o120000; | |
| 1128 | pub const S_IFSOCK = 0o140000; | |
| 1129 | pub const S_IFWHT = 0o160000; | |
| 1130 | ||
| 1131 | pub const S_ISUID = 0o4000; | |
| 1132 | pub const S_ISGID = 0o2000; | |
| 1133 | pub const S_ISVTX = 0o1000; | |
| 1134 | pub const S_IRWXU = 0o700; | |
| 1135 | pub const S_IRUSR = 0o400; | |
| 1136 | pub const S_IWUSR = 0o200; | |
| 1137 | pub const S_IXUSR = 0o100; | |
| 1138 | pub const S_IRWXG = 0o070; | |
| 1139 | pub const S_IRGRP = 0o040; | |
| 1140 | pub const S_IWGRP = 0o020; | |
| 1141 | pub const S_IXGRP = 0o010; | |
| 1142 | pub const S_IRWXO = 0o007; | |
| 1143 | pub const S_IROTH = 0o004; | |
| 1144 | pub const S_IWOTH = 0o002; | |
| 1145 | pub const S_IXOTH = 0o001; | |
| 1146 | ||
| 1147 | pub fn S_ISFIFO(m: u32) bool { | |
| 1148 | return m & S_IFMT == S_IFIFO; | |
| 1149 | } | |
| 1150 | ||
| 1151 | pub fn S_ISCHR(m: u32) bool { | |
| 1152 | return m & S_IFMT == S_IFCHR; | |
| 1153 | } | |
| 1154 | ||
| 1155 | pub fn S_ISDIR(m: u32) bool { | |
| 1156 | return m & S_IFMT == S_IFDIR; | |
| 1157 | } | |
| 1158 | ||
| 1159 | pub fn S_ISBLK(m: u32) bool { | |
| 1160 | return m & S_IFMT == S_IFBLK; | |
| 1161 | } | |
| 1162 | ||
| 1163 | pub fn S_ISREG(m: u32) bool { | |
| 1164 | return m & S_IFMT == S_IFREG; | |
| 1165 | } | |
| 1166 | ||
| 1167 | pub fn S_ISLNK(m: u32) bool { | |
| 1168 | return m & S_IFMT == S_IFLNK; | |
| 1169 | } | |
| 1170 | ||
| 1171 | pub fn S_ISSOCK(m: u32) bool { | |
| 1172 | return m & S_IFMT == S_IFSOCK; | |
| 1173 | } | |
| 1174 | ||
| 1175 | pub 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 | 876 | ss_size: isize, |
| 877 | 877 | ss_flags: i32, |
| 878 | 878 | }; |
| 879 | ||
| 880 | pub const S_IFMT = 0o170000; | |
| 881 | ||
| 882 | pub const S_IFIFO = 0o010000; | |
| 883 | pub const S_IFCHR = 0o020000; | |
| 884 | pub const S_IFDIR = 0o040000; | |
| 885 | pub const S_IFBLK = 0o060000; | |
| 886 | pub const S_IFREG = 0o100000; | |
| 887 | pub const S_IFLNK = 0o120000; | |
| 888 | pub const S_IFSOCK = 0o140000; | |
| 889 | pub const S_IFWHT = 0o160000; | |
| 890 | ||
| 891 | pub const S_ISUID = 0o4000; | |
| 892 | pub const S_ISGID = 0o2000; | |
| 893 | pub const S_ISVTX = 0o1000; | |
| 894 | pub const S_IRWXU = 0o700; | |
| 895 | pub const S_IRUSR = 0o400; | |
| 896 | pub const S_IWUSR = 0o200; | |
| 897 | pub const S_IXUSR = 0o100; | |
| 898 | pub const S_IRWXG = 0o070; | |
| 899 | pub const S_IRGRP = 0o040; | |
| 900 | pub const S_IWGRP = 0o020; | |
| 901 | pub const S_IXGRP = 0o010; | |
| 902 | pub const S_IRWXO = 0o007; | |
| 903 | pub const S_IROTH = 0o004; | |
| 904 | pub const S_IWOTH = 0o002; | |
| 905 | pub const S_IXOTH = 0o001; | |
| 906 | ||
| 907 | pub fn S_ISFIFO(m: u32) bool { | |
| 908 | return m & S_IFMT == S_IFIFO; | |
| 909 | } | |
| 910 | ||
| 911 | pub fn S_ISCHR(m: u32) bool { | |
| 912 | return m & S_IFMT == S_IFCHR; | |
| 913 | } | |
| 914 | ||
| 915 | pub fn S_ISDIR(m: u32) bool { | |
| 916 | return m & S_IFMT == S_IFDIR; | |
| 917 | } | |
| 918 | ||
| 919 | pub fn S_ISBLK(m: u32) bool { | |
| 920 | return m & S_IFMT == S_IFBLK; | |
| 921 | } | |
| 922 | ||
| 923 | pub fn S_ISREG(m: u32) bool { | |
| 924 | return m & S_IFMT == S_IFREG; | |
| 925 | } | |
| 926 | ||
| 927 | pub fn S_ISLNK(m: u32) bool { | |
| 928 | return m & S_IFMT == S_IFLNK; | |
| 929 | } | |
| 930 | ||
| 931 | pub fn S_ISSOCK(m: u32) bool { | |
| 932 | return m & S_IFMT == S_IFSOCK; | |
| 933 | } | |
| 934 | ||
| 935 | pub 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 | 5 | else => false, |
| 6 | 6 | }; |
| 7 | 7 | pub usingnamespace std.c; |
| 8 | pub 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 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | pub const is_the_target = builtin.os == .freebsd; |
| 4 | 4 | pub usingnamespace std.c; |
| 5 | pub usingnamespace @import("bits.zig"); | |
| \ No newline at end of file |
std/rb.zig+49-2| ... | ... | @@ -234,10 +234,13 @@ pub const Tree = struct { |
| 234 | 234 | return null; |
| 235 | 235 | } |
| 236 | 236 | |
| 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 | 241 | pub fn lookup(tree: *Tree, key: *Node) ?*Node { |
| 238 | var parent: *Node = undefined; | |
| 242 | var parent: ?*Node = undefined; | |
| 239 | 243 | var is_left: bool = undefined; |
| 240 | ||
| 241 | 244 | return doLookup(key, tree, &parent, &is_left); |
| 242 | 245 | } |
| 243 | 246 | |
| ... | ... | @@ -545,3 +548,47 @@ test "rb" { |
| 545 | 548 | num = testGetNumber(num.node.next().?); |
| 546 | 549 | } |
| 547 | 550 | } |
| 551 | ||
| 552 | ||
| 553 | test "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 | ||
| 574 | test "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 | } |