mirror of
https://github.com/ziglang/zig.git
synced 2025-12-18 04:03:14 +00:00
Merge remote-tracking branch 'origin/master' into rewrite-coroutines
This commit is contained in:
commit
b7c94be688
@ -25,6 +25,7 @@ Here are some examples:
|
|||||||
|
|
||||||
* [Iterative Replacement of C with Zig](http://tiehuis.github.io/blog/zig1.html)
|
* [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)
|
* [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
|
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,
|
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
|
The issue label
|
||||||
[Contributor Friendly](https://github.com/ziglang/zig/issues?q=is%3Aissue+is%3Aopen+label%3A%22contributor+friendly%22)
|
[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
|
exists to help you find issues that are **limited in scope and/or
|
||||||
knowledge of Zig internals."
|
knowledge of Zig internals.**
|
||||||
|
|
||||||
### Editing Source Code
|
### Editing Source Code
|
||||||
|
|
||||||
@ -61,8 +62,7 @@ To test changes, do the following from the build directory:
|
|||||||
|
|
||||||
1. Run `make install` (on POSIX) or
|
1. Run `make install` (on POSIX) or
|
||||||
`msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows).
|
`msbuild -p:Configuration=Release INSTALL.vcxproj` (on Windows).
|
||||||
2. `bin/zig build --build-file ../build.zig test` (on POSIX) or
|
2. `bin/zig build test` (on POSIX) or `bin\zig.exe build test` (on Windows).
|
||||||
`bin\zig.exe build --build-file ..\build.zig test` (on Windows).
|
|
||||||
|
|
||||||
That runs the whole test suite, which does a lot of extra testing that you
|
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
|
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
|
not the other ones. Combining this suggestion with the previous one, you could
|
||||||
do this:
|
do this:
|
||||||
|
|
||||||
`bin/zig build --build-file ../build.zig test-std -Dskip-release` (on POSIX) or
|
`bin/zig build test-std -Dskip-release` (on POSIX) or
|
||||||
`bin\zig.exe build --build-file ..\build.zig test-std -Dskip-release` (on Windows).
|
`bin\zig.exe build test-std -Dskip-release` (on Windows).
|
||||||
|
|
||||||
This will run only the standard library tests, in debug mode only, for all
|
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
|
targets (it will cross-compile the tests for non-native targets but not run
|
||||||
|
|||||||
@ -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
|
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
|
we can remove this restriction
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
Supported operations:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>{#syntax#}.Xchg{#endsyntax#} - stores the operand unmodified.</li>
|
||||||
|
<li>{#syntax#}.Add{#endsyntax#} - for integers, twos complement wraparound addition.
|
||||||
|
Also supports {#link|Floats#}.</li>
|
||||||
|
<li>{#syntax#}.Sub{#endsyntax#} - for integers, twos complement wraparound subtraction.
|
||||||
|
Also supports {#link|Floats#}.</li>
|
||||||
|
<li>{#syntax#}.And{#endsyntax#} - bitwise and</li>
|
||||||
|
<li>{#syntax#}.Nand{#endsyntax#} - bitwise nand</li>
|
||||||
|
<li>{#syntax#}.Or{#endsyntax#} - bitwise or</li>
|
||||||
|
<li>{#syntax#}.Xor{#endsyntax#} - bitwise xor</li>
|
||||||
|
<li>{#syntax#}.Max{#endsyntax#} - stores the operand if it is larger. Supports integers and floats.</li>
|
||||||
|
<li>{#syntax#}.Min{#endsyntax#} - stores the operand if it is smaller. Supports integers and floats.</li>
|
||||||
|
</ul>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|@bitCast#}
|
{#header_open|@bitCast#}
|
||||||
<pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
|
<pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
|
||||||
|
|||||||
@ -805,11 +805,7 @@ pub const Builder = struct {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
const full_path = try fs.path.join(self.allocator, [_][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });
|
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 fs.realpathAlloc(self.allocator, full_path) catch continue;
|
||||||
return real_path;
|
|
||||||
} else |_| {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self.env_map.get("PATH")) |PATH| {
|
if (self.env_map.get("PATH")) |PATH| {
|
||||||
@ -817,14 +813,10 @@ pub const Builder = struct {
|
|||||||
if (fs.path.isAbsolute(name)) {
|
if (fs.path.isAbsolute(name)) {
|
||||||
return 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| {
|
while (it.next()) |path| {
|
||||||
const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
|
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 fs.realpathAlloc(self.allocator, full_path) catch continue;
|
||||||
return real_path;
|
|
||||||
} else |_| {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -834,11 +826,7 @@ pub const Builder = struct {
|
|||||||
}
|
}
|
||||||
for (paths) |path| {
|
for (paths) |path| {
|
||||||
const full_path = try fs.path.join(self.allocator, [_][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
|
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 fs.realpathAlloc(self.allocator, full_path) catch continue;
|
||||||
return real_path;
|
|
||||||
} else |_| {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return error.FileNotFound;
|
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 {
|
pub const Version = struct {
|
||||||
major: u32,
|
major: u32,
|
||||||
minor: u32,
|
minor: u32,
|
||||||
@ -1122,6 +1119,9 @@ pub const Target = union(enum) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn libPrefix(self: Target) []const u8 {
|
pub fn libPrefix(self: Target) []const u8 {
|
||||||
|
if (self.isWasm()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
switch (self.getAbi()) {
|
switch (self.getAbi()) {
|
||||||
.msvc => return "",
|
.msvc => return "",
|
||||||
else => return "lib",
|
else => return "lib",
|
||||||
|
|||||||
@ -1024,8 +1024,7 @@ pub fn openElfDebugInfo(
|
|||||||
elf_seekable_stream: *DwarfSeekableStream,
|
elf_seekable_stream: *DwarfSeekableStream,
|
||||||
elf_in_stream: *DwarfInStream,
|
elf_in_stream: *DwarfInStream,
|
||||||
) !DwarfInfo {
|
) !DwarfInfo {
|
||||||
var efile: elf.Elf = undefined;
|
var efile = try elf.Elf.openStream(allocator, elf_seekable_stream, elf_in_stream);
|
||||||
try efile.openStream(allocator, elf_seekable_stream, elf_in_stream);
|
|
||||||
errdefer efile.close();
|
errdefer efile.close();
|
||||||
|
|
||||||
var di = DwarfInfo{
|
var di = DwarfInfo{
|
||||||
|
|||||||
15
std/elf.zig
15
std/elf.zig
@ -356,7 +356,6 @@ pub const SectionHeader = struct {
|
|||||||
pub const Elf = struct {
|
pub const Elf = struct {
|
||||||
seekable_stream: *io.SeekableStream(anyerror, anyerror),
|
seekable_stream: *io.SeekableStream(anyerror, anyerror),
|
||||||
in_stream: *io.InStream(anyerror),
|
in_stream: *io.InStream(anyerror),
|
||||||
auto_close_stream: bool,
|
|
||||||
is_64: bool,
|
is_64: bool,
|
||||||
endian: builtin.Endian,
|
endian: builtin.Endian,
|
||||||
file_type: FileType,
|
file_type: FileType,
|
||||||
@ -368,25 +367,23 @@ pub const Elf = struct {
|
|||||||
string_section: *SectionHeader,
|
string_section: *SectionHeader,
|
||||||
section_headers: []SectionHeader,
|
section_headers: []SectionHeader,
|
||||||
allocator: *mem.Allocator,
|
allocator: *mem.Allocator,
|
||||||
prealloc_file: File,
|
|
||||||
|
|
||||||
/// Call close when done.
|
/// 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");
|
@compileError("TODO implement");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call close when done.
|
/// 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");
|
@compileError("TODO implement");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn openStream(
|
pub fn openStream(
|
||||||
elf: *Elf,
|
|
||||||
allocator: *mem.Allocator,
|
allocator: *mem.Allocator,
|
||||||
seekable_stream: *io.SeekableStream(anyerror, anyerror),
|
seekable_stream: *io.SeekableStream(anyerror, anyerror),
|
||||||
in: *io.InStream(anyerror),
|
in: *io.InStream(anyerror),
|
||||||
) !void {
|
) !Elf {
|
||||||
elf.auto_close_stream = false;
|
var elf: Elf = undefined;
|
||||||
elf.allocator = allocator;
|
elf.allocator = allocator;
|
||||||
elf.seekable_stream = seekable_stream;
|
elf.seekable_stream = seekable_stream;
|
||||||
elf.in_stream = in;
|
elf.in_stream = in;
|
||||||
@ -523,12 +520,12 @@ pub const Elf = struct {
|
|||||||
// not a string table
|
// not a string table
|
||||||
return error.InvalidFormat;
|
return error.InvalidFormat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return elf;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn close(elf: *Elf) void {
|
pub fn close(elf: *Elf) void {
|
||||||
elf.allocator.free(elf.section_headers);
|
elf.allocator.free(elf.section_headers);
|
||||||
|
|
||||||
if (elf.auto_close_stream) elf.prealloc_file.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
|
pub fn findSection(elf: *Elf, name: []const u8) !?*SectionHeader {
|
||||||
|
|||||||
10
std/hash.zig
10
std/hash.zig
@ -1,6 +1,9 @@
|
|||||||
const adler = @import("hash/adler.zig");
|
const adler = @import("hash/adler.zig");
|
||||||
pub const Adler32 = adler.Adler32;
|
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 for polynomials + generic crc32 construction
|
||||||
pub const crc = @import("hash/crc.zig");
|
pub const crc = @import("hash/crc.zig");
|
||||||
pub const Crc32 = crc.Crc32;
|
pub const Crc32 = crc.Crc32;
|
||||||
@ -16,6 +19,8 @@ pub const SipHash128 = siphash.SipHash128;
|
|||||||
|
|
||||||
pub const murmur = @import("hash/murmur.zig");
|
pub const murmur = @import("hash/murmur.zig");
|
||||||
pub const Murmur2_32 = murmur.Murmur2_32;
|
pub const Murmur2_32 = murmur.Murmur2_32;
|
||||||
|
|
||||||
|
|
||||||
pub const Murmur2_64 = murmur.Murmur2_64;
|
pub const Murmur2_64 = murmur.Murmur2_64;
|
||||||
pub const Murmur3_32 = murmur.Murmur3_32;
|
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 CityHash32 = cityhash.CityHash32;
|
||||||
pub const CityHash64 = cityhash.CityHash64;
|
pub const CityHash64 = cityhash.CityHash64;
|
||||||
|
|
||||||
|
const wyhash = @import("hash/wyhash.zig");
|
||||||
|
pub const Wyhash = wyhash.Wyhash;
|
||||||
|
|
||||||
test "hash" {
|
test "hash" {
|
||||||
_ = @import("hash/adler.zig");
|
_ = @import("hash/adler.zig");
|
||||||
|
_ = @import("hash/auto_hash.zig");
|
||||||
_ = @import("hash/crc.zig");
|
_ = @import("hash/crc.zig");
|
||||||
_ = @import("hash/fnv.zig");
|
_ = @import("hash/fnv.zig");
|
||||||
_ = @import("hash/siphash.zig");
|
_ = @import("hash/siphash.zig");
|
||||||
_ = @import("hash/murmur.zig");
|
_ = @import("hash/murmur.zig");
|
||||||
_ = @import("hash/cityhash.zig");
|
_ = @import("hash/cityhash.zig");
|
||||||
|
_ = @import("hash/wyhash.zig");
|
||||||
}
|
}
|
||||||
|
|||||||
211
std/hash/auto_hash.zig
Normal file
211
std/hash/auto_hash.zig
Normal file
@ -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));
|
||||||
|
}
|
||||||
148
std/hash/throughput_test.zig
Normal file
148
std/hash/throughput_test.zig
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
135
std/hash/wyhash.zig
Normal file
135
std/hash/wyhash.zig
Normal file
@ -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);
|
||||||
|
}
|
||||||
125
std/hash_map.zig
125
std/hash_map.zig
@ -4,6 +4,9 @@ const assert = debug.assert;
|
|||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const math = std.math;
|
const math = std.math;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
|
const meta = std.meta;
|
||||||
|
const autoHash = std.hash.autoHash;
|
||||||
|
const Wyhash = std.hash.Wyhash;
|
||||||
const Allocator = mem.Allocator;
|
const Allocator = mem.Allocator;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
@ -448,15 +451,17 @@ test "iterator hash map" {
|
|||||||
try reset_map.putNoClobber(2, 22);
|
try reset_map.putNoClobber(2, 22);
|
||||||
try reset_map.putNoClobber(3, 33);
|
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{
|
var keys = [_]i32{
|
||||||
|
1,
|
||||||
3,
|
3,
|
||||||
2,
|
2,
|
||||||
1,
|
|
||||||
};
|
};
|
||||||
var values = [_]i32{
|
var values = [_]i32{
|
||||||
|
11,
|
||||||
33,
|
33,
|
||||||
22,
|
22,
|
||||||
11,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var it = reset_map.iterator();
|
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) {
|
pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
|
||||||
return struct {
|
return struct {
|
||||||
fn hash(key: K) u32 {
|
fn hash(key: K) u32 {
|
||||||
comptime var rng = comptime std.rand.DefaultPrng.init(0);
|
var hasher = Wyhash.init(0);
|
||||||
return autoHash(key, &rng.random, u32);
|
autoHash(&hasher, key);
|
||||||
|
return @truncate(u32, hasher.final());
|
||||||
}
|
}
|
||||||
}.hash;
|
}.hash;
|
||||||
}
|
}
|
||||||
@ -527,116 +533,7 @@ pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
|
|||||||
pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
|
pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) {
|
||||||
return struct {
|
return struct {
|
||||||
fn eql(a: K, b: K) bool {
|
fn eql(a: K, b: K) bool {
|
||||||
return autoEql(a, b);
|
return meta.eql(a, b);
|
||||||
}
|
}
|
||||||
}.eql;
|
}.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"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -102,9 +102,19 @@ test "HeaderEntry" {
|
|||||||
testing.expectEqualSlices(u8, "x", e.value);
|
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 HeaderList = std.ArrayList(HeaderEntry);
|
||||||
const HeaderIndexList = std.ArrayList(usize);
|
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 {
|
pub const Headers = struct {
|
||||||
// the owned header field name is stored in the index as part of the key
|
// the owned header field name is stored in the index as part of the key
|
||||||
|
|||||||
@ -133,6 +133,11 @@ fn getRandomBytesDevURandom(buf: []u8) !void {
|
|||||||
const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
|
const fd = try openC(c"/dev/urandom", O_RDONLY | O_CLOEXEC, 0);
|
||||||
defer close(fd);
|
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;
|
const stream = &std.fs.File.openHandle(fd).inStream().stream;
|
||||||
stream.readNoEof(buf) catch return error.Unexpected;
|
stream.readNoEof(buf) catch return error.Unexpected;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1116,3 +1116,62 @@ pub const stack_t = extern struct {
|
|||||||
ss_size: isize,
|
ss_size: isize,
|
||||||
ss_flags: i32,
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -876,3 +876,62 @@ pub const stack_t = extern struct {
|
|||||||
ss_size: isize,
|
ss_size: isize,
|
||||||
ss_flags: i32,
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -5,3 +5,4 @@ pub const is_the_target = switch (builtin.os) {
|
|||||||
else => false,
|
else => false,
|
||||||
};
|
};
|
||||||
pub usingnamespace std.c;
|
pub usingnamespace std.c;
|
||||||
|
pub usingnamespace @import("bits.zig");
|
||||||
@ -2,3 +2,4 @@ const std = @import("../std.zig");
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
pub const is_the_target = builtin.os == .freebsd;
|
pub const is_the_target = builtin.os == .freebsd;
|
||||||
pub usingnamespace std.c;
|
pub usingnamespace std.c;
|
||||||
|
pub usingnamespace @import("bits.zig");
|
||||||
51
std/rb.zig
51
std/rb.zig
@ -234,10 +234,13 @@ pub const Tree = struct {
|
|||||||
return null;
|
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 {
|
pub fn lookup(tree: *Tree, key: *Node) ?*Node {
|
||||||
var parent: *Node = undefined;
|
var parent: ?*Node = undefined;
|
||||||
var is_left: bool = undefined;
|
var is_left: bool = undefined;
|
||||||
|
|
||||||
return doLookup(key, tree, &parent, &is_left);
|
return doLookup(key, tree, &parent, &is_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -545,3 +548,47 @@ test "rb" {
|
|||||||
num = testGetNumber(num.node.next().?);
|
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);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user