| author | |
| committer | |
| log | b7c94be6881e68fe40b432778163f1e9bca43e3d |
| tree | 9c30ae943bfca71087e9f4587e41887f250b3866 |
| parent | fbf21efd24bf812e0fd52a5917708a4c45f05b5e |
| parent | 30466bccefedd5e795b72b422f98b8a58e786289 |
| signature |
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: |
| 25 | 25 | ||
| 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/) | ||
| 28 | 29 | ||
| 29 | Zig is a brand new language, with no advertising budget. Word of mouth is the | 30 | Zig is a brand new language, with no advertising budget. Word of mouth is the |
| 30 | only way people find out about the project, and the more people hear about it, | 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,8 +46,8 @@ The most highly regarded argument in such a discussion is a real world use case. |
| 45 | 46 | ||
| 46 | The issue label | 47 | The 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) |
| 48 | exists to help contributors find issues that are "limited in scope and/or | 49 | exists to help you find issues that are **limited in scope and/or |
| 49 | knowledge of Zig internals." | 50 | knowledge of Zig internals.** |
| 50 | 51 | ||
| 51 | ### Editing Source Code | 52 | ### Editing Source Code |
| 52 | 53 | ||
| ... | @@ -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: |
| 61 | 62 | ||
| 62 | 1. Run `make install` (on POSIX) or | 63 | 1. Run `make install` (on POSIX) or |
| 63 | `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows). | 64 | `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows). |
| 64 | 2. `bin/zig build --build-file ../build.zig test` (on POSIX) or | 65 | 2. `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). | ||
| 66 | 66 | ||
| 67 | That runs the whole test suite, which does a lot of extra testing that you | 67 | That runs the whole test suite, which does a lot of extra testing that you |
| 68 | likely won't always need, and can take upwards of 2 hours. This is what the | 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,8 +79,8 @@ Another example is choosing a different set of things to test. For example, |
| 79 | not the other ones. Combining this suggestion with the previous one, you could | 79 | not the other ones. Combining this suggestion with the previous one, you could |
| 80 | do this: | 80 | do this: |
| 81 | 81 | ||
| 82 | `bin/zig build --build-file ../build.zig test-std -Dskip-release` (on POSIX) or | 82 | `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). |
| 84 | 84 | ||
| 85 | This will run only the standard library tests, in debug mode only, for all | 85 | This will run only the standard library tests, in debug mode only, for all |
| 86 | targets (it will cross-compile the tests for non-native targets but not run | 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,6 +6330,22 @@ comptime { |
| 6330 | TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe | 6330 | 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 restriction | 6331 | 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 | }; |
| 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 | pub const Version = struct { | 904 | pub 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 | } |
| 1123 | 1120 | ||
| 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(); |
| 1030 | 1029 | ||
| 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 { |
| 356 | pub const Elf = struct { | 356 | pub 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, | ||
| 372 | 370 | ||
| 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 | } |
| 377 | 375 | ||
| 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 | } |
| 382 | 380 | ||
| 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 table | 520 | // not a string table |
| 524 | return error.InvalidFormat; | 521 | return error.InvalidFormat; |
| 525 | } | 522 | } |
| 523 | |||
| 524 | return elf; | ||
| 526 | } | 525 | } |
| 527 | 526 | ||
| 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 | } |
| 533 | 530 | ||
| 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 @@ |
| 1 | const adler = @import("hash/adler.zig"); | 1 | const adler = @import("hash/adler.zig"); |
| 2 | pub const Adler32 = adler.Adler32; | 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 | // pub for polynomials + generic crc32 construction | 7 | // pub for polynomials + generic crc32 construction |
| 5 | pub const crc = @import("hash/crc.zig"); | 8 | pub const crc = @import("hash/crc.zig"); |
| 6 | pub const Crc32 = crc.Crc32; | 9 | pub const Crc32 = crc.Crc32; |
| ... | @@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128; | ... | @@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128; |
| 16 | 19 | ||
| 17 | pub const murmur = @import("hash/murmur.zig"); | 20 | pub const murmur = @import("hash/murmur.zig"); |
| 18 | pub const Murmur2_32 = murmur.Murmur2_32; | 21 | pub const Murmur2_32 = murmur.Murmur2_32; |
| 22 | |||
| 23 | |||
| 19 | pub const Murmur2_64 = murmur.Murmur2_64; | 24 | pub const Murmur2_64 = murmur.Murmur2_64; |
| 20 | pub const Murmur3_32 = murmur.Murmur3_32; | 25 | pub const Murmur3_32 = murmur.Murmur3_32; |
| 21 | 26 | ||
| ... | @@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig"); | ... | @@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig"); |
| 23 | pub const CityHash32 = cityhash.CityHash32; | 28 | pub const CityHash32 = cityhash.CityHash32; |
| 24 | pub const CityHash64 = cityhash.CityHash64; | 29 | pub const CityHash64 = cityhash.CityHash64; |
| 25 | 30 | ||
| 31 | const wyhash = @import("hash/wyhash.zig"); | ||
| 32 | pub const Wyhash = wyhash.Wyhash; | ||
| 33 | |||
| 26 | test "hash" { | 34 | test "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 @@ | ||
| 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,6 +4,9 @@ const assert = debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
| 5 | const math = std.math; | 5 | const math = std.math; |
| 6 | const mem = std.mem; | 6 | const mem = std.mem; |
| 7 | const meta = std.meta; | ||
| 8 | const autoHash = std.hash.autoHash; | ||
| 9 | const Wyhash = std.hash.Wyhash; | ||
| 7 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 8 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
| 9 | 12 | ||
| ... | @@ -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); |
| 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 | 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 | }; |
| 461 | 466 | ||
| 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) { |
| 518 | pub fn getAutoHashFn(comptime K: type) (fn (K) u32) { | 523 | pub 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) { |
| 527 | pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { | 533 | pub 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 | ||
| 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,9 +102,19 @@ test "HeaderEntry" { |
| 102 | testing.expectEqualSlices(u8, "x", e.value); | 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 | const HeaderList = std.ArrayList(HeaderEntry); | 115 | const HeaderList = std.ArrayList(HeaderEntry); |
| 106 | const HeaderIndexList = std.ArrayList(usize); | 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 | pub const Headers = struct { | 119 | pub const Headers = struct { |
| 110 | // the owned header field name is stored in the index as part of the key | 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,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); |
| 135 | 135 | ||
| 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 | |||
| 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,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 | |||
| 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,3 +5,4 @@ pub const is_the_target = switch (builtin.os) { |
| 5 | else => false, | 5 | else => false, |
| 6 | }; | 6 | }; |
| 7 | pub usingnamespace std.c; | 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,3 +2,4 @@ const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | pub const is_the_target = builtin.os == .freebsd; | 3 | pub const is_the_target = builtin.os == .freebsd; |
| 4 | pub usingnamespace std.c; | 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,10 +234,13 @@ pub const Tree = struct { |
| 234 | return null; | 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 | 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 | } |
| 243 | 246 | ||
| ... | @@ -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 | |||
| 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 | } |