aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/glibc/sysdeps/arm/arm-features.h59
-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
-rw-r--r--lib/zig.h152
9 files changed, 584 insertions, 249 deletions
diff --git a/lib/libc/glibc/sysdeps/arm/arm-features.h b/lib/libc/glibc/sysdeps/arm/arm-features.h
new file mode 100644
index 0000000000..80a1e2272b
--- /dev/null
+++ b/lib/libc/glibc/sysdeps/arm/arm-features.h
@@ -0,0 +1,59 @@
+/* Macros to test for CPU features on ARM. Generic ARM version.
+ Copyright (C) 2012-2022 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library. If not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _ARM_ARM_FEATURES_H
+#define _ARM_ARM_FEATURES_H 1
+
+/* An OS-specific arm-features.h file should define ARM_HAVE_VFP to
+ an appropriate expression for testing at runtime whether the VFP
+ hardware is present. We'll then redefine it to a constant if we
+ know at compile time that we can assume VFP. */
+
+#ifndef __SOFTFP__
+/* The compiler is generating VFP instructions, so we're already
+ assuming the hardware exists. */
+# undef ARM_HAVE_VFP
+# define ARM_HAVE_VFP 1
+#endif
+
+/* An OS-specific arm-features.h file may define ARM_ASSUME_NO_IWMMXT
+ to indicate at compile time that iWMMXt hardware is never present
+ at runtime (or that we never care about its state) and so need not
+ be checked for. */
+
+/* A more-specific arm-features.h file may define ARM_ALWAYS_BX to indicate
+ that instructions using pc as a destination register must never be used,
+ so a "bx" (or "blx") instruction is always required. */
+
+/* The log2 of the minimum alignment required for an address that
+ is the target of a computed branch (i.e. a "bx" instruction).
+ A more-specific arm-features.h file may define this to set a more
+ stringent requirement.
+
+ Using this only makes sense for code in ARM mode (where instructions
+ always have a fixed size of four bytes), or for Thumb-mode code that is
+ specifically aligning all the related branch targets to match (since
+ Thumb instructions might be either two or four bytes). */
+#ifndef ARM_BX_ALIGN_LOG2
+# define ARM_BX_ALIGN_LOG2 2
+#endif
+
+/* An OS-specific arm-features.h file may define ARM_NO_INDEX_REGISTER to
+ indicate that the two-register addressing modes must never be used. */
+
+#endif /* arm-features.h */
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,
};
}
diff --git a/lib/zig.h b/lib/zig.h
index b8a8936839..e9666baf81 100644
--- a/lib/zig.h
+++ b/lib/zig.h
@@ -344,6 +344,12 @@ static inline zig_bool zig_addo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_
#endif
}
+static inline void zig_vaddo_u32(zig_u8 *ov, zig_u32 *res, int n,
+ const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_u32(&res[i], lhs[i], rhs[i], bits);
+}
+
zig_extern zig_i32 __addosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow);
static inline zig_bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
@@ -358,6 +364,12 @@ static inline zig_bool zig_addo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_
return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits);
}
+static inline void zig_vaddo_i32(zig_u8 *ov, zig_i32 *res, int n,
+ const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_i32(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
zig_u64 full_res;
@@ -370,6 +382,12 @@ static inline zig_bool zig_addo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_
#endif
}
+static inline void zig_vaddo_u64(zig_u8 *ov, zig_u64 *res, int n,
+ const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_u64(&res[i], lhs[i], rhs[i], bits);
+}
+
zig_extern zig_i64 __addodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow);
static inline zig_bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
@@ -384,6 +402,12 @@ static inline zig_bool zig_addo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_
return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits);
}
+static inline void zig_vaddo_i64(zig_u8 *ov, zig_i64 *res, int n,
+ const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_i64(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
zig_u8 full_res;
@@ -395,6 +419,12 @@ static inline zig_bool zig_addo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 b
#endif
}
+static inline void zig_vaddo_u8(zig_u8 *ov, zig_u8 *res, int n,
+ const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_u8(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
zig_i8 full_res;
@@ -406,6 +436,12 @@ static inline zig_bool zig_addo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 b
#endif
}
+static inline void zig_vaddo_i8(zig_u8 *ov, zig_i8 *res, int n,
+ const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_i8(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
zig_u16 full_res;
@@ -417,6 +453,12 @@ static inline zig_bool zig_addo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_
#endif
}
+static inline void zig_vaddo_u16(zig_u8 *ov, zig_u16 *res, int n,
+ const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_u16(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) {
#if zig_has_builtin(add_overflow)
zig_i16 full_res;
@@ -428,6 +470,12 @@ static inline zig_bool zig_addo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_
#endif
}
+static inline void zig_vaddo_i16(zig_u8 *ov, zig_i16 *res, int n,
+ const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_addo_i16(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
zig_u32 full_res;
@@ -440,6 +488,12 @@ static inline zig_bool zig_subo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_
#endif
}
+static inline void zig_vsubo_u32(zig_u8 *ov, zig_u32 *res, int n,
+ const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_u32(&res[i], lhs[i], rhs[i], bits);
+}
+
zig_extern zig_i32 __subosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow);
static inline zig_bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
@@ -454,6 +508,12 @@ static inline zig_bool zig_subo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_
return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits);
}
+static inline void zig_vsubo_i32(zig_u8 *ov, zig_i32 *res, int n,
+ const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_i32(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
zig_u64 full_res;
@@ -466,6 +526,12 @@ static inline zig_bool zig_subo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_
#endif
}
+static inline void zig_vsubo_u64(zig_u8 *ov, zig_u64 *res, int n,
+ const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_u64(&res[i], lhs[i], rhs[i], bits);
+}
+
zig_extern zig_i64 __subodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow);
static inline zig_bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
@@ -480,6 +546,12 @@ static inline zig_bool zig_subo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_
return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits);
}
+static inline void zig_vsubo_i64(zig_u8 *ov, zig_i64 *res, int n,
+ const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_i64(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
zig_u8 full_res;
@@ -491,6 +563,12 @@ static inline zig_bool zig_subo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 b
#endif
}
+static inline void zig_vsubo_u8(zig_u8 *ov, zig_u8 *res, int n,
+ const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_u8(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
zig_i8 full_res;
@@ -502,6 +580,13 @@ static inline zig_bool zig_subo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 b
#endif
}
+static inline void zig_vsubo_i8(zig_u8 *ov, zig_i8 *res, int n,
+ const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_i8(&res[i], lhs[i], rhs[i], bits);
+}
+
+
static inline zig_bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
zig_u16 full_res;
@@ -513,6 +598,13 @@ static inline zig_bool zig_subo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_
#endif
}
+static inline void zig_vsubo_u16(zig_u8 *ov, zig_u16 *res, int n,
+ const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_u16(&res[i], lhs[i], rhs[i], bits);
+}
+
+
static inline zig_bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) {
#if zig_has_builtin(sub_overflow)
zig_i16 full_res;
@@ -524,6 +616,12 @@ static inline zig_bool zig_subo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_
#endif
}
+static inline void zig_vsubo_i16(zig_u8 *ov, zig_i16 *res, int n,
+ const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_subo_i16(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
zig_u32 full_res;
@@ -536,6 +634,12 @@ static inline zig_bool zig_mulo_u32(zig_u32 *res, zig_u32 lhs, zig_u32 rhs, zig_
#endif
}
+static inline void zig_vmulo_u32(zig_u8 *ov, zig_u32 *res, int n,
+ const zig_u32 *lhs, const zig_u32 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u32(&res[i], lhs[i], rhs[i], bits);
+}
+
zig_extern zig_i32 __mulosi4(zig_i32 lhs, zig_i32 rhs, zig_c_int *overflow);
static inline zig_bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
@@ -550,6 +654,12 @@ static inline zig_bool zig_mulo_i32(zig_i32 *res, zig_i32 lhs, zig_i32 rhs, zig_
return overflow || full_res < zig_minInt(i32, bits) || full_res > zig_maxInt(i32, bits);
}
+static inline void zig_vmulo_i32(zig_u8 *ov, zig_i32 *res, int n,
+ const zig_i32 *lhs, const zig_i32 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i32(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
zig_u64 full_res;
@@ -562,6 +672,12 @@ static inline zig_bool zig_mulo_u64(zig_u64 *res, zig_u64 lhs, zig_u64 rhs, zig_
#endif
}
+static inline void zig_vmulo_u64(zig_u8 *ov, zig_u64 *res, int n,
+ const zig_u64 *lhs, const zig_u64 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u64(&res[i], lhs[i], rhs[i], bits);
+}
+
zig_extern zig_i64 __mulodi4(zig_i64 lhs, zig_i64 rhs, zig_c_int *overflow);
static inline zig_bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
@@ -576,6 +692,12 @@ static inline zig_bool zig_mulo_i64(zig_i64 *res, zig_i64 lhs, zig_i64 rhs, zig_
return overflow || full_res < zig_minInt(i64, bits) || full_res > zig_maxInt(i64, bits);
}
+static inline void zig_vmulo_i64(zig_u8 *ov, zig_i64 *res, int n,
+ const zig_i64 *lhs, const zig_i64 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i64(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
zig_u8 full_res;
@@ -587,6 +709,12 @@ static inline zig_bool zig_mulo_u8(zig_u8 *res, zig_u8 lhs, zig_u8 rhs, zig_u8 b
#endif
}
+static inline void zig_vmulo_u8(zig_u8 *ov, zig_u8 *res, int n,
+ const zig_u8 *lhs, const zig_u8 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u8(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
zig_i8 full_res;
@@ -598,6 +726,12 @@ static inline zig_bool zig_mulo_i8(zig_i8 *res, zig_i8 lhs, zig_i8 rhs, zig_u8 b
#endif
}
+static inline void zig_vmulo_i8(zig_u8 *ov, zig_i8 *res, int n,
+ const zig_i8 *lhs, const zig_i8 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i8(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
zig_u16 full_res;
@@ -609,6 +743,12 @@ static inline zig_bool zig_mulo_u16(zig_u16 *res, zig_u16 lhs, zig_u16 rhs, zig_
#endif
}
+static inline void zig_vmulo_u16(zig_u8 *ov, zig_u16 *res, int n,
+ const zig_u16 *lhs, const zig_u16 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_u16(&res[i], lhs[i], rhs[i], bits);
+}
+
static inline zig_bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_u8 bits) {
#if zig_has_builtin(mul_overflow)
zig_i16 full_res;
@@ -620,6 +760,12 @@ static inline zig_bool zig_mulo_i16(zig_i16 *res, zig_i16 lhs, zig_i16 rhs, zig_
#endif
}
+static inline void zig_vmulo_i16(zig_u8 *ov, zig_i16 *res, int n,
+ const zig_i16 *lhs, const zig_i16 *rhs, zig_u8 bits)
+{
+ for (int i = 0; i < n; ++i) ov[i] = zig_mulo_i16(&res[i], lhs[i], rhs[i], bits);
+}
+
#define zig_int_builtins(w) \
static inline zig_u##w zig_shlw_u##w(zig_u##w lhs, zig_u8 rhs, zig_u8 bits) { \
return zig_wrap_u##w(zig_shl_u##w(lhs, rhs), bits); \
@@ -846,10 +992,8 @@ static inline zig_u8 zig_bit_reverse_u8(zig_u8 val, zig_u8 bits) {
full_res = __builtin_bitreverse8(val);
#else
static zig_u8 const lut[0x10] = {
- 0b0000, 0b1000, 0b0100, 0b1100,
- 0b0010, 0b1010, 0b0110, 0b1110,
- 0b0001, 0b1001, 0b0101, 0b1101,
- 0b0011, 0b1011, 0b0111, 0b1111,
+ 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe,
+ 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf
};
full_res = lut[val >> 0 & 0xF] << 4 | lut[val >> 4 & 0xF] << 0;
#endif