aboutsummaryrefslogtreecommitdiff
path: root/lib/std
diff options
context:
space:
mode:
Diffstat (limited to 'lib/std')
-rw-r--r--lib/std/Thread/Condition.zig196
-rw-r--r--lib/std/builtin.zig22
-rw-r--r--lib/std/crypto/25519/ed25519.zig1
-rw-r--r--lib/std/fs/path.zig390
-rw-r--r--lib/std/fs/test.zig4
-rw-r--r--lib/std/fs/wasi.zig5
-rw-r--r--lib/std/target.zig4
7 files changed, 377 insertions, 245 deletions
diff --git a/lib/std/Thread/Condition.zig b/lib/std/Thread/Condition.zig
index 1482c8166d..3625aab576 100644
--- a/lib/std/Thread/Condition.zig
+++ b/lib/std/Thread/Condition.zig
@@ -194,42 +194,27 @@ const FutexImpl = struct {
const signal_mask = 0xffff << 16;
fn wait(self: *Impl, mutex: *Mutex, timeout: ?u64) error{Timeout}!void {
- // Register that we're waiting on the state by incrementing the wait count.
- // This assumes that there can be at most ((1<<16)-1) or 65,355 threads concurrently waiting on the same Condvar.
- // If this is hit in practice, then this condvar not working is the least of your concerns.
+ // Observe the epoch, then check the state again to see if we should wake up.
+ // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock:
+ //
+ // - T1: s = LOAD(&state)
+ // - T2: UPDATE(&s, signal)
+ // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch)
+ // - T1: e = LOAD(&epoch) (was reordered after the state load)
+ // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change)
+ //
+ // Acquire barrier to ensure the epoch load happens before the state load.
+ var epoch = self.epoch.load(.Acquire);
var state = self.state.fetchAdd(one_waiter, .Monotonic);
assert(state & waiter_mask != waiter_mask);
state += one_waiter;
- // Temporarily release the mutex in order to block on the condition variable.
mutex.unlock();
defer mutex.lock();
var futex_deadline = Futex.Deadline.init(timeout);
- while (true) {
- // Try to wake up by consuming a signal and decremented the waiter we added previously.
- // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return.
- while (state & signal_mask != 0) {
- const new_state = state - one_waiter - one_signal;
- state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return;
- }
-
- // Observe the epoch, then check the state again to see if we should wake up.
- // The epoch must be observed before we check the state or we could potentially miss a wake() and deadlock:
- //
- // - T1: s = LOAD(&state)
- // - T2: UPDATE(&s, signal)
- // - T2: UPDATE(&epoch, 1) + FUTEX_WAKE(&epoch)
- // - T1: e = LOAD(&epoch) (was reordered after the state load)
- // - T1: s & signals == 0 -> FUTEX_WAIT(&epoch, e) (missed the state update + the epoch change)
- //
- // Acquire barrier to ensure the epoch load happens before the state load.
- const epoch = self.epoch.load(.Acquire);
- state = self.state.load(.Monotonic);
- if (state & signal_mask != 0) {
- continue;
- }
+ while (true) {
futex_deadline.wait(&self.epoch, epoch) catch |err| switch (err) {
// On timeout, we must decrement the waiter we added above.
error.Timeout => {
@@ -247,6 +232,16 @@ const FutexImpl = struct {
}
},
};
+
+ epoch = self.epoch.load(.Acquire);
+ state = self.state.load(.Monotonic);
+
+ // Try to wake up by consuming a signal and decremented the waiter we added previously.
+ // Acquire barrier ensures code before the wake() which added the signal happens before we decrement it and return.
+ while (state & signal_mask != 0) {
+ const new_state = state - one_waiter - one_signal;
+ state = self.state.tryCompareAndSwap(state, new_state, .Acquire, .Monotonic) orelse return;
+ }
}
}
@@ -536,3 +531,150 @@ test "Condition - broadcasting" {
t.join();
}
}
+
+test "Condition - broadcasting - wake all threads" {
+ // Tests issue #12877
+ // This test requires spawning threads
+ if (builtin.single_threaded) {
+ return error.SkipZigTest;
+ }
+
+ var num_runs: usize = 1;
+ const num_threads = 10;
+
+ while (num_runs > 0) : (num_runs -= 1) {
+ const BroadcastTest = struct {
+ mutex: Mutex = .{},
+ cond: Condition = .{},
+ completed: Condition = .{},
+ count: usize = 0,
+ thread_id_to_wake: usize = 0,
+ threads: [num_threads]std.Thread = undefined,
+ wakeups: usize = 0,
+
+ fn run(self: *@This(), thread_id: usize) void {
+ self.mutex.lock();
+ defer self.mutex.unlock();
+
+ // The last broadcast thread to start tells the main test thread it's completed.
+ self.count += 1;
+ if (self.count == num_threads) {
+ self.completed.signal();
+ }
+
+ while (self.thread_id_to_wake != thread_id) {
+ self.cond.timedWait(&self.mutex, 1 * std.time.ns_per_s) catch std.debug.panic("thread_id {d} timeout {d}", .{ thread_id, self.thread_id_to_wake });
+ self.wakeups += 1;
+ }
+ if (self.thread_id_to_wake <= num_threads) {
+ // Signal next thread to wake up.
+ self.thread_id_to_wake += 1;
+ self.cond.broadcast();
+ }
+ }
+ };
+
+ var broadcast_test = BroadcastTest{};
+ var thread_id: usize = 1;
+ for (broadcast_test.threads) |*t| {
+ t.* = try std.Thread.spawn(.{}, BroadcastTest.run, .{ &broadcast_test, thread_id });
+ thread_id += 1;
+ }
+
+ {
+ broadcast_test.mutex.lock();
+ defer broadcast_test.mutex.unlock();
+
+ // Wait for all the broadcast threads to spawn.
+ // timedWait() to detect any potential deadlocks.
+ while (broadcast_test.count != num_threads) {
+ try broadcast_test.completed.timedWait(
+ &broadcast_test.mutex,
+ 1 * std.time.ns_per_s,
+ );
+ }
+
+ // Signal thread 1 to wake up
+ broadcast_test.thread_id_to_wake = 1;
+ broadcast_test.cond.broadcast();
+ }
+
+ for (broadcast_test.threads) |t| {
+ t.join();
+ }
+ }
+}
+
+test "Condition - signal wakes one" {
+ // This test requires spawning threads
+ if (builtin.single_threaded) {
+ return error.SkipZigTest;
+ }
+
+ var num_runs: usize = 1;
+ const num_threads = 3;
+ const timeoutDelay = 10 * std.time.ns_per_ms;
+
+ while (num_runs > 0) : (num_runs -= 1) {
+
+ // Start multiple runner threads, wait for them to start and send the signal
+ // then. Expect that one thread wake up and all other times out.
+ //
+ // Test depends on delay in timedWait! If too small all threads can timeout
+ // before any one gets wake up.
+
+ const Runner = struct {
+ mutex: Mutex = .{},
+ cond: Condition = .{},
+ completed: Condition = .{},
+ count: usize = 0,
+ threads: [num_threads]std.Thread = undefined,
+ wakeups: usize = 0,
+ timeouts: usize = 0,
+
+ fn run(self: *@This()) void {
+ self.mutex.lock();
+ defer self.mutex.unlock();
+
+ // The last started thread tells the main test thread it's completed.
+ self.count += 1;
+ if (self.count == num_threads) {
+ self.completed.signal();
+ }
+
+ self.cond.timedWait(&self.mutex, timeoutDelay) catch {
+ self.timeouts += 1;
+ return;
+ };
+ self.wakeups += 1;
+ }
+ };
+
+ // Start threads
+ var runner = Runner{};
+ for (runner.threads) |*t| {
+ t.* = try std.Thread.spawn(.{}, Runner.run, .{&runner});
+ }
+
+ {
+ runner.mutex.lock();
+ defer runner.mutex.unlock();
+
+ // Wait for all the threads to spawn.
+ // timedWait() to detect any potential deadlocks.
+ while (runner.count != num_threads) {
+ try runner.completed.timedWait(&runner.mutex, 1 * std.time.ns_per_s);
+ }
+ // Signal one thread, the others should get timeout.
+ runner.cond.signal();
+ }
+
+ for (runner.threads) |t| {
+ t.join();
+ }
+
+ // Expect that only one got singal
+ try std.testing.expectEqual(runner.wakeups, 1);
+ try std.testing.expectEqual(runner.timeouts, num_threads - 1);
+ }
+}
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 33604dfee7..1f7d48ccb9 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -168,7 +168,7 @@ pub const AddressSpace = enum {
gs,
fs,
ss,
- // GPU address spaces
+ // GPU address spaces.
global,
constant,
param,
@@ -869,6 +869,26 @@ pub noinline fn returnError(st: *StackTrace) void {
addErrRetTraceAddr(st, @returnAddress());
}
+pub const panic_messages = struct {
+ pub const unreach = "reached unreachable code";
+ pub const unwrap_null = "attempt to use null value";
+ pub const cast_to_null = "cast causes pointer to be null";
+ pub const incorrect_alignment = "incorrect alignment";
+ pub const invalid_error_code = "invalid error code";
+ pub const cast_truncated_data = "integer cast truncated bits";
+ pub const negative_to_unsigned = "attempt to cast negative value to unsigned integer";
+ pub const integer_overflow = "integer overflow";
+ pub const shl_overflow = "left shift overflowed bits";
+ pub const shr_overflow = "right shift overflowed bits";
+ pub const divide_by_zero = "division by zero";
+ pub const exact_division_remainder = "exact division produced remainder";
+ pub const inactive_union_field = "access of inactive union field";
+ pub const integer_part_out_of_bounds = "integer part of floating point value out of bounds";
+ pub const corrupt_switch = "switch on corrupt value";
+ pub const shift_rhs_too_big = "shift amount is greater than the type size";
+ pub const invalid_enum_value = "invalid enum value";
+};
+
pub inline fn addErrRetTraceAddr(st: *StackTrace, addr: usize) void {
if (st.index < st.instruction_addresses.len)
st.instruction_addresses[st.index] = addr;
diff --git a/lib/std/crypto/25519/ed25519.zig b/lib/std/crypto/25519/ed25519.zig
index 552bd114a1..149191040a 100644
--- a/lib/std/crypto/25519/ed25519.zig
+++ b/lib/std/crypto/25519/ed25519.zig
@@ -318,6 +318,7 @@ pub const Ed25519 = struct {
h.update(&scalar_and_prefix.prefix);
var noise2: [noise_length]u8 = undefined;
crypto.random.bytes(&noise2);
+ h.update(&noise2);
if (noise) |*z| {
h.update(z);
}
diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig
index 0d102493cf..feacf38daf 100644
--- a/lib/std/fs/path.zig
+++ b/lib/std/fs/path.zig
@@ -467,55 +467,49 @@ pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8 {
/// Path separators are canonicalized to '\\' and drives are canonicalized to capital letters.
/// Note: all usage of this function should be audited due to the existence of symlinks.
/// Without performing actual syscalls, resolving `..` could be incorrect.
+/// This API may break in the future: https://github.com/ziglang/zig/issues/13613
pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
- if (paths.len == 0) {
- assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
- return process.getCwdAlloc(allocator);
- }
+ assert(paths.len > 0);
// determine which disk designator we will result with, if any
var result_drive_buf = "_:".*;
- var result_disk_designator: []const u8 = "";
- var have_drive_kind = WindowsPath.Kind.None;
+ var disk_designator: []const u8 = "";
+ var drive_kind = WindowsPath.Kind.None;
var have_abs_path = false;
var first_index: usize = 0;
- var max_size: usize = 0;
for (paths) |p, i| {
const parsed = windowsParsePath(p);
if (parsed.is_abs) {
have_abs_path = true;
first_index = i;
- max_size = result_disk_designator.len;
}
switch (parsed.kind) {
- WindowsPath.Kind.Drive => {
+ .Drive => {
result_drive_buf[0] = ascii.toUpper(parsed.disk_designator[0]);
- result_disk_designator = result_drive_buf[0..];
- have_drive_kind = WindowsPath.Kind.Drive;
+ disk_designator = result_drive_buf[0..];
+ drive_kind = WindowsPath.Kind.Drive;
},
- WindowsPath.Kind.NetworkShare => {
- result_disk_designator = parsed.disk_designator;
- have_drive_kind = WindowsPath.Kind.NetworkShare;
+ .NetworkShare => {
+ disk_designator = parsed.disk_designator;
+ drive_kind = WindowsPath.Kind.NetworkShare;
},
- WindowsPath.Kind.None => {},
+ .None => {},
}
- max_size += p.len + 1;
}
// if we will result with a disk designator, loop again to determine
// which is the last time the disk designator is absolutely specified, if any
// and count up the max bytes for paths related to this disk designator
- if (have_drive_kind != WindowsPath.Kind.None) {
+ if (drive_kind != WindowsPath.Kind.None) {
have_abs_path = false;
first_index = 0;
- max_size = result_disk_designator.len;
var correct_disk_designator = false;
for (paths) |p, i| {
const parsed = windowsParsePath(p);
if (parsed.kind != WindowsPath.Kind.None) {
- if (parsed.kind == have_drive_kind) {
- correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
+ if (parsed.kind == drive_kind) {
+ correct_disk_designator = compareDiskDesignators(drive_kind, disk_designator, parsed.disk_designator);
} else {
continue;
}
@@ -525,92 +519,51 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
}
if (parsed.is_abs) {
first_index = i;
- max_size = result_disk_designator.len;
have_abs_path = true;
}
- max_size += p.len + 1;
}
}
- // Allocate result and fill in the disk designator, calling getCwd if we have to.
- var result: []u8 = undefined;
- var result_index: usize = 0;
-
- if (have_abs_path) {
- switch (have_drive_kind) {
- WindowsPath.Kind.Drive => {
- result = try allocator.alloc(u8, max_size);
+ // Allocate result and fill in the disk designator.
+ var result = std.ArrayList(u8).init(allocator);
+ defer result.deinit();
- mem.copy(u8, result, result_disk_designator);
- result_index += result_disk_designator.len;
+ const disk_designator_len: usize = l: {
+ if (!have_abs_path) break :l 0;
+ switch (drive_kind) {
+ .Drive => {
+ try result.appendSlice(disk_designator);
+ break :l disk_designator.len;
},
- WindowsPath.Kind.NetworkShare => {
- result = try allocator.alloc(u8, max_size);
+ .NetworkShare => {
var it = mem.tokenize(u8, paths[first_index], "/\\");
const server_name = it.next().?;
const other_name = it.next().?;
- result[result_index] = '\\';
- result_index += 1;
- result[result_index] = '\\';
- result_index += 1;
- mem.copy(u8, result[result_index..], server_name);
- result_index += server_name.len;
- result[result_index] = '\\';
- result_index += 1;
- mem.copy(u8, result[result_index..], other_name);
- result_index += other_name.len;
-
- result_disk_designator = result[0..result_index];
+ try result.ensureUnusedCapacity(2 + 1 + server_name.len + other_name.len);
+ result.appendSliceAssumeCapacity("\\\\");
+ result.appendSliceAssumeCapacity(server_name);
+ result.appendAssumeCapacity('\\');
+ result.appendSliceAssumeCapacity(other_name);
+
+ break :l result.items.len;
},
- WindowsPath.Kind.None => {
- assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
- const cwd = try process.getCwdAlloc(allocator);
- defer allocator.free(cwd);
- const parsed_cwd = windowsParsePath(cwd);
- result = try allocator.alloc(u8, max_size + parsed_cwd.disk_designator.len + 1);
- mem.copy(u8, result, parsed_cwd.disk_designator);
- result_index += parsed_cwd.disk_designator.len;
- result_disk_designator = result[0..parsed_cwd.disk_designator.len];
- if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
- result[0] = ascii.toUpper(result[0]);
- }
- have_drive_kind = parsed_cwd.kind;
+ .None => {
+ break :l 1;
},
}
- } else {
- assert(native_os == .windows); // resolveWindows called on non windows can't use getCwd
- // TODO call get cwd for the result_disk_designator instead of the global one
- const cwd = try process.getCwdAlloc(allocator);
- defer allocator.free(cwd);
-
- result = try allocator.alloc(u8, max_size + cwd.len + 1);
-
- mem.copy(u8, result, cwd);
- result_index += cwd.len;
- const parsed_cwd = windowsParsePath(result[0..result_index]);
- result_disk_designator = parsed_cwd.disk_designator;
- if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
- result[0] = ascii.toUpper(result[0]);
- // Remove the trailing slash if present, eg. if the cwd is a root
- // directory.
- if (cwd.len > 0 and cwd[cwd.len - 1] == sep_windows) {
- result_index -= 1;
- }
- }
- have_drive_kind = parsed_cwd.kind;
- }
- errdefer allocator.free(result);
+ };
- // Now we know the disk designator to use, if any, and what kind it is. And our result
- // is big enough to append all the paths to.
var correct_disk_designator = true;
+ var negative_count: usize = 0;
+
for (paths[first_index..]) |p| {
const parsed = windowsParsePath(p);
- if (parsed.kind != WindowsPath.Kind.None) {
- if (parsed.kind == have_drive_kind) {
- correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator);
+ if (parsed.kind != .None) {
+ if (parsed.kind == drive_kind) {
+ const dd = result.items[0..disk_designator_len];
+ correct_disk_designator = compareDiskDesignators(drive_kind, dd, parsed.disk_designator);
} else {
continue;
}
@@ -619,154 +572,167 @@ pub fn resolveWindows(allocator: Allocator, paths: []const []const u8) ![]u8 {
continue;
}
var it = mem.tokenize(u8, p[parsed.disk_designator.len..], "/\\");
- while (it.next()) |component| {
+ component: while (it.next()) |component| {
if (mem.eql(u8, component, ".")) {
continue;
} else if (mem.eql(u8, component, "..")) {
while (true) {
- if (result_index == 0 or result_index == result_disk_designator.len)
- break;
- result_index -= 1;
- if (result[result_index] == '\\' or result[result_index] == '/')
+ if (result.items.len == 0) {
+ negative_count += 1;
+ continue :component;
+ }
+ if (result.items.len == disk_designator_len) {
break;
+ }
+ const end_with_sep = switch (result.items[result.items.len - 1]) {
+ '\\', '/' => true,
+ else => false,
+ };
+ result.items.len -= 1;
+ if (end_with_sep) break;
}
+ } else if (!have_abs_path and result.items.len == 0) {
+ try result.appendSlice(component);
} else {
- result[result_index] = sep_windows;
- result_index += 1;
- mem.copy(u8, result[result_index..], component);
- result_index += component.len;
+ try result.ensureUnusedCapacity(1 + component.len);
+ result.appendAssumeCapacity('\\');
+ result.appendSliceAssumeCapacity(component);
}
}
}
- if (result_index == result_disk_designator.len) {
- result[result_index] = '\\';
- result_index += 1;
+ if (disk_designator_len != 0 and result.items.len == disk_designator_len) {
+ try result.append('\\');
+ return result.toOwnedSlice();
+ }
+
+ if (result.items.len == 0) {
+ if (negative_count == 0) {
+ return allocator.dupe(u8, ".");
+ } else {
+ const real_result = try allocator.alloc(u8, 3 * negative_count - 1);
+ var count = negative_count - 1;
+ var i: usize = 0;
+ while (count > 0) : (count -= 1) {
+ real_result[i..][0..3].* = "..\\".*;
+ i += 3;
+ }
+ real_result[i..][0..2].* = "..".*;
+ return real_result;
+ }
}
- return allocator.shrink(result, result_index);
+ if (negative_count == 0) {
+ return result.toOwnedSlice();
+ } else {
+ const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len);
+ var count = negative_count;
+ var i: usize = 0;
+ while (count > 0) : (count -= 1) {
+ real_result[i..][0..3].* = "..\\".*;
+ i += 3;
+ }
+ mem.copy(u8, real_result[i..], result.items);
+ return real_result;
+ }
}
/// This function is like a series of `cd` statements executed one after another.
/// It resolves "." and "..".
/// The result does not have a trailing path separator.
-/// If all paths are relative it uses the current working directory as a starting point.
-/// Note: all usage of this function should be audited due to the existence of symlinks.
-/// Without performing actual syscalls, resolving `..` could be incorrect.
-pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) ![]u8 {
- if (paths.len == 0) {
- assert(native_os != .windows); // resolvePosix called on windows can't use getCwd
- return process.getCwdAlloc(allocator);
- }
+/// This function does not perform any syscalls. Executing this series of path
+/// lookups on the actual filesystem may produce different results due to
+/// symlinks.
+pub fn resolvePosix(allocator: Allocator, paths: []const []const u8) Allocator.Error![]u8 {
+ assert(paths.len > 0);
- var first_index: usize = 0;
- var have_abs = false;
- var max_size: usize = 0;
- for (paths) |p, i| {
- if (isAbsolutePosix(p)) {
- first_index = i;
- have_abs = true;
- max_size = 0;
- }
- max_size += p.len + 1;
- }
-
- var result: []u8 = undefined;
- var result_index: usize = 0;
+ var result = std.ArrayList(u8).init(allocator);
+ defer result.deinit();
- if (have_abs) {
- result = try allocator.alloc(u8, max_size);
- } else {
- assert(native_os != .windows); // resolvePosix called on windows can't use getCwd
- const cwd = try process.getCwdAlloc(allocator);
- defer allocator.free(cwd);
- result = try allocator.alloc(u8, max_size + cwd.len + 1);
- mem.copy(u8, result, cwd);
- result_index += cwd.len;
- }
- errdefer allocator.free(result);
+ var negative_count: usize = 0;
+ var is_abs = false;
- for (paths[first_index..]) |p| {
+ for (paths) |p| {
+ if (isAbsolutePosix(p)) {
+ is_abs = true;
+ negative_count = 0;
+ result.clearRetainingCapacity();
+ }
var it = mem.tokenize(u8, p, "/");
- while (it.next()) |component| {
+ component: while (it.next()) |component| {
if (mem.eql(u8, component, ".")) {
continue;
} else if (mem.eql(u8, component, "..")) {
while (true) {
- if (result_index == 0)
- break;
- result_index -= 1;
- if (result[result_index] == '/')
- break;
+ if (result.items.len == 0) {
+ negative_count += @boolToInt(!is_abs);
+ continue :component;
+ }
+ const ends_with_slash = result.items[result.items.len - 1] == '/';
+ result.items.len -= 1;
+ if (ends_with_slash) break;
}
+ } else if (result.items.len > 0 or is_abs) {
+ try result.ensureUnusedCapacity(1 + component.len);
+ result.appendAssumeCapacity('/');
+ result.appendSliceAssumeCapacity(component);
} else {
- result[result_index] = '/';
- result_index += 1;
- mem.copy(u8, result[result_index..], component);
- result_index += component.len;
+ try result.appendSlice(component);
}
}
}
- if (result_index == 0) {
- result[0] = '/';
- result_index += 1;
+ if (result.items.len == 0) {
+ if (is_abs) {
+ return allocator.dupe(u8, "/");
+ }
+ if (negative_count == 0) {
+ return allocator.dupe(u8, ".");
+ } else {
+ const real_result = try allocator.alloc(u8, 3 * negative_count - 1);
+ var count = negative_count - 1;
+ var i: usize = 0;
+ while (count > 0) : (count -= 1) {
+ real_result[i..][0..3].* = "../".*;
+ i += 3;
+ }
+ real_result[i..][0..2].* = "..".*;
+ return real_result;
+ }
}
- return allocator.shrink(result, result_index);
+ if (negative_count == 0) {
+ return result.toOwnedSlice();
+ } else {
+ const real_result = try allocator.alloc(u8, 3 * negative_count + result.items.len);
+ var count = negative_count;
+ var i: usize = 0;
+ while (count > 0) : (count -= 1) {
+ real_result[i..][0..3].* = "../".*;
+ i += 3;
+ }
+ mem.copy(u8, real_result[i..], result.items);
+ return real_result;
+ }
}
test "resolve" {
- if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
+ try testResolveWindows(&[_][]const u8{ "a\\b\\c\\", "..\\..\\.." }, "..");
+ try testResolveWindows(&[_][]const u8{"."}, ".");
- const cwd = try process.getCwdAlloc(testing.allocator);
- defer testing.allocator.free(cwd);
- if (native_os == .windows) {
- if (windowsParsePath(cwd).kind == WindowsPath.Kind.Drive) {
- cwd[0] = ascii.toUpper(cwd[0]);
- }
- try testResolveWindows(&[_][]const u8{"."}, cwd);
- } else {
- try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, cwd);
- try testResolvePosix(&[_][]const u8{"."}, cwd);
- }
+ try testResolvePosix(&[_][]const u8{ "a/b/c/", "../../.." }, "..");
+ try testResolvePosix(&[_][]const u8{"."}, ".");
}
test "resolveWindows" {
- if (builtin.target.cpu.arch == .aarch64) {
- // TODO https://github.com/ziglang/zig/issues/3288
- return error.SkipZigTest;
- }
- if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
- if (native_os == .windows) {
- const cwd = try process.getCwdAlloc(testing.allocator);
- defer testing.allocator.free(cwd);
- const parsed_cwd = windowsParsePath(cwd);
- {
- const expected = try join(testing.allocator, &[_][]const u8{
- parsed_cwd.disk_designator,
- "usr\\local\\lib\\zig\\std\\array_list.zig",
- });
- defer testing.allocator.free(expected);
- if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
- expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]);
- }
- try testResolveWindows(&[_][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" }, expected);
- }
- {
- const expected = try join(testing.allocator, &[_][]const u8{
- cwd,
- "usr\\local\\lib\\zig",
- });
- defer testing.allocator.free(expected);
- if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
- expected[0] = ascii.toUpper(parsed_cwd.disk_designator[0]);
- }
- try testResolveWindows(&[_][]const u8{ "usr/local", "lib\\zig" }, expected);
- }
- }
+ try testResolveWindows(
+ &[_][]const u8{ "Z:\\", "/usr/local", "lib\\zig\\std\\array_list.zig" },
+ "Z:\\usr\\local\\lib\\zig\\std\\array_list.zig",
+ );
+ try testResolveWindows(
+ &[_][]const u8{ "z:\\", "usr/local", "lib\\zig" },
+ "Z:\\usr\\local\\lib\\zig",
+ );
try testResolveWindows(&[_][]const u8{ "c:\\a\\b\\c", "/hi", "ok" }, "C:\\hi\\ok");
try testResolveWindows(&[_][]const u8{ "c:/blah\\blah", "d:/games", "c:../a" }, "C:\\blah\\a");
@@ -781,12 +747,12 @@ test "resolveWindows" {
try testResolveWindows(&[_][]const u8{ "c:/", "//server//share" }, "\\\\server\\share\\");
try testResolveWindows(&[_][]const u8{ "c:/", "///some//dir" }, "C:\\some\\dir");
try testResolveWindows(&[_][]const u8{ "C:\\foo\\tmp.3\\", "..\\tmp.3\\cycles\\root.js" }, "C:\\foo\\tmp.3\\cycles\\root.js");
+
+ // Keep relative paths relative.
+ try testResolveWindows(&[_][]const u8{"a/b"}, "a\\b");
}
test "resolvePosix" {
- if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
-
try testResolvePosix(&[_][]const u8{ "/a/b", "c" }, "/a/b/c");
try testResolvePosix(&[_][]const u8{ "/a/b", "c", "//d", "e///" }, "/d/e");
try testResolvePosix(&[_][]const u8{ "/a/b/c", "..", "../" }, "/a");
@@ -797,18 +763,21 @@ test "resolvePosix" {
try testResolvePosix(&[_][]const u8{ "/var/lib", "/../", "file/" }, "/file");
try testResolvePosix(&[_][]const u8{ "/some/dir", ".", "/absolute/" }, "/absolute");
try testResolvePosix(&[_][]const u8{ "/foo/tmp.3/", "../tmp.3/cycles/root.js" }, "/foo/tmp.3/cycles/root.js");
+
+ // Keep relative paths relative.
+ try testResolvePosix(&[_][]const u8{"a/b"}, "a/b");
}
fn testResolveWindows(paths: []const []const u8, expected: []const u8) !void {
const actual = try resolveWindows(testing.allocator, paths);
defer testing.allocator.free(actual);
- try testing.expect(mem.eql(u8, actual, expected));
+ try testing.expectEqualStrings(expected, actual);
}
fn testResolvePosix(paths: []const []const u8, expected: []const u8) !void {
const actual = try resolvePosix(testing.allocator, paths);
defer testing.allocator.free(actual);
- try testing.expect(mem.eql(u8, actual, expected));
+ try testing.expectEqualStrings(expected, actual);
}
/// Strip the last component from a file path.
@@ -1089,13 +1058,15 @@ pub fn relativeWindows(allocator: Allocator, from: []const u8, to: []const u8) !
if (parsed_from.kind != parsed_to.kind) {
break :x true;
} else switch (parsed_from.kind) {
- WindowsPath.Kind.NetworkShare => {
+ .NetworkShare => {
break :x !networkShareServersEql(parsed_to.disk_designator, parsed_from.disk_designator);
},
- WindowsPath.Kind.Drive => {
+ .Drive => {
break :x ascii.toUpper(parsed_from.disk_designator[0]) != ascii.toUpper(parsed_to.disk_designator[0]);
},
- else => unreachable,
+ .None => {
+ break :x false;
+ },
}
};
@@ -1194,13 +1165,6 @@ pub fn relativePosix(allocator: Allocator, from: []const u8, to: []const u8) ![]
}
test "relative" {
- if (builtin.target.cpu.arch == .aarch64) {
- // TODO https://github.com/ziglang/zig/issues/3288
- return error.SkipZigTest;
- }
- if (native_os == .wasi and builtin.link_libc) return error.SkipZigTest;
- if (native_os == .wasi and !builtin.link_libc) try os.initPreopensWasi(std.heap.page_allocator, "/");
-
try testRelativeWindows("c:/blah\\blah", "d:/games", "D:\\games");
try testRelativeWindows("c:/aaaa/bbbb", "c:/aaaa", "..");
try testRelativeWindows("c:/aaaa/bbbb", "c:/cccc", "..\\..\\cccc");
@@ -1226,6 +1190,10 @@ test "relative" {
try testRelativeWindows("C:\\baz", "\\\\foo\\bar\\baz", "\\\\foo\\bar\\baz");
try testRelativeWindows("\\\\foo\\bar\\baz", "C:\\baz", "C:\\baz");
+ try testRelativeWindows("a/b/c", "a\\b", "..");
+ try testRelativeWindows("a/b/c", "a", "..\\..");
+ try testRelativeWindows("a/b/c", "a\\b\\c\\d", "d");
+
try testRelativePosix("/var/lib", "/var", "..");
try testRelativePosix("/var/lib", "/bin", "../../bin");
try testRelativePosix("/var/lib", "/var/lib", "");
@@ -1243,13 +1211,13 @@ test "relative" {
fn testRelativePosix(from: []const u8, to: []const u8, expected_output: []const u8) !void {
const result = try relativePosix(testing.allocator, from, to);
defer testing.allocator.free(result);
- try testing.expectEqualSlices(u8, expected_output, result);
+ try testing.expectEqualStrings(expected_output, result);
}
fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []const u8) !void {
const result = try relativeWindows(testing.allocator, from, to);
defer testing.allocator.free(result);
- try testing.expectEqualSlices(u8, expected_output, result);
+ try testing.expectEqualStrings(expected_output, result);
}
/// Returns the extension of the file name (if any).
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index f6168054b6..00e42b6417 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -1095,7 +1095,9 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
const allocator = testing.allocator;
- const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
+ const cwd = try std.process.getCwdAlloc(allocator);
+ defer allocator.free(cwd);
+ const file_paths: [2][]const u8 = .{ cwd, "zig-test-absolute-paths.txt" };
const filename = try fs.path.resolve(allocator, &file_paths);
defer allocator.free(filename);
diff --git a/lib/std/fs/wasi.zig b/lib/std/fs/wasi.zig
index 2051215dfe..6358873ede 100644
--- a/lib/std/fs/wasi.zig
+++ b/lib/std/fs/wasi.zig
@@ -202,10 +202,7 @@ pub const PreopenList = struct {
// POSIX paths, relative to "/" or `cwd_root` depending on whether they start with "."
const path = if (cwd_root) |cwd| blk: {
const resolve_paths: []const []const u8 = if (raw_path[0] == '.') &.{ cwd, raw_path } else &.{ "/", raw_path };
- break :blk fs.path.resolve(self.buffer.allocator, resolve_paths) catch |err| switch (err) {
- error.CurrentWorkingDirectoryUnlinked => unreachable, // root is absolute, so CWD not queried
- else => |e| return e,
- };
+ break :blk try fs.path.resolve(self.buffer.allocator, resolve_paths);
} else blk: {
// If we were provided no CWD root, we preserve the preopen dir without resolving
break :blk try self.buffer.allocator.dupe(u8, raw_path);
diff --git a/lib/std/target.zig b/lib/std/target.zig
index 745be7dbff..49a7bd1c7d 100644
--- a/lib/std/target.zig
+++ b/lib/std/target.zig
@@ -1179,10 +1179,12 @@ pub const Target = struct {
/// Returns whether this architecture supports the address space
pub fn supportsAddressSpace(arch: Arch, address_space: std.builtin.AddressSpace) bool {
const is_nvptx = arch == .nvptx or arch == .nvptx64;
+ const is_spirv = arch == .spirv32 or arch == .spirv64;
+ const is_gpu = is_nvptx or is_spirv or arch == .amdgcn;
return switch (address_space) {
.generic => true,
.fs, .gs, .ss => arch == .x86_64 or arch == .x86,
- .global, .constant, .local, .shared => arch == .amdgcn or is_nvptx,
+ .global, .constant, .local, .shared => is_gpu,
.param => is_nvptx,
};
}