diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c19ef48829..2ee0e85ccf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,6 +25,7 @@ Here are some examples: * [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html) * [The Right Tool for the Right Job: Redis Modules & Zig](https://www.youtube.com/watch?v=eCHM8-_poZY) + * [Writing a small ray tracer in Rust and Zig](https://nelari.us/post/raytracer_with_rust_and_zig/) Zig is a brand new language, with no advertising budget. Word of mouth is the 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. The issue label [Contributor Friendly](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributor+friendly%22) -exists to help contributors find issues that are "limited in scope and/or -knowledge of Zig internals." +exists to help you find issues that are **limited in scope and/or +knowledge of Zig internals.** ### Editing Source Code @@ -61,8 +62,7 @@ To test changes, do the following from the build directory: 1. Run `make install` (on POSIX) or `msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows). -2. `bin/zig build --build-file ../build.zig test` (on POSIX) or - `bin\zig.exe build --build-file ..\build.zig test` (on Windows). +2. `bin/zig build test` (on POSIX) or `bin\zig.exe build test` (on Windows). That runs the whole test suite, which does a lot of extra testing that you 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, not the other ones. Combining this suggestion with the previous one, you could do this: -`bin/zig build --build-file ../build.zig test-std -Dskip-release` (on POSIX) or -`bin\zig.exe build --build-file ..\build.zig test-std -Dskip-release` (on Windows). +`bin/zig build test-std -Dskip-release` (on POSIX) or +`bin\zig.exe build test-std -Dskip-release` (on Windows). This will run only the standard library tests, in debug mode only, for all targets (it will cross-compile the tests for non-native targets but not run diff --git a/doc/langref.html.in b/doc/langref.html.in index b5fe464c35..ac381e00b2 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -6330,6 +6330,22 @@ comptime { TODO right now bool is not accepted. Also I think we could make non powers of 2 work fine, maybe we can remove this restriction
++ Supported operations: +
+{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}
diff --git a/std/build.zig b/std/build.zig
index 6be88ae7ec..9393d72c15 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -805,11 +805,7 @@ pub const Builder = struct {
return name;
}
const full_path = try fs.path.join(self.allocator, [_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });
- if (fs.path.real(self.allocator, full_path)) |real_path| {
- return real_path;
- } else |_| {
- continue;
- }
+ return fs.realpathAlloc(self.allocator, full_path) catch continue;
}
}
if (self.env_map.get("PATH")) |PATH| {
@@ -817,14 +813,10 @@ pub const Builder = struct {
if (fs.path.isAbsolute(name)) {
return name;
}
- var it = mem.tokenize(PATH, []u8{fs.path.delimiter});
+ var it = mem.tokenize(PATH, [_]u8{fs.path.delimiter});
while (it.next()) |path| {
const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
- if (fs.path.real(self.allocator, full_path)) |real_path| {
- return real_path;
- } else |_| {
- continue;
- }
+ return fs.realpathAlloc(self.allocator, full_path) catch continue;
}
}
}
@@ -834,11 +826,7 @@ pub const Builder = struct {
}
for (paths) |path| {
const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
- if (fs.path.real(self.allocator, full_path)) |real_path| {
- return real_path;
- } else |_| {
- continue;
- }
+ return fs.realpathAlloc(self.allocator, full_path) catch continue;
}
}
return error.FileNotFound;
@@ -904,6 +892,15 @@ pub const Builder = struct {
}
};
+test "builder.findProgram compiles" {
+ //allocator: *Allocator,
+ //zig_exe: []const u8,
+ //build_root: []const u8,
+ //cache_root: []const u8,
+ const builder = try Builder.create(std.heap.direct_allocator, "zig", "zig-cache", "zig-cache");
+ _ = builder.findProgram([_][]const u8{}, [_][]const u8{}) catch null;
+}
+
pub const Version = struct {
major: u32,
minor: u32,
@@ -1122,6 +1119,9 @@ pub const Target = union(enum) {
}
pub fn libPrefix(self: Target) []const u8 {
+ if (self.isWasm()) {
+ return "";
+ }
switch (self.getAbi()) {
.msvc => return "",
else => return "lib",
diff --git a/std/debug.zig b/std/debug.zig
index 32f96d3e15..d1c17343ef 100644
--- a/std/debug.zig
+++ b/std/debug.zig
@@ -1024,8 +1024,7 @@ pub fn openElfDebugInfo(
elf_seekable_stream: *DwarfSeekableStream,
elf_in_stream: *DwarfInStream,
) !DwarfInfo {
- var efile: elf.Elf = undefined;
- try efile.openStream(allocator, elf_seekable_stream, elf_in_stream);
+ var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream);
errdefer efile.close();
var di = DwarfInfo{
diff --git a/std/elf.zig b/std/elf.zig
index c605a177a5..37635895fd 100644
--- a/std/elf.zig
+++ b/std/elf.zig
@@ -356,7 +356,6 @@ pub const SectionHeader = struct {
pub const Elf = struct {
seekable_stream: *io.SeekableStream(anyerror, anyerror),
in_stream: *io.InStream(anyerror),
- auto_close_stream: bool,
is_64: bool,
endian: builtin.Endian,
file_type: FileType,
@@ -368,25 +367,23 @@ pub const Elf = struct {
string_section: *SectionHeader,
section_headers: []SectionHeader,
allocator: *mem.Allocator,
- prealloc_file: File,
/// Call close when done.
- pub fn openPath(elf: *Elf, allocator: *mem.Allocator, path: []const u8) !void {
+ pub fn openPath(allocator: *mem.Allocator, path: []const u8) !Elf {
@compileError("TODO implement");
}
/// Call close when done.
- pub fn openFile(elf: *Elf, allocator: *mem.Allocator, file: File) !void {
+ pub fn openFile(allocator: *mem.Allocator, file: File) !Elf {
@compileError("TODO implement");
}
pub fn openStream(
- elf: *Elf,
allocator: *mem.Allocator,
seekable_stream: *io.SeekableStream(anyerror, anyerror),
in: *io.InStream(anyerror),
- ) !void {
- elf.auto_close_stream = false;
+ ) !Elf {
+ var elf: Elf = undefined;
elf.allocator = allocator;
elf.seekable_stream = seekable_stream;
elf.in_stream = in;
@@ -523,12 +520,12 @@ pub const Elf = struct {
// not a string table
return error.InvalidFormat;
}
+
+ return elf;
}
pub fn close(elf: *Elf) void {
elf.allocator.free(elf.section_headers);
-
- if (elf.auto_close_stream) elf.prealloc_file.close();
}
pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
diff --git a/std/hash.zig b/std/hash.zig
index 148504aa39..648f34b11d 100644
--- a/std/hash.zig
+++ b/std/hash.zig
@@ -1,6 +1,9 @@
const adler = @import("hash/adler.zig");
pub const Adler32 = adler.Adler32;
+const auto_hash = @import("hash/auto_hash.zig");
+pub const autoHash = auto_hash.autoHash;
+
// pub for polynomials + generic crc32 construction
pub const crc = @import("hash/crc.zig");
pub const Crc32 = crc.Crc32;
@@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128;
pub const murmur = @import("hash/murmur.zig");
pub const Murmur2_32 = murmur.Murmur2_32;
+
+
pub const Murmur2_64 = murmur.Murmur2_64;
pub const Murmur3_32 = murmur.Murmur3_32;
@@ -23,11 +28,16 @@ pub const cityhash = @import("hash/cityhash.zig");
pub const CityHash32 = cityhash.CityHash32;
pub const CityHash64 = cityhash.CityHash64;
+const wyhash = @import("hash/wyhash.zig");
+pub const Wyhash = wyhash.Wyhash;
+
test "hash" {
_ = @import("hash/adler.zig");
+ _ = @import("hash/auto_hash.zig");
_ = @import("hash/crc.zig");
_ = @import("hash/fnv.zig");
_ = @import("hash/siphash.zig");
_ = @import("hash/murmur.zig");
_ = @import("hash/cityhash.zig");
+ _ = @import("hash/wyhash.zig");
}
diff --git a/std/hash/auto_hash.zig b/std/hash/auto_hash.zig
new file mode 100644
index 0000000000..251f7e798a
--- /dev/null
+++ b/std/hash/auto_hash.zig
@@ -0,0 +1,211 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const mem = std.mem;
+const meta = std.meta;
+
+/// Provides generic hashing for any eligible type.
+/// Only hashes `key` itself, pointers are not followed.
+pub fn autoHash(hasher: var, key: var) void {
+ const Key = @typeOf(key);
+ switch (@typeInfo(Key)) {
+ .NoReturn,
+ .Opaque,
+ .Undefined,
+ .ArgTuple,
+ .Void,
+ .Null,
+ .BoundFn,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .Type,
+ .EnumLiteral,
+ .Frame,
+ => @compileError("cannot hash this type"),
+
+ // Help the optimizer see that hashing an int is easy by inlining!
+ // TODO Check if the situation is better after #561 is resolved.
+ .Int => @inlineCall(hasher.update, std.mem.asBytes(&key)),
+
+ .Float => |info| autoHash(hasher, @bitCast(@IntType(false, info.bits), key)),
+
+ .Bool => autoHash(hasher, @boolToInt(key)),
+ .Enum => autoHash(hasher, @enumToInt(key)),
+ .ErrorSet => autoHash(hasher, @errorToInt(key)),
+ .AnyFrame, .Fn => autoHash(hasher, @ptrToInt(key)),
+
+ .Pointer => |info| switch (info.size) {
+ builtin.TypeInfo.Pointer.Size.One,
+ builtin.TypeInfo.Pointer.Size.Many,
+ builtin.TypeInfo.Pointer.Size.C,
+ => autoHash(hasher, @ptrToInt(key)),
+
+ builtin.TypeInfo.Pointer.Size.Slice => {
+ autoHash(hasher, key.ptr);
+ autoHash(hasher, key.len);
+ },
+ },
+
+ .Optional => if (key) |k| autoHash(hasher, k),
+
+ .Array => {
+ // TODO detect via a trait when Key has no padding bits to
+ // hash it as an array of bytes.
+ // Otherwise, hash every element.
+ for (key) |element| {
+ autoHash(hasher, element);
+ }
+ },
+
+ .Vector => |info| {
+ if (info.child.bit_count % 8 == 0) {
+ // If there's no unused bits in the child type, we can just hash
+ // this as an array of bytes.
+ hasher.update(mem.asBytes(&key));
+ } else {
+ // Otherwise, hash every element.
+ // TODO remove the copy to an array once field access is done.
+ const array: [info.len]info.child = key;
+ comptime var i: u32 = 0;
+ inline while (i < info.len) : (i += 1) {
+ autoHash(hasher, array[i]);
+ }
+ }
+ },
+
+ .Struct => |info| {
+ // TODO detect via a trait when Key has no padding bits to
+ // hash it as an array of bytes.
+ // Otherwise, hash every field.
+ inline for (info.fields) |field| {
+ // We reuse the hash of the previous field as the seed for the
+ // next one so that they're dependant.
+ autoHash(hasher, @field(key, field.name));
+ }
+ },
+
+ .Union => |info| blk: {
+ if (info.tag_type) |tag_type| {
+ const tag = meta.activeTag(key);
+ const s = autoHash(hasher, tag);
+ inline for (info.fields) |field| {
+ const enum_field = field.enum_field.?;
+ if (enum_field.value == @enumToInt(tag)) {
+ autoHash(hasher, @field(key, enum_field.name));
+ // TODO use a labelled break when it does not crash the compiler.
+ // break :blk;
+ return;
+ }
+ }
+ unreachable;
+ } else @compileError("cannot hash untagged union type: " ++ @typeName(Key) ++ ", provide your own hash function");
+ },
+
+ .ErrorUnion => blk: {
+ const payload = key catch |err| {
+ autoHash(hasher, err);
+ break :blk;
+ };
+ autoHash(hasher, payload);
+ },
+ }
+}
+
+const testing = std.testing;
+const Wyhash = std.hash.Wyhash;
+
+fn testAutoHash(key: var) u64 {
+ // Any hash could be used here, for testing autoHash.
+ var hasher = Wyhash.init(0);
+ autoHash(&hasher, key);
+ return hasher.final();
+}
+
+test "autoHash slice" {
+ // Allocate one array dynamically so that we're assured it is not merged
+ // with the other by the optimization passes.
+ const array1 = try std.heap.direct_allocator.create([6]u32);
+ defer std.heap.direct_allocator.destroy(array1);
+ array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 };
+ const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 };
+ const a = array1[0..];
+ const b = array2[0..];
+ const c = array1[0..3];
+ testing.expect(testAutoHash(a) == testAutoHash(a));
+ testing.expect(testAutoHash(a) != testAutoHash(array1));
+ testing.expect(testAutoHash(a) != testAutoHash(b));
+ testing.expect(testAutoHash(a) != testAutoHash(c));
+}
+
+test "testAutoHash optional" {
+ const a: ?u32 = 123;
+ const b: ?u32 = null;
+ testing.expectEqual(testAutoHash(a), testAutoHash(u32(123)));
+ testing.expect(testAutoHash(a) != testAutoHash(b));
+ testing.expectEqual(testAutoHash(b), 0);
+}
+
+test "testAutoHash array" {
+ const a = [_]u32{ 1, 2, 3 };
+ const h = testAutoHash(a);
+ var hasher = Wyhash.init(0);
+ autoHash(&hasher, u32(1));
+ autoHash(&hasher, u32(2));
+ autoHash(&hasher, u32(3));
+ testing.expectEqual(h, hasher.final());
+}
+
+test "testAutoHash struct" {
+ const Foo = struct {
+ a: u32 = 1,
+ b: u32 = 2,
+ c: u32 = 3,
+ };
+ const f = Foo{};
+ const h = testAutoHash(f);
+ var hasher = Wyhash.init(0);
+ autoHash(&hasher, u32(1));
+ autoHash(&hasher, u32(2));
+ autoHash(&hasher, u32(3));
+ testing.expectEqual(h, hasher.final());
+}
+
+test "testAutoHash union" {
+ const Foo = union(enum) {
+ A: u32,
+ B: f32,
+ C: u32,
+ };
+
+ const a = Foo{ .A = 18 };
+ var b = Foo{ .B = 12.34 };
+ const c = Foo{ .C = 18 };
+ testing.expect(testAutoHash(a) == testAutoHash(a));
+ testing.expect(testAutoHash(a) != testAutoHash(b));
+ testing.expect(testAutoHash(a) != testAutoHash(c));
+
+ b = Foo{ .A = 18 };
+ testing.expect(testAutoHash(a) == testAutoHash(b));
+}
+
+test "testAutoHash vector" {
+ const a: @Vector(4, u32) = [_]u32{ 1, 2, 3, 4 };
+ const b: @Vector(4, u32) = [_]u32{ 1, 2, 3, 5 };
+ const c: @Vector(4, u31) = [_]u31{ 1, 2, 3, 4 };
+ testing.expect(testAutoHash(a) == testAutoHash(a));
+ testing.expect(testAutoHash(a) != testAutoHash(b));
+ testing.expect(testAutoHash(a) != testAutoHash(c));
+}
+
+test "testAutoHash error union" {
+ const Errors = error{Test};
+ const Foo = struct {
+ a: u32 = 1,
+ b: u32 = 2,
+ c: u32 = 3,
+ };
+ const f = Foo{};
+ const g: Errors!Foo = Errors.Test;
+ testing.expect(testAutoHash(f) != testAutoHash(g));
+ testing.expect(testAutoHash(f) == testAutoHash(Foo{}));
+ testing.expect(testAutoHash(g) == testAutoHash(Errors.Test));
+}
diff --git a/std/hash/throughput_test.zig b/std/hash/throughput_test.zig
new file mode 100644
index 0000000000..4b7e8ef344
--- /dev/null
+++ b/std/hash/throughput_test.zig
@@ -0,0 +1,148 @@
+const builtin = @import("builtin");
+const std = @import("std");
+const time = std.time;
+const Timer = time.Timer;
+const hash = std.hash;
+
+const KiB = 1024;
+const MiB = 1024 * KiB;
+const GiB = 1024 * MiB;
+
+var prng = std.rand.DefaultPrng.init(0);
+
+const Hash = struct {
+ ty: type,
+ name: []const u8,
+ init_u8s: ?[]const u8 = null,
+ init_u64: ?u64 = null,
+};
+
+const siphash_key = "0123456789abcdef";
+
+const hashes = [_]Hash{
+ Hash{ .ty = hash.Wyhash, .name = "wyhash", .init_u64 = 0 },
+ Hash{ .ty = hash.SipHash64(1, 3), .name = "siphash(1,3)", .init_u8s = siphash_key },
+ Hash{ .ty = hash.SipHash64(2, 4), .name = "siphash(2,4)", .init_u8s = siphash_key },
+ Hash{ .ty = hash.Fnv1a_64, .name = "fnv1a" },
+ Hash{ .ty = hash.Crc32, .name = "crc32" },
+};
+
+const Result = struct {
+ hash: u64,
+ throughput: u64,
+};
+
+pub fn benchmarkHash(comptime H: var, bytes: usize) !Result {
+ var h = blk: {
+ if (H.init_u8s) |init| {
+ break :blk H.ty.init(init);
+ }
+ if (H.init_u64) |init| {
+ break :blk H.ty.init(init);
+ }
+ break :blk H.ty.init();
+ };
+
+ var block: [8192]u8 = undefined;
+ prng.random.bytes(block[0..]);
+
+ var offset: usize = 0;
+ var timer = try Timer.start();
+ const start = timer.lap();
+ while (offset < bytes) : (offset += block.len) {
+ h.update(block[0..]);
+ }
+ const end = timer.read();
+
+ const elapsed_s = @intToFloat(f64, end - start) / time.ns_per_s;
+ const throughput = @floatToInt(u64, @intToFloat(f64, bytes) / elapsed_s);
+
+ return Result{
+ .hash = h.final(),
+ .throughput = throughput,
+ };
+}
+
+fn usage() void {
+ std.debug.warn(
+ \\throughput_test [options]
+ \\
+ \\Options:
+ \\ --filter [test-name]
+ \\ --seed [int]
+ \\ --count [int]
+ \\ --help
+ \\
+ );
+}
+
+fn mode(comptime x: comptime_int) comptime_int {
+ return if (builtin.mode == builtin.Mode.Debug) x / 64 else x;
+}
+
+// TODO(#1358): Replace with builtin formatted padding when available.
+fn printPad(stdout: var, s: []const u8) !void {
+ var i: usize = 0;
+ while (i < 12 - s.len) : (i += 1) {
+ try stdout.print(" ");
+ }
+ try stdout.print("{}", s);
+}
+
+pub fn main() !void {
+ var stdout_file = try std.io.getStdOut();
+ var stdout_out_stream = stdout_file.outStream();
+ const stdout = &stdout_out_stream.stream;
+
+ var buffer: [1024]u8 = undefined;
+ var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
+ const args = try std.process.argsAlloc(&fixed.allocator);
+
+ var filter: ?[]u8 = "";
+ var count: usize = mode(128 * MiB);
+
+ var i: usize = 1;
+ while (i < args.len) : (i += 1) {
+ if (std.mem.eql(u8, args[i], "--seed")) {
+ i += 1;
+ if (i == args.len) {
+ usage();
+ std.os.exit(1);
+ }
+
+ const seed = try std.fmt.parseUnsigned(u32, args[i], 10);
+ prng.seed(seed);
+ } else if (std.mem.eql(u8, args[i], "--filter")) {
+ i += 1;
+ if (i == args.len) {
+ usage();
+ std.os.exit(1);
+ }
+
+ filter = args[i];
+ } else if (std.mem.eql(u8, args[i], "--count")) {
+ i += 1;
+ if (i == args.len) {
+ usage();
+ std.os.exit(1);
+ }
+
+ const c = try std.fmt.parseUnsigned(u32, args[i], 10);
+ count = c * MiB;
+ } else if (std.mem.eql(u8, args[i], "--help")) {
+ usage();
+ return;
+ } else {
+ usage();
+ std.os.exit(1);
+ }
+ }
+
+ inline for (hashes) |H| {
+ if (filter == null or std.mem.indexOf(u8, H.name, filter.?) != null) {
+ const result = try benchmarkHash(H, count);
+ try printPad(stdout, H.name);
+ try stdout.print(": {:4} MiB/s [{:16}]\n", result.throughput / (1 * MiB), result.hash);
+ }
+ }
+}
diff --git a/std/hash/wyhash.zig b/std/hash/wyhash.zig
new file mode 100644
index 0000000000..dfa5156cad
--- /dev/null
+++ b/std/hash/wyhash.zig
@@ -0,0 +1,135 @@
+const std = @import("std");
+const mem = std.mem;
+
+const primes = [_]u64{
+ 0xa0761d6478bd642f,
+ 0xe7037ed1a0b428db,
+ 0x8ebc6af09c88c6e3,
+ 0x589965cc75374cc3,
+ 0x1d8e4e27c47d124f,
+};
+
+fn read_bytes(comptime bytes: u8, data: []const u8) u64 {
+ return mem.readVarInt(u64, data[0..bytes], .Little);
+}
+
+fn read_8bytes_swapped(data: []const u8) u64 {
+ return (read_bytes(4, data) << 32 | read_bytes(4, data[4..]));
+}
+
+fn mum(a: u64, b: u64) u64 {
+ var r = std.math.mulWide(u64, a, b);
+ r = (r >> 64) ^ r;
+ return @truncate(u64, r);
+}
+
+fn mix0(a: u64, b: u64, seed: u64) u64 {
+ return mum(a ^ seed ^ primes[0], b ^ seed ^ primes[1]);
+}
+
+fn mix1(a: u64, b: u64, seed: u64) u64 {
+ return mum(a ^ seed ^ primes[2], b ^ seed ^ primes[3]);
+}
+
+pub const Wyhash = struct {
+ seed: u64,
+ msg_len: usize,
+
+ pub fn init(seed: u64) Wyhash {
+ return Wyhash{
+ .seed = seed,
+ .msg_len = 0,
+ };
+ }
+
+ fn round(self: *Wyhash, b: []const u8) void {
+ std.debug.assert(b.len == 32);
+
+ self.seed = mix0(
+ read_bytes(8, b[0..]),
+ read_bytes(8, b[8..]),
+ self.seed,
+ ) ^ mix1(
+ read_bytes(8, b[16..]),
+ read_bytes(8, b[24..]),
+ self.seed,
+ );
+ }
+
+ fn partial(self: *Wyhash, b: []const u8) void {
+ const rem_key = b;
+ const rem_len = b.len;
+
+ var seed = self.seed;
+ seed = switch (@intCast(u5, rem_len)) {
+ 0 => seed,
+ 1 => mix0(read_bytes(1, rem_key), primes[4], seed),
+ 2 => mix0(read_bytes(2, rem_key), primes[4], seed),
+ 3 => mix0((read_bytes(2, rem_key) << 8) | read_bytes(1, rem_key[2..]), primes[4], seed),
+ 4 => mix0(read_bytes(4, rem_key), primes[4], seed),
+ 5 => mix0((read_bytes(4, rem_key) << 8) | read_bytes(1, rem_key[4..]), primes[4], seed),
+ 6 => mix0((read_bytes(4, rem_key) << 16) | read_bytes(2, rem_key[4..]), primes[4], seed),
+ 7 => mix0((read_bytes(4, rem_key) << 24) | (read_bytes(2, rem_key[4..]) << 8) | read_bytes(1, rem_key[6..]), primes[4], seed),
+ 8 => mix0(read_8bytes_swapped(rem_key), primes[4], seed),
+ 9 => mix0(read_8bytes_swapped(rem_key), read_bytes(1, rem_key[8..]), seed),
+ 10 => mix0(read_8bytes_swapped(rem_key), read_bytes(2, rem_key[8..]), seed),
+ 11 => mix0(read_8bytes_swapped(rem_key), (read_bytes(2, rem_key[8..]) << 8) | read_bytes(1, rem_key[10..]), seed),
+ 12 => mix0(read_8bytes_swapped(rem_key), read_bytes(4, rem_key[8..]), seed),
+ 13 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 8) | read_bytes(1, rem_key[12..]), seed),
+ 14 => mix0(read_8bytes_swapped(rem_key), (read_bytes(4, rem_key[8..]) << 16) | read_bytes(2, rem_key[12..]), seed),
+ 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),
+ 16 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed),
+ 17 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(1, rem_key[16..]), primes[4], seed),
+ 18 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(2, rem_key[16..]), primes[4], seed),
+ 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),
+ 20 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_bytes(4, rem_key[16..]), primes[4], seed),
+ 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),
+ 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),
+ 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),
+ 24 => mix0(read_8bytes_swapped(rem_key), read_8bytes_swapped(rem_key[8..]), seed) ^ mix1(read_8bytes_swapped(rem_key[16..]), primes[4], seed),
+ 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),
+ 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),
+ 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),
+ 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),
+ 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),
+ 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),
+ 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),
+ };
+ self.seed = seed;
+ }
+
+ pub fn update(self: *Wyhash, b: []const u8) void {
+ var off: usize = 0;
+
+ // Full middle blocks.
+ while (off + 32 <= b.len) : (off += 32) {
+ @inlineCall(self.round, b[off .. off + 32]);
+ }
+
+ self.partial(b[off..]);
+ self.msg_len += b.len;
+ }
+
+ pub fn final(self: *Wyhash) u64 {
+ return mum(self.seed ^ self.msg_len, primes[4]);
+ }
+
+ pub fn hash(seed: u64, input: []const u8) u64 {
+ var c = Wyhash.init(seed);
+ c.update(input);
+ return c.final();
+ }
+};
+
+test "test vectors" {
+ const expectEqual = std.testing.expectEqual;
+ const hash = Wyhash.hash;
+
+ expectEqual(hash(0, ""), 0x0);
+ expectEqual(hash(1, "a"), 0xbed235177f41d328);
+ expectEqual(hash(2, "abc"), 0xbe348debe59b27c3);
+ expectEqual(hash(3, "message digest"), 0x37320f657213a290);
+ expectEqual(hash(4, "abcdefghijklmnopqrstuvwxyz"), 0xd0b270e1d8a7019c);
+ expectEqual(hash(5, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"), 0x602a1894d3bbfe7f);
+ expectEqual(hash(6, "12345678901234567890123456789012345678901234567890123456789012345678901234567890"), 0x829e9c148b75970e);
+}
diff --git a/std/hash_map.zig b/std/hash_map.zig
index 431fbb35ab..ab3c4c248d 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -4,6 +4,9 @@ const assert = debug.assert;
const testing = std.testing;
const math = std.math;
const mem = std.mem;
+const meta = std.meta;
+const autoHash = std.hash.autoHash;
+const Wyhash = std.hash.Wyhash;
const Allocator = mem.Allocator;
const builtin = @import("builtin");
@@ -448,15 +451,17 @@ test "iterator hash map" {
try reset_map.putNoClobber(2, 22);
try reset_map.putNoClobber(3, 33);
+ // TODO this test depends on the hashing algorithm, because it assumes the
+ // order of the elements in the hashmap. This should not be the case.
var keys = [_]i32{
+ 1,
3,
2,
- 1,
};
var values = [_]i32{
+ 11,
33,
22,
- 11,
};
var it = reset_map.iterator();
@@ -518,8 +523,9 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
return struct {
fn hash(key: K) u32 {
- comptime var rng = comptime std.rand.DefaultPrng.init(0);
- return autoHash(key, &rng.random, u32);
+ var hasher = Wyhash.init(0);
+ autoHash(&hasher, key);
+ return @truncate(u32, hasher.final());
}
}.hash;
}
@@ -527,116 +533,7 @@ pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
return struct {
fn eql(a: K, b: K) bool {
- return autoEql(a, b);
+ return meta.eql(a, b);
}
}.eql;
}
-
-// TODO improve these hash functions
-pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type) HashInt {
- switch (@typeInfo(@typeOf(key))) {
- .NoReturn,
- .Opaque,
- .Undefined,
- .ArgTuple,
- .Frame,
- .AnyFrame,
- => @compileError("cannot hash this type"),
-
- .Void,
- .Null,
- => return 0,
-
- .Int => |info| {
- const unsigned_x = @bitCast(@IntType(false, info.bits), key);
- if (info.bits <= HashInt.bit_count) {
- return HashInt(unsigned_x) ^ comptime rng.scalar(HashInt);
- } else {
- return @truncate(HashInt, unsigned_x ^ comptime rng.scalar(@typeOf(unsigned_x)));
- }
- },
-
- .Float => |info| {
- return autoHash(@bitCast(@IntType(false, info.bits), key), rng, HashInt);
- },
- .Bool => return autoHash(@boolToInt(key), rng, HashInt),
- .Enum => return autoHash(@enumToInt(key), rng, HashInt),
- .ErrorSet => return autoHash(@errorToInt(key), rng, HashInt),
- .Fn => return autoHash(@ptrToInt(key), rng, HashInt),
-
- .BoundFn,
- .ComptimeFloat,
- .ComptimeInt,
- .Type,
- .EnumLiteral,
- => return 0,
-
- .Pointer => |info| switch (info.size) {
- .One => @compileError("TODO auto hash for single item pointers"),
- .Many => @compileError("TODO auto hash for many item pointers"),
- .C => @compileError("TODO auto hash C pointers"),
- .Slice => {
- const interval = std.math.max(1, key.len / 256);
- var i: usize = 0;
- var h = comptime rng.scalar(HashInt);
- while (i < key.len) : (i += interval) {
- h ^= autoHash(key[i], rng, HashInt);
- }
- return h;
- },
- },
-
- .Optional => @compileError("TODO auto hash for optionals"),
- .Array => @compileError("TODO auto hash for arrays"),
- .Vector => @compileError("TODO auto hash for vectors"),
- .Struct => @compileError("TODO auto hash for structs"),
- .Union => @compileError("TODO auto hash for unions"),
- .ErrorUnion => @compileError("TODO auto hash for unions"),
- }
-}
-
-pub fn autoEql(a: var, b: @typeOf(a)) bool {
- switch (@typeInfo(@typeOf(a))) {
- .NoReturn,
- .Opaque,
- .Undefined,
- .ArgTuple,
- => @compileError("cannot test equality of this type"),
- .Void,
- .Null,
- => return true,
- .Bool,
- .Int,
- .Float,
- .ComptimeFloat,
- .ComptimeInt,
- .EnumLiteral,
- .Promise,
- .Enum,
- .BoundFn,
- .Fn,
- .ErrorSet,
- .Type,
- => return a == b,
-
- .Pointer => |info| switch (info.size) {
- .One => @compileError("TODO auto eql for single item pointers"),
- .Many => @compileError("TODO auto eql for many item pointers"),
- .C => @compileError("TODO auto eql for C pointers"),
- .Slice => {
- if (a.len != b.len) return false;
- for (a) |a_item, i| {
- if (!autoEql(a_item, b[i])) return false;
- }
- return true;
- },
- },
-
- .Optional => @compileError("TODO auto eql for optionals"),
- .Array => @compileError("TODO auto eql for arrays"),
- .Struct => @compileError("TODO auto eql for structs"),
- .Union => @compileError("TODO auto eql for unions"),
- .ErrorUnion => @compileError("TODO auto eql for unions"),
- .Vector => @compileError("TODO auto eql for vectors"),
- }
-}
diff --git a/std/http/headers.zig b/std/http/headers.zig
index 69ed494f3a..c588f2d055 100644
--- a/std/http/headers.zig
+++ b/std/http/headers.zig
@@ -102,9 +102,19 @@ test "HeaderEntry" {
testing.expectEqualSlices(u8, "x", e.value);
}
+fn stringEql(a: []const u8, b: []const u8) bool {
+ if (a.len != b.len) return false;
+ if (a.ptr == b.ptr) return true;
+ return mem.compare(u8, a, b) == .Equal;
+}
+
+fn stringHash(s: []const u8) u32 {
+ return @truncate(u32, std.hash.Wyhash.hash(0, s));
+}
+
const HeaderList = std.ArrayList(HeaderEntry);
const HeaderIndexList = std.ArrayList(usize);
-const HeaderIndex = std.AutoHashMap([]const u8, HeaderIndexList);
+const HeaderIndex = std.HashMap([]const u8, HeaderIndexList, stringHash, stringEql);
pub const Headers = struct {
// the owned header field name is stored in the index as part of the key
diff --git a/std/os.zig b/std/os.zig
index 9ff2e8f87f..c2010bf6a9 100644
--- a/std/os.zig
+++ b/std/os.zig
@@ -133,6 +133,11 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
defer close(fd);
+ const st = try fstat(fd);
+ if (!S_ISCHR(st.mode)) {
+ return error.NoDevice;
+ }
+
const stream = &std.fs.File.openHandle(fd).inStream().stream;
stream.readNoEof(buf) catch return error.Unexpected;
}
diff --git a/std/os/bits/darwin.zig b/std/os/bits/darwin.zig
index b8d229dbe9..483d4cda90 100644
--- a/std/os/bits/darwin.zig
+++ b/std/os/bits/darwin.zig
@@ -1116,3 +1116,62 @@ pub const stack_t = extern struct {
ss_size: isize,
ss_flags: i32,
};
+
+pub const S_IFMT = 0o170000;
+
+pub const S_IFIFO = 0o010000;
+pub const S_IFCHR = 0o020000;
+pub const S_IFDIR = 0o040000;
+pub const S_IFBLK = 0o060000;
+pub const S_IFREG = 0o100000;
+pub const S_IFLNK = 0o120000;
+pub const S_IFSOCK = 0o140000;
+pub const S_IFWHT = 0o160000;
+
+pub const S_ISUID = 0o4000;
+pub const S_ISGID = 0o2000;
+pub const S_ISVTX = 0o1000;
+pub const S_IRWXU = 0o700;
+pub const S_IRUSR = 0o400;
+pub const S_IWUSR = 0o200;
+pub const S_IXUSR = 0o100;
+pub const S_IRWXG = 0o070;
+pub const S_IRGRP = 0o040;
+pub const S_IWGRP = 0o020;
+pub const S_IXGRP = 0o010;
+pub const S_IRWXO = 0o007;
+pub const S_IROTH = 0o004;
+pub const S_IWOTH = 0o002;
+pub const S_IXOTH = 0o001;
+
+pub fn S_ISFIFO(m: u32) bool {
+ return m & S_IFMT == S_IFIFO;
+}
+
+pub fn S_ISCHR(m: u32) bool {
+ return m & S_IFMT == S_IFCHR;
+}
+
+pub fn S_ISDIR(m: u32) bool {
+ return m & S_IFMT == S_IFDIR;
+}
+
+pub fn S_ISBLK(m: u32) bool {
+ return m & S_IFMT == S_IFBLK;
+}
+
+pub fn S_ISREG(m: u32) bool {
+ return m & S_IFMT == S_IFREG;
+}
+
+pub fn S_ISLNK(m: u32) bool {
+ return m & S_IFMT == S_IFLNK;
+}
+
+pub fn S_ISSOCK(m: u32) bool {
+ return m & S_IFMT == S_IFSOCK;
+}
+
+pub fn S_IWHT(m: u32) bool {
+ return m & S_IFMT == S_IFWHT;
+}
diff --git a/std/os/bits/freebsd.zig b/std/os/bits/freebsd.zig
index 198857983e..45432a6c07 100644
--- a/std/os/bits/freebsd.zig
+++ b/std/os/bits/freebsd.zig
@@ -876,3 +876,62 @@ pub const stack_t = extern struct {
ss_size: isize,
ss_flags: i32,
};
+
+pub const S_IFMT = 0o170000;
+
+pub const S_IFIFO = 0o010000;
+pub const S_IFCHR = 0o020000;
+pub const S_IFDIR = 0o040000;
+pub const S_IFBLK = 0o060000;
+pub const S_IFREG = 0o100000;
+pub const S_IFLNK = 0o120000;
+pub const S_IFSOCK = 0o140000;
+pub const S_IFWHT = 0o160000;
+
+pub const S_ISUID = 0o4000;
+pub const S_ISGID = 0o2000;
+pub const S_ISVTX = 0o1000;
+pub const S_IRWXU = 0o700;
+pub const S_IRUSR = 0o400;
+pub const S_IWUSR = 0o200;
+pub const S_IXUSR = 0o100;
+pub const S_IRWXG = 0o070;
+pub const S_IRGRP = 0o040;
+pub const S_IWGRP = 0o020;
+pub const S_IXGRP = 0o010;
+pub const S_IRWXO = 0o007;
+pub const S_IROTH = 0o004;
+pub const S_IWOTH = 0o002;
+pub const S_IXOTH = 0o001;
+
+pub fn S_ISFIFO(m: u32) bool {
+ return m & S_IFMT == S_IFIFO;
+}
+
+pub fn S_ISCHR(m: u32) bool {
+ return m & S_IFMT == S_IFCHR;
+}
+
+pub fn S_ISDIR(m: u32) bool {
+ return m & S_IFMT == S_IFDIR;
+}
+
+pub fn S_ISBLK(m: u32) bool {
+ return m & S_IFMT == S_IFBLK;
+}
+
+pub fn S_ISREG(m: u32) bool {
+ return m & S_IFMT == S_IFREG;
+}
+
+pub fn S_ISLNK(m: u32) bool {
+ return m & S_IFMT == S_IFLNK;
+}
+
+pub fn S_ISSOCK(m: u32) bool {
+ return m & S_IFMT == S_IFSOCK;
+}
+
+pub fn S_IWHT(m: u32) bool {
+ return m & S_IFMT == S_IFWHT;
+}
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index 67ce9a06cf..c2b6801e22 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -5,3 +5,4 @@ pub const is_the_target = switch (builtin.os) {
else => false,
};
pub usingnamespace std.c;
+pub usingnamespace @import("bits.zig");
\ No newline at end of file
diff --git a/std/os/freebsd.zig b/std/os/freebsd.zig
index d418ccd415..e9efe64920 100644
--- a/std/os/freebsd.zig
+++ b/std/os/freebsd.zig
@@ -2,3 +2,4 @@ const std = @import("../std.zig");
const builtin = @import("builtin");
pub const is_the_target = builtin.os == .freebsd;
pub usingnamespace std.c;
+pub usingnamespace @import("bits.zig");
\ No newline at end of file
diff --git a/std/rb.zig b/std/rb.zig
index b5935a2eac..0b84950544 100644
--- a/std/rb.zig
+++ b/std/rb.zig
@@ -234,10 +234,13 @@ pub const Tree = struct {
return null;
}
+ /// lookup searches for the value of key, using binary search. It will
+ /// return a pointer to the node if it is there, otherwise it will return null.
+ /// Complexity guaranteed O(log n), where n is the number of nodes book-kept
+ /// by tree.
pub fn lookup(tree: *Tree, key: *Node) ?*Node {
- var parent: *Node = undefined;
+ var parent: ?*Node = undefined;
var is_left: bool = undefined;
-
return doLookup(key, tree, &parent, &is_left);
}
@@ -545,3 +548,47 @@ test "rb" {
num = testGetNumber(num.node.next().?);
}
}
+
+
+test "inserting and looking up" {
+ var tree: Tree = undefined;
+ tree.init(testCompare);
+ var number: testNumber = undefined;
+ number.value = 1000;
+ _ = tree.insert(&number.node);
+ var dup: testNumber = undefined;
+ //Assert that tuples with identical value fields finds the same pointer
+ dup.value = 1000;
+ assert(tree.lookup(&dup.node) == &number.node);
+ //Assert that tuples with identical values do not clobber when inserted.
+ _ = tree.insert(&dup.node);
+ assert(tree.lookup(&dup.node) == &number.node);
+ assert(tree.lookup(&number.node) != &dup.node);
+ assert(testGetNumber(tree.lookup(&dup.node).?).value == testGetNumber(&dup.node).value);
+ //Assert that if looking for a non-existing value, return null.
+ var non_existing_value: testNumber = undefined;
+ non_existing_value.value = 1234;
+ assert(tree.lookup(&non_existing_value.node) == null);
+}
+
+test "multiple inserts, followed by calling first and last" {
+ var tree: Tree = undefined;
+ tree.init(testCompare);
+ var zeroth: testNumber = undefined;
+ zeroth.value = 0;
+ var first: testNumber = undefined;
+ first.value = 1;
+ var second: testNumber = undefined;
+ second.value = 2;
+ var third: testNumber = undefined;
+ third.value = 3;
+ _ = tree.insert(&zeroth.node);
+ _ = tree.insert(&first.node);
+ _ = tree.insert(&second.node);
+ _ = tree.insert(&third.node);
+ assert(testGetNumber(tree.first().?).value == 0);
+ assert(testGetNumber(tree.last().?).value == 3);
+ var lookupNode: testNumber = undefined;
+ lookupNode.value = 3;
+ assert(tree.lookup(&lookupNode.node) == &third.node);
+}