aboutsummaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-06-19 19:01:28 -0400
committerAndrew Kelley <andrew@ziglang.org>2019-06-19 19:01:28 -0400
commit04c25efe112e374facaf1bc8b58bbdb6999a39e3 (patch)
tree66a70b185ad8e44a69c8be27dd7dbc23425b040c /std
parent4ffab5b85f03f63a7e724698482f8497cacc7212 (diff)
parent381c6a38b145665a22440f7aa816f0ddd9b70ee5 (diff)
downloadzig-04c25efe112e374facaf1bc8b58bbdb6999a39e3.tar.gz
zig-04c25efe112e374facaf1bc8b58bbdb6999a39e3.zip
Merge remote-tracking branch 'origin/master' into copy-elision-3
Diffstat (limited to 'std')
-rw-r--r--std/atomic/queue.zig21
-rw-r--r--std/child_process.zig37
-rw-r--r--std/crypto.zig3
-rw-r--r--std/crypto/gimli.zig168
-rw-r--r--std/fmt.zig451
-rw-r--r--std/hash_map.zig10
-rw-r--r--std/mem.zig37
-rw-r--r--std/os/windows.zig2
8 files changed, 511 insertions, 218 deletions
diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig
index e8d03c4f13..73db2996bd 100644
--- a/std/atomic/queue.zig
+++ b/std/atomic/queue.zig
@@ -100,7 +100,7 @@ pub fn Queue(comptime T: type) type {
pub fn isEmpty(self: *Self) bool {
const held = self.mutex.acquire();
defer held.release();
- return self.head != null;
+ return self.head == null;
}
pub fn dump(self: *Self) void {
@@ -172,12 +172,14 @@ test "std.atomic.Queue" {
};
if (builtin.single_threaded) {
+ expect(context.queue.isEmpty());
{
var i: usize = 0;
while (i < put_thread_count) : (i += 1) {
expect(startPuts(&context) == 0);
}
}
+ expect(!context.queue.isEmpty());
context.puts_done = 1;
{
var i: usize = 0;
@@ -185,7 +187,10 @@ test "std.atomic.Queue" {
expect(startGets(&context) == 0);
}
}
+ expect(context.queue.isEmpty());
} else {
+ expect(context.queue.isEmpty());
+
var putters: [put_thread_count]*std.Thread = undefined;
for (putters) |*t| {
t.* = try std.Thread.spawn(&context, startPuts);
@@ -200,6 +205,8 @@ test "std.atomic.Queue" {
_ = @atomicRmw(u8, &context.puts_done, builtin.AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
for (getters) |t|
t.wait();
+
+ expect(context.queue.isEmpty());
}
if (context.put_sum != context.get_sum) {
@@ -250,6 +257,7 @@ fn startGets(ctx: *Context) u8 {
test "std.atomic.Queue single-threaded" {
var queue = Queue(i32).init();
+ expect(queue.isEmpty());
var node_0 = Queue(i32).Node{
.data = 0,
@@ -257,6 +265,7 @@ test "std.atomic.Queue single-threaded" {
.prev = undefined,
};
queue.put(&node_0);
+ expect(!queue.isEmpty());
var node_1 = Queue(i32).Node{
.data = 1,
@@ -264,8 +273,10 @@ test "std.atomic.Queue single-threaded" {
.prev = undefined,
};
queue.put(&node_1);
+ expect(!queue.isEmpty());
expect(queue.get().?.data == 0);
+ expect(!queue.isEmpty());
var node_2 = Queue(i32).Node{
.data = 2,
@@ -273,6 +284,7 @@ test "std.atomic.Queue single-threaded" {
.prev = undefined,
};
queue.put(&node_2);
+ expect(!queue.isEmpty());
var node_3 = Queue(i32).Node{
.data = 3,
@@ -280,10 +292,13 @@ test "std.atomic.Queue single-threaded" {
.prev = undefined,
};
queue.put(&node_3);
+ expect(!queue.isEmpty());
expect(queue.get().?.data == 1);
+ expect(!queue.isEmpty());
expect(queue.get().?.data == 2);
+ expect(!queue.isEmpty());
var node_4 = Queue(i32).Node{
.data = 4,
@@ -291,13 +306,17 @@ test "std.atomic.Queue single-threaded" {
.prev = undefined,
};
queue.put(&node_4);
+ expect(!queue.isEmpty());
expect(queue.get().?.data == 3);
node_3.next = null;
+ expect(!queue.isEmpty());
expect(queue.get().?.data == 4);
+ expect(queue.isEmpty());
expect(queue.get() == null);
+ expect(queue.isEmpty());
}
test "std.atomic.Queue dump" {
diff --git a/std/child_process.zig b/std/child_process.zig
index 8e4c086d1d..6e6cca335a 100644
--- a/std/child_process.zig
+++ b/std/child_process.zig
@@ -543,25 +543,32 @@ pub const ChildProcess = struct {
const PATH = try process.getEnvVarOwned(self.allocator, "PATH");
defer self.allocator.free(PATH);
+ const PATHEXT = try process.getEnvVarOwned(self.allocator, "PATHEXT");
+ defer self.allocator.free(PATHEXT);
var it = mem.tokenize(PATH, ";");
- while (it.next()) |search_path| {
- const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_name });
- defer self.allocator.free(joined_path);
-
- const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
- defer self.allocator.free(joined_path_w);
-
- if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
- break;
- } else |err| if (err == error.FileNotFound) {
- continue;
- } else {
- return err;
+ retry: while (it.next()) |search_path| {
+ var ext_it = mem.tokenize(PATHEXT, ";");
+ while (ext_it.next()) |app_ext| {
+ const app_basename = try mem.concat(self.allocator, u8, [_][]const u8{app_name[0..app_name.len - 1], app_ext});
+ defer self.allocator.free(app_basename);
+
+ const joined_path = try fs.path.join(self.allocator, [_][]const u8{ search_path, app_basename });
+ defer self.allocator.free(joined_path);
+
+ const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
+ defer self.allocator.free(joined_path_w);
+
+ if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
+ break :retry;
+ } else |err| switch (err) {
+ error.FileNotFound => { continue; },
+ error.AccessDenied => { continue; },
+ else => { return err; },
+ }
}
} else {
- // Every other error would have been returned earlier.
- return error.FileNotFound;
+ return no_path_err; // return the original error
}
};
diff --git a/std/crypto.zig b/std/crypto.zig
index 2b57de9e60..b7703f2f7a 100644
--- a/std/crypto.zig
+++ b/std/crypto.zig
@@ -13,6 +13,8 @@ pub const Sha3_256 = sha3.Sha3_256;
pub const Sha3_384 = sha3.Sha3_384;
pub const Sha3_512 = sha3.Sha3_512;
+pub const gimli = @import("crypto/gimli.zig");
+
const blake2 = @import("crypto/blake2.zig");
pub const Blake2s224 = blake2.Blake2s224;
pub const Blake2s256 = blake2.Blake2s256;
@@ -38,6 +40,7 @@ pub const randomBytes = std.os.getrandom;
test "crypto" {
_ = @import("crypto/blake2.zig");
_ = @import("crypto/chacha20.zig");
+ _ = @import("crypto/gimli.zig");
_ = @import("crypto/hmac.zig");
_ = @import("crypto/md5.zig");
_ = @import("crypto/poly1305.zig");
diff --git a/std/crypto/gimli.zig b/std/crypto/gimli.zig
new file mode 100644
index 0000000000..0a0a5056c6
--- /dev/null
+++ b/std/crypto/gimli.zig
@@ -0,0 +1,168 @@
+// Gimli is a 384-bit permutation designed to achieve high security with high
+// performance across a broad range of platforms, including 64-bit Intel/AMD
+// server CPUs, 64-bit and 32-bit ARM smartphone CPUs, 32-bit ARM
+// microcontrollers, 8-bit AVR microcontrollers, FPGAs, ASICs without
+// side-channel protection, and ASICs with side-channel protection.
+//
+// https://gimli.cr.yp.to/
+// https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/gimli-spec.pdf
+
+const std = @import("../std.zig");
+const mem = std.mem;
+const math = std.math;
+const debug = std.debug;
+const assert = std.debug.assert;
+const testing = std.testing;
+const htest = @import("test.zig");
+
+pub const State = struct {
+ pub const BLOCKBYTES = 48;
+ pub const RATE = 16;
+
+ // TODO: https://github.com/ziglang/zig/issues/2673#issuecomment-501763017
+ data: [BLOCKBYTES / 4]u32,
+
+ const Self = @This();
+
+ pub fn toSlice(self: *Self) []u8 {
+ return @sliceToBytes(self.data[0..]);
+ }
+
+ pub fn toSliceConst(self: *Self) []const u8 {
+ return @sliceToBytes(self.data[0..]);
+ }
+
+ pub fn permute(self: *Self) void {
+ const state = &self.data;
+ var round = u32(24);
+ while (round > 0) : (round -= 1) {
+ var column = usize(0);
+ while (column < 4) : (column += 1) {
+ const x = math.rotl(u32, state[column], 24);
+ const y = math.rotl(u32, state[4 + column], 9);
+ const z = state[8 + column];
+ state[8 + column] = ((x ^ (z << 1)) ^ ((y & z) << 2));
+ state[4 + column] = ((y ^ x) ^ ((x | z) << 1));
+ state[column] = ((z ^ y) ^ ((x & y) << 3));
+ }
+ switch (round & 3) {
+ 0 => {
+ mem.swap(u32, &state[0], &state[1]);
+ mem.swap(u32, &state[2], &state[3]);
+ state[0] ^= round | 0x9e377900;
+ },
+ 2 => {
+ mem.swap(u32, &state[0], &state[2]);
+ mem.swap(u32, &state[1], &state[3]);
+ },
+ else => {},
+ }
+ }
+ }
+
+ pub fn squeeze(self: *Self, out: []u8) void {
+ var i = usize(0);
+ while (i + RATE <= out.len) : (i += RATE) {
+ self.permute();
+ mem.copy(u8, out[i..], self.toSliceConst()[0..RATE]);
+ }
+ const leftover = out.len - i;
+ if (leftover != 0) {
+ self.permute();
+ mem.copy(u8, out[i..], self.toSliceConst()[0..leftover]);
+ }
+ }
+};
+
+test "permute" {
+ // test vector from gimli-20170627
+ var state = State{
+ .data = blk: {
+ var input: [12]u32 = undefined;
+ var i = u32(0);
+ while (i < 12) : (i += 1) {
+ input[i] = i * i * i + i *% 0x9e3779b9;
+ }
+ testing.expectEqualSlices(u32, input, [_]u32{
+ 0x00000000, 0x9e3779ba, 0x3c6ef37a, 0xdaa66d46,
+ 0x78dde724, 0x1715611a, 0xb54cdb2e, 0x53845566,
+ 0xf1bbcfc8, 0x8ff34a5a, 0x2e2ac522, 0xcc624026,
+ });
+ break :blk input;
+ },
+ };
+ state.permute();
+ testing.expectEqualSlices(u32, state.data, [_]u32{
+ 0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68,
+ 0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8,
+ 0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6,
+ });
+}
+
+pub const Hash = struct {
+ state: State,
+ buf_off: usize,
+
+ const Self = @This();
+
+ pub fn init() Self {
+ return Self{
+ .state = State{
+ .data = [_]u32{0} ** (State.BLOCKBYTES / 4),
+ },
+ .buf_off = 0,
+ };
+ }
+
+ /// Also known as 'absorb'
+ pub fn update(self: *Self, data: []const u8) void {
+ const buf = self.state.toSlice();
+ var in = data;
+ while (in.len > 0) {
+ var left = State.RATE - self.buf_off;
+ if (left == 0) {
+ self.state.permute();
+ self.buf_off = 0;
+ left = State.RATE;
+ }
+ const ps = math.min(in.len, left);
+ for (buf[self.buf_off .. self.buf_off + ps]) |*p, i| {
+ p.* ^= in[i];
+ }
+ self.buf_off += ps;
+ in = in[ps..];
+ }
+ }
+
+ /// Finish the current hashing operation, writing the hash to `out`
+ ///
+ /// From 4.9 "Application to hashing"
+ /// By default, Gimli-Hash provides a fixed-length output of 32 bytes
+ /// (the concatenation of two 16-byte blocks). However, Gimli-Hash can
+ /// be used as an “extendable one-way function” (XOF).
+ pub fn final(self: *Self, out: []u8) void {
+ const buf = self.state.toSlice();
+
+ // XOR 1 into the next byte of the state
+ buf[self.buf_off] ^= 1;
+ // XOR 1 into the last byte of the state, position 47.
+ buf[buf.len - 1] ^= 1;
+
+ self.state.squeeze(out);
+ }
+};
+
+pub fn hash(out: []u8, in: []const u8) void {
+ var st = Hash.init();
+ st.update(in);
+ st.final(out);
+}
+
+test "hash" {
+ // a test vector (30) from NIST KAT submission.
+ var msg: [58 / 2]u8 = undefined;
+ try std.fmt.hexToBytes(&msg, "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C");
+ var md: [32]u8 = undefined;
+ hash(&md, msg);
+ htest.assertEqual("1C9A03DC6A5DDC5444CFC6F4B154CFF5CF081633B2CEA4D7D0AE7CCFED5AAA44", md);
+}
diff --git a/std/fmt.zig b/std/fmt.zig
index 75e0ce85cb..7bf1fa3d4d 100644
--- a/std/fmt.zig
+++ b/std/fmt.zig
@@ -13,7 +13,13 @@ pub const default_max_depth = 3;
/// Renders fmt string with args, calling output with slices of bytes.
/// If `output` returns an error, the error is returned from `format` and
/// `output` is not called again.
-pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void {
+pub fn format(
+ context: var,
+ comptime Errors: type,
+ output: fn (@typeOf(context), []const u8) Errors!void,
+ comptime fmt: []const u8,
+ args: ...,
+) Errors!void {
const State = enum {
Start,
OpenBrace,
@@ -28,7 +34,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
inline for (fmt) |c, i| {
switch (state) {
- State.Start => switch (c) {
+ .Start => switch (c) {
'{' => {
if (start_index < i) {
try output(context, fmt[start_index..i]);
@@ -45,7 +51,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
},
else => {},
},
- State.OpenBrace => switch (c) {
+ .OpenBrace => switch (c) {
'{' => {
state = State.Start;
start_index = i;
@@ -61,14 +67,14 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
state = State.FormatString;
},
},
- State.CloseBrace => switch (c) {
+ .CloseBrace => switch (c) {
'}' => {
state = State.Start;
start_index = i;
},
else => @compileError("Single '}' encountered in format string"),
},
- State.FormatString => switch (c) {
+ .FormatString => switch (c) {
'}' => {
const s = start_index + 1;
try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth);
@@ -78,7 +84,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
},
else => {},
},
- State.Pointer => switch (c) {
+ .Pointer => switch (c) {
'}' => {
try output(context, @typeName(@typeOf(args[next_arg]).Child));
try output(context, "@");
@@ -114,88 +120,93 @@ pub fn formatType(
) Errors!void {
const T = @typeOf(value);
switch (@typeInfo(T)) {
- builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => {
+ .ComptimeInt, .Int, .Float => {
return formatValue(value, fmt, context, Errors, output);
},
- builtin.TypeId.Void => {
+ .Void => {
return output(context, "void");
},
- builtin.TypeId.Bool => {
+ .Bool => {
return output(context, if (value) "true" else "false");
},
- builtin.TypeId.Optional => {
+ .Optional => {
if (value) |payload| {
return formatType(payload, fmt, context, Errors, output, max_depth);
} else {
return output(context, "null");
}
},
- builtin.TypeId.ErrorUnion => {
+ .ErrorUnion => {
if (value) |payload| {
return formatType(payload, fmt, context, Errors, output, max_depth);
} else |err| {
return formatType(err, fmt, context, Errors, output, max_depth);
}
},
- builtin.TypeId.ErrorSet => {
+ .ErrorSet => {
try output(context, "error.");
return output(context, @errorName(value));
},
- builtin.TypeId.Promise => {
+ .Promise => {
return format(context, Errors, output, "promise@{x}", @ptrToInt(value));
},
- builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
- if (comptime std.meta.trait.hasFn("format")(T)) return value.format(fmt, context, Errors, output);
+ .Enum => {
+ if (comptime std.meta.trait.hasFn("format")(T)) {
+ return value.format(fmt, context, Errors, output);
+ }
try output(context, @typeName(T));
- switch (comptime @typeId(T)) {
- builtin.TypeId.Enum => {
- try output(context, ".");
- try formatType(@tagName(value), "", context, Errors, output, max_depth);
- return;
- },
- builtin.TypeId.Struct => {
- if (max_depth == 0) {
- return output(context, "{ ... }");
- }
- comptime var field_i = 0;
- inline while (field_i < @memberCount(T)) : (field_i += 1) {
- if (field_i == 0) {
- try output(context, "{ .");
- } else {
- try output(context, ", .");
- }
- try output(context, @memberName(T, field_i));
- try output(context, " = ");
- try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1);
- }
- try output(context, " }");
- },
- builtin.TypeId.Union => {
- if (max_depth == 0) {
- return output(context, "{ ... }");
- }
- const info = @typeInfo(T).Union;
- if (info.tag_type) |UnionTagType| {
- try output(context, "{ .");
- try output(context, @tagName(UnionTagType(value)));
- try output(context, " = ");
- inline for (info.fields) |u_field| {
- if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
- try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1);
- }
- }
- try output(context, " }");
- } else {
- try format(context, Errors, output, "@{x}", @ptrToInt(&value));
+ try output(context, ".");
+ return formatType(@tagName(value), "", context, Errors, output, max_depth);
+ },
+ .Union => {
+ if (comptime std.meta.trait.hasFn("format")(T)) {
+ return value.format(fmt, context, Errors, output);
+ }
+
+ try output(context, @typeName(T));
+ if (max_depth == 0) {
+ return output(context, "{ ... }");
+ }
+ const info = @typeInfo(T).Union;
+ if (info.tag_type) |UnionTagType| {
+ try output(context, "{ .");
+ try output(context, @tagName(UnionTagType(value)));
+ try output(context, " = ");
+ inline for (info.fields) |u_field| {
+ if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) {
+ try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth - 1);
}
- },
- else => unreachable,
+ }
+ try output(context, " }");
+ } else {
+ try format(context, Errors, output, "@{x}", @ptrToInt(&value));
}
- return;
},
- builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
- builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
+ .Struct => {
+ if (comptime std.meta.trait.hasFn("format")(T)) {
+ return value.format(fmt, context, Errors, output);
+ }
+
+ try output(context, @typeName(T));
+ if (max_depth == 0) {
+ return output(context, "{ ... }");
+ }
+ comptime var field_i = 0;
+ inline while (field_i < @memberCount(T)) : (field_i += 1) {
+ if (field_i == 0) {
+ try output(context, "{ .");
+ } else {
+ try output(context, ", .");
+ }
+ try output(context, @memberName(T, field_i));
+ try output(context, " = ");
+ try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output, max_depth - 1);
+ }
+ try output(context, " }");
+ },
+ .Pointer => |ptr_info| switch (ptr_info.size) {
+ .One => switch (@typeInfo(ptr_info.child)) {
builtin.TypeId.Array => |info| {
if (info.child == u8) {
return formatText(value, fmt, context, Errors, output);
@@ -207,7 +218,7 @@ pub fn formatType(
},
else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
},
- builtin.TypeInfo.Pointer.Size.Many => {
+ .Many => {
if (ptr_info.child == u8) {
if (fmt.len > 0 and fmt[0] == 's') {
const len = mem.len(u8, value);
@@ -216,7 +227,7 @@ pub fn formatType(
}
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
},
- builtin.TypeInfo.Pointer.Size.Slice => {
+ .Slice => {
if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
return formatText(value, fmt, context, Errors, output);
}
@@ -225,17 +236,17 @@ pub fn formatType(
}
return format(context, Errors, output, "{}@{x}", @typeName(ptr_info.child), @ptrToInt(value.ptr));
},
- builtin.TypeInfo.Pointer.Size.C => {
+ .C => {
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
},
},
- builtin.TypeId.Array => |info| {
+ .Array => |info| {
if (info.child == u8) {
return formatText(value, fmt, context, Errors, output);
}
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value));
},
- builtin.TypeId.Fn => {
+ .Fn => {
return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value));
},
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
@@ -249,24 +260,24 @@ fn formatValue(
comptime Errors: type,
output: fn (@typeOf(context), []const u8) Errors!void,
) Errors!void {
- if (fmt.len > 0) {
- if (fmt[0] == 'B') {
- comptime var width: ?usize = null;
- if (fmt.len > 1) {
- if (fmt[1] == 'i') {
- if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
- return formatBytes(value, width, 1024, context, Errors, output);
+ if (fmt.len > 0 and fmt[0] == 'B') {
+ comptime var width: ?usize = null;
+ if (fmt.len > 1) {
+ if (fmt[1] == 'i') {
+ if (fmt.len > 2) {
+ width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable);
}
- width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
+ return formatBytes(value, width, 1024, context, Errors, output);
}
- return formatBytes(value, width, 1000, context, Errors, output);
+ width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable);
}
+ return formatBytes(value, width, 1000, context, Errors, output);
}
const T = @typeOf(value);
switch (@typeId(T)) {
- builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output),
- builtin.TypeId.Int, builtin.TypeId.ComptimeInt => return formatIntValue(value, fmt, context, Errors, output),
+ .Float => return formatFloatValue(value, fmt, context, Errors, output),
+ .Int, .ComptimeInt => return formatIntValue(value, fmt, context, Errors, output),
else => comptime unreachable,
}
}
@@ -797,7 +808,7 @@ pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T {
}
}
-test "fmt.parseInt" {
+test "parseInt" {
testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10);
testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10);
testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter);
@@ -828,7 +839,7 @@ pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsigned
return x;
}
-test "fmt.parseUnsigned" {
+test "parseUnsigned" {
testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124);
testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535);
testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10));
@@ -913,7 +924,7 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
size.* += bytes.len;
}
-test "buf print int" {
+test "bufPrintInt" {
var buffer: [100]u8 = undefined;
const buf = buffer[0..];
testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));
@@ -949,7 +960,7 @@ test "parse unsigned comptime" {
}
}
-test "fmt.format" {
+test "fmt.optional" {
{
const value: ?i32 = 1234;
try testFmt("optional: 1234\n", "optional: {}\n", value);
@@ -958,6 +969,9 @@ test "fmt.format" {
const value: ?i32 = null;
try testFmt("optional: null\n", "optional: {}\n", value);
}
+}
+
+test "fmt.error" {
{
const value: anyerror!i32 = 1234;
try testFmt("error union: 1234\n", "error union: {}\n", value);
@@ -966,10 +980,16 @@ test "fmt.format" {
const value: anyerror!i32 = error.InvalidChar;
try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value);
}
+}
+
+test "fmt.int.small" {
{
const value: u3 = 0b101;
try testFmt("u3: 5\n", "u3: {}\n", value);
}
+}
+
+test "fmt.int.specifier" {
{
const value: u8 = 'a';
try testFmt("u8: a\n", "u8: {c}\n", value);
@@ -978,6 +998,9 @@ test "fmt.format" {
const value: u8 = 0b1100;
try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value);
}
+}
+
+test "fmt.buffer" {
{
var buf1: [32]u8 = undefined;
var context = BufPrintContext{ .remaining = buf1[0..] };
@@ -995,6 +1018,9 @@ test "fmt.format" {
res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "1100"));
}
+}
+
+test "fmt.array" {
{
const value: [3]u8 = "abc";
try testFmt("array: abc\n", "array: {}\n", value);
@@ -1007,6 +1033,9 @@ test "fmt.format" {
&value,
);
}
+}
+
+test "fmt.slice" {
{
const value: []const u8 = "abc";
try testFmt("slice: abc\n", "slice: {}\n", value);
@@ -1015,6 +1044,12 @@ test "fmt.format" {
const value = @intToPtr([*]const []const u8, 0xdeadbeef)[0..0];
try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", value);
}
+
+ try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
+ try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
+}
+
+test "fmt.pointer" {
{
const value = @intToPtr(*i32, 0xdeadbeef);
try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
@@ -1028,12 +1063,19 @@ test "fmt.format" {
const value = @intToPtr(fn () void, 0xdeadbeef);
try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value);
}
- try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
- try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
+}
+
+test "fmt.cstr" {
try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C");
+}
+
+test "fmt.filesize" {
try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
+}
+
+test "fmt.struct" {
{
const Struct = struct {
field: u8,
@@ -1050,15 +1092,19 @@ test "fmt.format" {
const value = Struct{ .a = 0, .b = 1 };
try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value);
}
- {
- const Enum = enum {
- One,
- Two,
- };
- const value = Enum.Two;
- try testFmt("enum: Enum.Two\n", "enum: {}\n", value);
- try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
- }
+}
+
+test "fmt.enum" {
+ const Enum = enum {
+ One,
+ Two,
+ };
+ const value = Enum.Two;
+ try testFmt("enum: Enum.Two\n", "enum: {}\n", value);
+ try testFmt("enum: Enum.Two\n", "enum: {}\n", &value);
+}
+
+test "fmt.float.scientific" {
{
var buf1: [32]u8 = undefined;
const value: f32 = 1.34;
@@ -1088,6 +1134,9 @@ test "fmt.format" {
testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n"));
}
}
+}
+
+test "fmt.float.scientific.precision" {
{
var buf1: [32]u8 = undefined;
const value: f64 = 1.409706e-42;
@@ -1114,6 +1163,9 @@ test "fmt.format" {
const result = try bufPrint(buf1[0..], "f64: {e5}\n", value);
testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n"));
}
+}
+
+test "fmt.float.special" {
{
var buf1: [32]u8 = undefined;
const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
@@ -1136,6 +1188,9 @@ test "fmt.format" {
const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
testing.expect(mem.eql(u8, result, "f64: -inf\n"));
}
+}
+
+test "fmt.float.decimal" {
{
var buf1: [64]u8 = undefined;
const value: f64 = 1.52314e+29;
@@ -1216,7 +1271,9 @@ test "fmt.format" {
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
testing.expect(mem.eql(u8, result, "f64: 0.00000\n"));
}
- // libc checks
+}
+
+test "fmt.float.libc.sanity" {
{
var buf1: [32]u8 = undefined;
const value: f64 = f64(@bitCast(f32, u32(916964781)));
@@ -1267,127 +1324,127 @@ test "fmt.format" {
const result = try bufPrint(buf1[0..], "f64: {.5}\n", value);
testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n"));
}
- //custom type format
- {
- const Vec2 = struct {
- const SelfType = @This();
- x: f32,
- y: f32,
-
- pub fn format(
- self: SelfType,
- comptime fmt: []const u8,
- context: var,
- comptime Errors: type,
- output: fn (@typeOf(context), []const u8) Errors!void,
- ) Errors!void {
- switch (fmt.len) {
- 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
- 1 => switch (fmt[0]) {
- //point format
- 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
- //dimension format
- 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
- else => unreachable,
- },
+}
+
+test "fmt.custom" {
+ const Vec2 = struct {
+ const SelfType = @This();
+ x: f32,
+ y: f32,
+
+ pub fn format(
+ self: SelfType,
+ comptime fmt: []const u8,
+ context: var,
+ comptime Errors: type,
+ output: fn (@typeOf(context), []const u8) Errors!void,
+ ) Errors!void {
+ switch (fmt.len) {
+ 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
+ 1 => switch (fmt[0]) {
+ //point format
+ 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y),
+ //dimension format
+ 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y),
else => unreachable,
- }
+ },
+ else => unreachable,
}
- };
+ }
+ };
- var buf1: [32]u8 = undefined;
- var value = Vec2{
- .x = 10.2,
- .y = 2.22,
- };
- try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
- try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
+ var buf1: [32]u8 = undefined;
+ var value = Vec2{
+ .x = 10.2,
+ .y = 2.22,
+ };
+ try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value);
+ try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value);
- // same thing but not passing a pointer
- try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
- try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
- }
- //struct format
- {
- const S = struct {
- a: u32,
- b: anyerror,
- };
+ // same thing but not passing a pointer
+ try testFmt("point: (10.200,2.220)\n", "point: {}\n", value);
+ try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value);
+}
- const inst = S{
- .a = 456,
- .b = error.Unused,
- };
+test "fmt.struct" {
+ const S = struct {
+ a: u32,
+ b: anyerror,
+ };
- try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
- }
- //union format
- {
- const TU = union(enum) {
- float: f32,
- int: u32,
- };
+ const inst = S{
+ .a = 456,
+ .b = error.Unused,
+ };
- const UU = union {
- float: f32,
- int: u32,
- };
+ try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst);
+}
- const EU = extern union {
- float: f32,
- int: u32,
- };
+test "fmt.union" {
+ const TU = union(enum) {
+ float: f32,
+ int: u32,
+ };
- const tu_inst = TU{ .int = 123 };
- const uu_inst = UU{ .int = 456 };
- const eu_inst = EU{ .float = 321.123 };
+ const UU = union {
+ float: f32,
+ int: u32,
+ };
- try testFmt("TU{ .int = 123 }", "{}", tu_inst);
+ const EU = extern union {
+ float: f32,
+ int: u32,
+ };
- var buf: [100]u8 = undefined;
- const uu_result = try bufPrint(buf[0..], "{}", uu_inst);
- testing.expect(mem.eql(u8, uu_result[0..3], "UU@"));
+ const tu_inst = TU{ .int = 123 };
+ const uu_inst = UU{ .int = 456 };
+ const eu_inst = EU{ .float = 321.123 };
- const eu_result = try bufPrint(buf[0..], "{}", eu_inst);
- testing.expect(mem.eql(u8, uu_result[0..3], "EU@"));
- }
- //enum format
- {
- const E = enum {
- One,
- Two,
- Three,
- };
+ try testFmt("TU{ .int = 123 }", "{}", tu_inst);
- const inst = E.Two;
+ var buf: [100]u8 = undefined;
+ const uu_result = try bufPrint(buf[0..], "{}", uu_inst);
+ testing.expect(mem.eql(u8, uu_result[0..3], "UU@"));
- try testFmt("E.Two", "{}", inst);
- }
- //self-referential struct format
- {
- const S = struct {
- const SelfType = @This();
- a: ?*SelfType,
- };
+ const eu_result = try bufPrint(buf[0..], "{}", eu_inst);
+ testing.expect(mem.eql(u8, uu_result[0..3], "EU@"));
+}
- var inst = S{
- .a = null,
- };
- inst.a = &inst;
+test "fmt.enum" {
+ const E = enum {
+ One,
+ Two,
+ Three,
+ };
- try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
- }
- //print bytes as hex
- {
- const some_bytes = "\xCA\xFE\xBA\xBE";
- try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
- try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
- //Test Slices
- try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]);
- try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]);
- const bytes_with_zeros = "\x00\x0E\xBA\xBE";
- try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros);
- }
+ const inst = E.Two;
+
+ try testFmt("E.Two", "{}", inst);
+}
+
+test "fmt.struct.self-referential" {
+ const S = struct {
+ const SelfType = @This();
+ a: ?*SelfType,
+ };
+
+ var inst = S{
+ .a = null,
+ };
+ inst.a = &inst;
+
+ try testFmt("S{ .a = S{ .a = S{ .a = S{ ... } } } }", "{}", inst);
+}
+
+test "fmt.bytes.hex" {
+ const some_bytes = "\xCA\xFE\xBA\xBE";
+ try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes);
+ try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes);
+ //Test Slices
+ try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]);
+ try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]);
+ const bytes_with_zeros = "\x00\x0E\xBA\xBE";
+ try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros);
}
fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void {
diff --git a/std/hash_map.zig b/std/hash_map.zig
index df7ba740e8..13ebb0a1fe 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -564,12 +564,12 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type
},
builtin.TypeId.Float => |info| {
- return autoHash(@bitCast(@IntType(false, info.bits), key), rng);
+ return autoHash(@bitCast(@IntType(false, info.bits), key), rng, HashInt);
},
- builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng),
- builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng),
- builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng),
- builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng),
+ builtin.TypeId.Bool => return autoHash(@boolToInt(key), rng, HashInt),
+ builtin.TypeId.Enum => return autoHash(@enumToInt(key), rng, HashInt),
+ builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng, HashInt),
+ builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng, HashInt),
builtin.TypeId.BoundFn,
builtin.TypeId.ComptimeFloat,
diff --git a/std/mem.zig b/std/mem.zig
index 49ea48a890..7ecd483020 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -996,6 +996,43 @@ test "mem.join" {
testing.expect(eql(u8, try join(a, ",", [_][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c"));
}
+/// Copies each T from slices into a new slice that exactly holds all the elements.
+pub fn concat(allocator: *Allocator, comptime T: type, slices: []const []const T) ![]T {
+ if (slices.len == 0) return (([*]T)(undefined))[0..0];
+
+ const total_len = blk: {
+ var sum: usize = 0;
+ for (slices) |slice| {
+ sum += slice.len;
+ }
+ break :blk sum;
+ };
+
+ const buf = try allocator.alloc(T, total_len);
+ errdefer allocator.free(buf);
+
+ var buf_index: usize = 0;
+ for (slices) |slice| {
+ copy(T, buf[buf_index..], slice);
+ buf_index += slice.len;
+ }
+
+ // No need for shrink since buf is exactly the correct size.
+ return buf;
+}
+
+test "concat" {
+ var buf: [1024]u8 = undefined;
+ const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
+ testing.expect(eql(u8, try concat(a, u8, [_][]const u8{ "abc", "def", "ghi" }), "abcdefghi"));
+ testing.expect(eql(u32, try concat(a, u32, [_][]const u32{
+ [_]u32{ 0, 1 },
+ [_]u32{ 2, 3, 4 },
+ [_]u32{},
+ [_]u32{5},
+ }), [_]u32{ 0, 1, 2, 3, 4, 5 }));
+}
+
test "testStringEquality" {
testing.expect(eql(u8, "abcd", "abcd"));
testing.expect(!eql(u8, "abcdef", "abZdef"));
diff --git a/std/os/windows.zig b/std/os/windows.zig
index 7b9feb7b15..d10ab695db 100644
--- a/std/os/windows.zig
+++ b/std/os/windows.zig
@@ -632,6 +632,7 @@ pub fn GetEnvironmentVariableW(lpName: LPWSTR, lpBuffer: LPWSTR, nSize: DWORD) G
pub const CreateProcessError = error{
FileNotFound,
+ AccessDenied,
InvalidName,
Unexpected,
};
@@ -663,6 +664,7 @@ pub fn CreateProcessW(
switch (kernel32.GetLastError()) {
ERROR.FILE_NOT_FOUND => return error.FileNotFound,
ERROR.PATH_NOT_FOUND => return error.FileNotFound,
+ ERROR.ACCESS_DENIED => return error.AccessDenied,
ERROR.INVALID_PARAMETER => unreachable,
ERROR.INVALID_NAME => return error.InvalidName,
else => |err| return unexpectedError(err),