mirror of
https://github.com/ziglang/zig.git
synced 2025-12-16 03:03:09 +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)
|
||||
* [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
|
||||
|
||||
@ -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
|
||||
</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_open|@bitCast#}
|
||||
<pre>{#syntax#}@bitCast(comptime DestType: type, value: var) DestType{#endsyntax#}</pre>
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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{
|
||||
|
||||
15
std/elf.zig
15
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 {
|
||||
|
||||
10
std/hash.zig
10
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");
|
||||
}
|
||||
|
||||
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 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"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -5,3 +5,4 @@ pub const is_the_target = switch (builtin.os) {
|
||||
else => false,
|
||||
};
|
||||
pub usingnamespace std.c;
|
||||
pub usingnamespace @import("bits.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");
|
||||
51
std/rb.zig
51
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);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user