aboutsummaryrefslogtreecommitdiff
path: root/std
diff options
context:
space:
mode:
authorhryx <codroid@gmail.com>2019-05-12 02:00:49 -0700
committerhryx <codroid@gmail.com>2019-05-12 02:00:49 -0700
commit3787f3428625e830fd852a8f5a40c7d8a2d429f6 (patch)
tree23fb493b9d2f07c7abe57955874682959936319a /std
parent16aee1f58a80295f7599a8290d764a5c7040c373 (diff)
parentedcc7c72d1a684a8a16ca23ad26689f2cce4e803 (diff)
downloadzig-3787f3428625e830fd852a8f5a40c7d8a2d429f6.tar.gz
zig-3787f3428625e830fd852a8f5a40c7d8a2d429f6.zip
Merge branch 'master' into rebased
Diffstat (limited to 'std')
-rw-r--r--std/array_list.zig38
-rw-r--r--std/build.zig27
-rw-r--r--std/c.zig6
-rw-r--r--std/c/freebsd.zig32
-rw-r--r--std/c/linux.zig4
-rw-r--r--std/c/netbsd.zig32
-rw-r--r--std/crypto/chacha20.zig5
-rw-r--r--std/debug.zig630
-rw-r--r--std/debug/failing_allocator.zig18
-rw-r--r--std/debug/leb128.zig228
-rw-r--r--std/dwarf.zig4
-rw-r--r--std/dynamic_library.zig83
-rw-r--r--std/elf.zig5
-rw-r--r--std/fmt.zig108
-rw-r--r--std/hash_map.zig83
-rw-r--r--std/heap.zig478
-rw-r--r--std/io.zig41
-rw-r--r--std/io/c_out_stream.zig48
-rw-r--r--std/io/seekable_stream.zig16
-rw-r--r--std/io/test.zig (renamed from std/io_test.zig)13
-rw-r--r--std/json.zig4
-rw-r--r--std/json/test.zig1904
-rw-r--r--std/json_test.zig1904
-rw-r--r--std/math/acos.zig10
-rw-r--r--std/math/acosh.zig12
-rw-r--r--std/math/asin.zig12
-rw-r--r--std/math/asinh.zig14
-rw-r--r--std/math/atan.zig12
-rw-r--r--std/math/atan2.zig42
-rw-r--r--std/math/atanh.zig14
-rw-r--r--std/math/big.zig2
-rw-r--r--std/math/big/int.zig786
-rw-r--r--std/math/big/rational.zig938
-rw-r--r--std/math/cbrt.zig14
-rw-r--r--std/math/ceil.zig14
-rw-r--r--std/math/complex.zig12
-rw-r--r--std/math/complex/abs.zig1
-rw-r--r--std/math/complex/acos.zig1
-rw-r--r--std/math/complex/acosh.zig1
-rw-r--r--std/math/complex/arg.zig1
-rw-r--r--std/math/complex/asin.zig1
-rw-r--r--std/math/complex/asinh.zig1
-rw-r--r--std/math/complex/atan.zig7
-rw-r--r--std/math/complex/atanh.zig1
-rw-r--r--std/math/complex/conj.zig1
-rw-r--r--std/math/complex/cos.zig1
-rw-r--r--std/math/complex/cosh.zig7
-rw-r--r--std/math/complex/exp.zig7
-rw-r--r--std/math/complex/ldexp.zig7
-rw-r--r--std/math/complex/log.zig1
-rw-r--r--std/math/complex/pow.zig1
-rw-r--r--std/math/complex/proj.zig1
-rw-r--r--std/math/complex/sin.zig1
-rw-r--r--std/math/complex/sinh.zig7
-rw-r--r--std/math/complex/sqrt.zig8
-rw-r--r--std/math/complex/tan.zig1
-rw-r--r--std/math/complex/tanh.zig7
-rw-r--r--std/math/copysign.zig7
-rw-r--r--std/math/cos.zig146
-rw-r--r--std/math/cosh.zig14
-rw-r--r--std/math/exp.zig12
-rw-r--r--std/math/exp2.zig12
-rw-r--r--std/math/expm1.zig17
-rw-r--r--std/math/expo2.zig7
-rw-r--r--std/math/fabs.zig12
-rw-r--r--std/math/floor.zig14
-rw-r--r--std/math/fma.zig10
-rw-r--r--std/math/frexp.zig15
-rw-r--r--std/math/hypot.zig16
-rw-r--r--std/math/ilogb.zig14
-rw-r--r--std/math/inf.zig1
-rw-r--r--std/math/isfinite.zig1
-rw-r--r--std/math/isinf.zig3
-rw-r--r--std/math/isnan.zig6
-rw-r--r--std/math/isnormal.zig1
-rw-r--r--std/math/ln.zig16
-rw-r--r--std/math/log.zig7
-rw-r--r--std/math/log10.zig16
-rw-r--r--std/math/log1p.zig18
-rw-r--r--std/math/log2.zig16
-rw-r--r--std/math/modf.zig13
-rw-r--r--std/math/nan.zig6
-rw-r--r--std/math/pow.zig96
-rw-r--r--std/math/powi.zig22
-rw-r--r--std/math/round.zig14
-rw-r--r--std/math/scalbn.zig7
-rw-r--r--std/math/signbit.zig1
-rw-r--r--std/math/sin.zig160
-rw-r--r--std/math/sinh.zig14
-rw-r--r--std/math/sqrt.zig14
-rw-r--r--std/math/tan.zig154
-rw-r--r--std/math/tanh.zig14
-rw-r--r--std/math/trunc.zig14
-rw-r--r--std/mem.zig52
-rw-r--r--std/os.zig118
-rw-r--r--std/os/darwin.zig2
-rw-r--r--std/os/file.zig28
-rw-r--r--std/os/linux.zig166
-rw-r--r--std/os/linux/arm64.zig19
-rw-r--r--std/os/linux/test.zig41
-rw-r--r--std/os/linux/tls.zig247
-rw-r--r--std/os/linux/x86_64.zig25
-rw-r--r--std/os/time.zig30
-rw-r--r--std/os/wasi.zig42
-rw-r--r--std/os/wasi/core.zig374
-rw-r--r--std/os/windows.zig31
-rw-r--r--std/os/windows/kernel32.zig3
-rw-r--r--std/packed_int_array.zig649
-rw-r--r--std/pdb.zig13
-rw-r--r--std/rand.zig4
-rw-r--r--std/special/bootstrap.zig74
-rw-r--r--std/special/build_runner.zig30
-rw-r--r--std/special/compiler_rt.zig687
-rw-r--r--std/special/compiler_rt/addXf3.zig4
-rw-r--r--std/special/compiler_rt/arm/aeabi_dcmp.zig108
-rw-r--r--std/special/compiler_rt/arm/aeabi_fcmp.zig108
-rw-r--r--std/special/compiler_rt/ashlti3.zig41
-rw-r--r--std/special/compiler_rt/ashlti3_test.zig46
-rw-r--r--std/special/compiler_rt/ashrti3.zig42
-rw-r--r--std/special/compiler_rt/ashrti3_test.zig58
-rw-r--r--std/special/compiler_rt/comparedf2.zig122
-rw-r--r--std/special/compiler_rt/comparedf2_test.zig101
-rw-r--r--std/special/compiler_rt/comparesf2.zig122
-rw-r--r--std/special/compiler_rt/comparesf2_test.zig101
-rw-r--r--std/special/compiler_rt/divti3.zig6
-rw-r--r--std/special/compiler_rt/extendXfYf2.zig14
-rw-r--r--std/special/compiler_rt/floatdidf.zig22
-rw-r--r--std/special/compiler_rt/floatdidf_test.zig53
-rw-r--r--std/special/compiler_rt/floatsiXf.zig109
-rw-r--r--std/special/compiler_rt/floatundidf.zig24
-rw-r--r--std/special/compiler_rt/floatundidf_test.zig50
-rw-r--r--std/special/compiler_rt/floatunsidf.zig33
-rw-r--r--std/special/compiler_rt/lshrti3.zig41
-rw-r--r--std/special/compiler_rt/lshrti3_test.zig46
-rw-r--r--std/special/compiler_rt/modti3.zig6
-rw-r--r--std/special/compiler_rt/mulodi4.zig44
-rw-r--r--std/special/compiler_rt/mulodi4_test.zig85
-rw-r--r--std/special/compiler_rt/muloti4.zig10
-rw-r--r--std/special/compiler_rt/multi3.zig6
-rw-r--r--std/special/compiler_rt/stack_probe.zig206
-rw-r--r--std/special/compiler_rt/truncXfYf2.zig4
-rw-r--r--std/special/compiler_rt/truncXfYf2_test.zig37
-rw-r--r--std/special/compiler_rt/udivmodti4.zig5
-rw-r--r--std/special/compiler_rt/udivti3.zig5
-rw-r--r--std/special/compiler_rt/umodti3.zig6
-rw-r--r--std/special/fmt_runner.zig260
-rw-r--r--std/special/panic.zig7
-rw-r--r--std/special/test_runner.zig2
-rw-r--r--std/std.zig7
-rw-r--r--std/zig/ast.zig9
-rw-r--r--std/zig/parse.zig338
-rw-r--r--std/zig/parser_test.zig36
-rw-r--r--std/zig/render.zig7
153 files changed, 9635 insertions, 3898 deletions
diff --git a/std/array_list.zig b/std/array_list.zig
index b173dab747..ca7d5f911e 100644
--- a/std/array_list.zig
+++ b/std/array_list.zig
@@ -111,6 +111,17 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
new_item_ptr.* = item;
}
+ pub fn orderedRemove(self: *Self, i: usize) T {
+ const newlen = self.len - 1;
+ if (newlen == i) return self.pop();
+
+ const old_item = self.at(i);
+ for (self.items[i..newlen]) |*b, j| b.* = self.items[i + 1 + j];
+ self.items[newlen] = undefined;
+ self.len = newlen;
+ return old_item;
+ }
+
/// Removes the element at the specified index and returns it.
/// The empty slot is filled from the end of the list.
pub fn swapRemove(self: *Self, i: usize) T {
@@ -279,6 +290,33 @@ test "std.ArrayList.basic" {
testing.expect(list.pop() == 33);
}
+test "std.ArrayList.orderedRemove" {
+ var list = ArrayList(i32).init(debug.global_allocator);
+ defer list.deinit();
+
+ try list.append(1);
+ try list.append(2);
+ try list.append(3);
+ try list.append(4);
+ try list.append(5);
+ try list.append(6);
+ try list.append(7);
+
+ //remove from middle
+ testing.expectEqual(i32(4), list.orderedRemove(3));
+ testing.expectEqual(i32(5), list.at(3));
+ testing.expectEqual(usize(6), list.len);
+
+ //remove from end
+ testing.expectEqual(i32(7), list.orderedRemove(5));
+ testing.expectEqual(usize(5), list.len);
+
+ //remove from front
+ testing.expectEqual(i32(1), list.orderedRemove(0));
+ testing.expectEqual(i32(2), list.at(0));
+ testing.expectEqual(usize(4), list.len);
+}
+
test "std.ArrayList.swapRemove" {
var list = ArrayList(i32).init(debug.global_allocator);
defer list.deinit();
diff --git a/std/build.zig b/std/build.zig
index 2bd4a9b08f..b5ec97ab5f 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -50,6 +50,8 @@ pub const Builder = struct {
build_root: []const u8,
cache_root: []const u8,
release_mode: ?builtin.Mode,
+ override_std_dir: ?[]const u8,
+ override_lib_dir: ?[]const u8,
pub const CStd = enum {
C89,
@@ -133,6 +135,8 @@ pub const Builder = struct {
},
.have_install_step = false,
.release_mode = null,
+ .override_std_dir = null,
+ .override_lib_dir = null,
};
self.detectNativeSystemPaths();
self.default_step = self.step("default", "Build the project");
@@ -937,8 +941,11 @@ pub const LibExeObjStep = struct {
verbose_link: bool,
verbose_cc: bool,
disable_gen_h: bool,
+ bundle_compiler_rt: bool,
+ disable_stack_probing: bool,
c_std: Builder.CStd,
override_std_dir: ?[]const u8,
+ override_lib_dir: ?[]const u8,
main_pkg_path: ?[]const u8,
exec_cmd_args: ?[]const ?[]const u8,
name_prefix: []const u8,
@@ -1039,11 +1046,14 @@ pub const LibExeObjStep = struct {
.c_std = Builder.CStd.C99,
.system_linker_hack = false,
.override_std_dir = null,
+ .override_lib_dir = null,
.main_pkg_path = null,
.exec_cmd_args = null,
.name_prefix = "",
.filter = null,
.disable_gen_h = false,
+ .bundle_compiler_rt = false,
+ .disable_stack_probing = false,
.output_dir = null,
.need_system_paths = false,
.single_threaded = false,
@@ -1446,6 +1456,12 @@ pub const LibExeObjStep = struct {
if (self.disable_gen_h) {
try zig_args.append("--disable-gen-h");
}
+ if (self.bundle_compiler_rt) {
+ try zig_args.append("--bundle-compiler-rt");
+ }
+ if (self.disable_stack_probing) {
+ try zig_args.append("--disable-stack-probing");
+ }
switch (self.target) {
Target.Native => {},
@@ -1528,6 +1544,17 @@ pub const LibExeObjStep = struct {
if (self.override_std_dir) |dir| {
try zig_args.append("--override-std-dir");
try zig_args.append(builder.pathFromRoot(dir));
+ } else if (self.builder.override_std_dir) |dir| {
+ try zig_args.append("--override-std-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ }
+
+ if (self.override_lib_dir) |dir| {
+ try zig_args.append("--override-lib-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
+ } else if (self.builder.override_lib_dir) |dir| {
+ try zig_args.append("--override-lib-dir");
+ try zig_args.append(builder.pathFromRoot(dir));
}
if (self.main_pkg_path) |dir| {
diff --git a/std/c.zig b/std/c.zig
index acff9229d1..db28105eec 100644
--- a/std/c.zig
+++ b/std/c.zig
@@ -12,6 +12,12 @@ pub use switch (builtin.os) {
// TODO https://github.com/ziglang/zig/issues/265 on this whole file
+pub const FILE = @OpaqueType();
+pub extern "c" fn fopen(filename: [*]const u8, modes: [*]const u8) ?*FILE;
+pub extern "c" fn fclose(stream: *FILE) c_int;
+pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
+pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
+
pub extern "c" fn abort() noreturn;
pub extern "c" fn exit(code: c_int) noreturn;
pub extern "c" fn isatty(fd: c_int) c_int;
diff --git a/std/c/freebsd.zig b/std/c/freebsd.zig
index 2f2f4c0a1b..b6cf15bf31 100644
--- a/std/c/freebsd.zig
+++ b/std/c/freebsd.zig
@@ -42,14 +42,36 @@ pub const pthread_attr_t = extern struct {
};
pub const msghdr = extern struct {
- msg_name: *u8,
+ /// optional address
+ msg_name: ?*sockaddr,
+ /// size of address
msg_namelen: socklen_t,
- msg_iov: *iovec,
+ /// scatter/gather array
+ msg_iov: [*]iovec,
+ /// # elements in msg_iov
msg_iovlen: i32,
- __pad1: i32,
- msg_control: *u8,
+ /// ancillary data
+ msg_control: ?*c_void,
+ /// ancillary data buffer len
msg_controllen: socklen_t,
- __pad2: socklen_t,
+ /// flags on received message
+ msg_flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+ /// optional address
+ msg_name: ?*const sockaddr,
+ /// size of address
+ msg_namelen: socklen_t,
+ /// scatter/gather array
+ msg_iov: [*]iovec_const,
+ /// # elements in msg_iov
+ msg_iovlen: i32,
+ /// ancillary data
+ msg_control: ?*c_void,
+ /// ancillary data buffer len
+ msg_controllen: socklen_t,
+ /// flags on received message
msg_flags: i32,
};
diff --git a/std/c/linux.zig b/std/c/linux.zig
index b0dadf071d..48e359f361 100644
--- a/std/c/linux.zig
+++ b/std/c/linux.zig
@@ -1,3 +1,4 @@
+const linux = @import("../os/linux.zig");
pub use @import("../os/linux/errno.zig");
pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;
@@ -11,3 +12,6 @@ pub const pthread_attr_t = extern struct {
/// See std.elf for constants for this
pub extern fn getauxval(__type: c_ulong) c_ulong;
+
+pub const dl_iterate_phdr_callback = extern fn (info: *linux.dl_phdr_info, size: usize, data: ?*c_void) c_int;
+pub extern fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
diff --git a/std/c/netbsd.zig b/std/c/netbsd.zig
index 796d45dafc..2c07ee296b 100644
--- a/std/c/netbsd.zig
+++ b/std/c/netbsd.zig
@@ -42,14 +42,36 @@ pub const pthread_attr_t = extern struct {
};
pub const msghdr = extern struct {
- msg_name: *u8,
+ /// optional address
+ msg_name: ?*sockaddr,
+ /// size of address
msg_namelen: socklen_t,
- msg_iov: *iovec,
+ /// scatter/gather array
+ msg_iov: [*]iovec,
+ /// # elements in msg_iov
msg_iovlen: i32,
- __pad1: i32,
- msg_control: *u8,
+ /// ancillary data
+ msg_control: ?*c_void,
+ /// ancillary data buffer len
msg_controllen: socklen_t,
- __pad2: socklen_t,
+ /// flags on received message
+ msg_flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+ /// optional address
+ msg_name: ?*const sockaddr,
+ /// size of address
+ msg_namelen: socklen_t,
+ /// scatter/gather array
+ msg_iov: [*]iovec_const,
+ /// # elements in msg_iov
+ msg_iovlen: i32,
+ /// ancillary data
+ msg_control: ?*c_void,
+ /// ancillary data buffer len
+ msg_controllen: socklen_t,
+ /// flags on received message
msg_flags: i32,
};
diff --git a/std/crypto/chacha20.zig b/std/crypto/chacha20.zig
index 78ef700bdf..796b5c6e4f 100644
--- a/std/crypto/chacha20.zig
+++ b/std/crypto/chacha20.zig
@@ -142,7 +142,7 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
assert(in.len >= out.len);
assert(counter +% (in.len >> 6) >= counter);
- var cursor: u64 = 0;
+ var cursor: usize = 0;
var k: [8]u32 = undefined;
var c: [4]u32 = undefined;
@@ -161,7 +161,8 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
c[3] = mem.readIntSliceLittle(u32, nonce[4..8]);
const block_size = (1 << 6);
- const big_block = (block_size << 32);
+ // The full block size is greater than the address space on a 32bit machine
+ const big_block = if (@sizeOf(usize) > 4) (block_size << 32) else maxInt(usize);
// first partial big block
if (((@intCast(u64, maxInt(u32) - @truncate(u32, counter)) + 1) << 6) < in.len) {
diff --git a/std/debug.zig b/std/debug.zig
index c85a982059..d017f96144 100644
--- a/std/debug.zig
+++ b/std/debug.zig
@@ -13,6 +13,8 @@ const ArrayList = std.ArrayList;
const builtin = @import("builtin");
const maxInt = std.math.maxInt;
+const leb = @import("debug/leb128.zig");
+
pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAllocator;
pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator;
@@ -214,14 +216,14 @@ pub fn writeStackTrace(
tty_color: bool,
) !void {
var frame_index: usize = 0;
- var frames_left: usize = stack_trace.index;
+ var frames_left: usize = std.math.min(stack_trace.index, stack_trace.instruction_addresses.len);
while (frames_left != 0) : ({
frames_left -= 1;
frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len;
}) {
const return_address = stack_trace.instruction_addresses[frame_index];
- try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
+ try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color);
}
}
@@ -263,7 +265,7 @@ pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color
}
var it = StackIterator.init(start_addr);
while (it.next()) |return_address| {
- try printSourceAtAddress(debug_info, out_stream, return_address, tty_color);
+ try printSourceAtAddress(debug_info, out_stream, return_address - 1, tty_color);
}
}
@@ -376,7 +378,6 @@ fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_addres
// There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
// from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
// breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
-
const subsection_end_index = sect_offset + subsect_hdr.Length;
while (line_index < subsection_end_index) {
@@ -690,9 +691,9 @@ pub fn printSourceAtAddressDwarf(
return;
};
const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name);
- if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| {
+ if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address)) |line_info| {
defer line_info.deinit();
- const symbol_name = "???";
+ const symbol_name = getSymbolNameDwarf(debug_info, address) orelse "???";
try printLineInfo(
out_stream,
line_info,
@@ -969,6 +970,8 @@ fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Sec
pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
+ di.func_list = ArrayList(Func).init(allocator);
+ try scanAllFunctions(di);
try scanAllCompileUnits(di);
}
@@ -992,6 +995,7 @@ pub fn openElfDebugInfo(
.debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")),
.abbrev_table_list = undefined,
.compile_unit_list = undefined,
+ .func_list = undefined,
};
try openDwarfDebugInfo(&di, allocator);
return di;
@@ -1162,6 +1166,7 @@ pub const DwarfInfo = struct {
debug_ranges: ?Section,
abbrev_table_list: ArrayList(AbbrevTableHeader),
compile_unit_list: ArrayList(CompileUnit),
+ func_list: ArrayList(Func),
pub const Section = struct {
offset: usize,
@@ -1178,7 +1183,7 @@ pub const DwarfInfo = struct {
};
pub const DebugInfo = switch (builtin.os) {
- builtin.Os.macosx => struct {
+ builtin.Os.macosx, builtin.Os.ios => struct {
symbols: []const MachoSymbol,
strings: []const u8,
ofiles: OFileTable,
@@ -1213,7 +1218,6 @@ const CompileUnit = struct {
version: u16,
is_64: bool,
die: *Die,
- index: usize,
pc_range: ?PcRange,
};
@@ -1244,21 +1248,19 @@ const FormValue = union(enum) {
ExprLoc: []u8,
Flag: bool,
SecOffset: u64,
- Ref: []u8,
+ Ref: u64,
RefAddr: u64,
- RefSig8: u64,
String: []u8,
StrPtr: u64,
};
const Constant = struct {
- payload: []u8,
+ payload: u64,
signed: bool,
fn asUnsignedLe(self: *const Constant) !u64 {
- if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo;
if (self.signed) return error.InvalidDebugInfo;
- return mem.readVarInt(u64, self.payload, builtin.Endian.Little);
+ return self.payload;
}
};
@@ -1304,6 +1306,14 @@ const Die = struct {
};
}
+ fn getAttrRef(self: *const Die, id: u64) !u64 {
+ const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
+ return switch (form_value.*) {
+ FormValue.Ref => |value| value,
+ else => error.InvalidDebugInfo,
+ };
+ }
+
fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) {
@@ -1443,11 +1453,18 @@ fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !
return parseFormValueBlockLen(allocator, in_stream, block_len);
}
-fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue {
+fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
return FormValue{
.Const = Constant{
.signed = signed,
- .payload = try readAllocBytes(allocator, in_stream, size),
+ .payload = switch (size) {
+ 1 => try in_stream.readIntLittle(u8),
+ 2 => try in_stream.readIntLittle(u16),
+ 4 => try in_stream.readIntLittle(u32),
+ 8 => try in_stream.readIntLittle(u64),
+ -1 => if (signed) @bitCast(u64, try leb.readILEB128(i64, in_stream)) else try leb.readULEB128(u64, in_stream),
+ else => @compileError("Invalid size"),
+ },
},
};
}
@@ -1460,14 +1477,17 @@ fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable;
}
-fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
- const buf = try readAllocBytes(allocator, in_stream, size);
- return FormValue{ .Ref = buf };
-}
-
-fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue {
- const block_len = try in_stream.readIntLittle(T);
- return parseFormValueRefLen(allocator, in_stream, block_len);
+fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
+ return FormValue{
+ .Ref = switch (size) {
+ 1 => try in_stream.readIntLittle(u8),
+ 2 => try in_stream.readIntLittle(u16),
+ 4 => try in_stream.readIntLittle(u32),
+ 8 => try in_stream.readIntLittle(u64),
+ -1 => try leb.readULEB128(u64, in_stream),
+ else => unreachable,
+ },
+ };
}
fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
@@ -1477,7 +1497,7 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
DW.FORM_block => x: {
- const block_len = try readULeb128(in_stream);
+ const block_len = try leb.readULEB128(usize, in_stream);
return parseFormValueBlockLen(allocator, in_stream, block_len);
},
DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
@@ -1485,12 +1505,11 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
DW.FORM_udata, DW.FORM_sdata => {
- const block_len = try readULeb128(in_stream);
const signed = form_id == DW.FORM_sdata;
- return parseFormValueConstant(allocator, in_stream, signed, block_len);
+ return parseFormValueConstant(allocator, in_stream, signed, -1);
},
DW.FORM_exprloc => {
- const size = try readULeb128(in_stream);
+ const size = try leb.readULEB128(usize, in_stream);
const buf = try readAllocBytes(allocator, in_stream, size);
return FormValue{ .ExprLoc = buf };
},
@@ -1498,22 +1517,19 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
DW.FORM_flag_present => FormValue{ .Flag = true },
DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
- DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8),
- DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16),
- DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, u32),
- DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64),
- DW.FORM_ref_udata => {
- const ref_len = try readULeb128(in_stream);
- return parseFormValueRefLen(allocator, in_stream, ref_len);
- },
+ DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
+ DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
+ DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
+ DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
+ DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
- DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLittle(u64) },
+ DW.FORM_ref_sig8 => FormValue{ .Ref = try in_stream.readIntLittle(u64) },
DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_indirect => {
- const child_form_id = try readULeb128(in_stream);
+ const child_form_id = try leb.readULEB128(u64, in_stream);
return parseFormValue(allocator, in_stream, child_form_id, is_64);
},
else => error.InvalidDebugInfo,
@@ -1523,19 +1539,19 @@ fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64
fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable {
var result = AbbrevTable.init(di.allocator());
while (true) {
- const abbrev_code = try readULeb128(di.dwarf_in_stream);
+ const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
if (abbrev_code == 0) return result;
try result.append(AbbrevTableEntry{
.abbrev_code = abbrev_code,
- .tag_id = try readULeb128(di.dwarf_in_stream),
+ .tag_id = try leb.readULEB128(u64, di.dwarf_in_stream),
.has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes,
.attrs = ArrayList(AbbrevAttr).init(di.allocator()),
});
const attrs = &result.items[result.len - 1].attrs;
while (true) {
- const attr_id = try readULeb128(di.dwarf_in_stream);
- const form_id = try readULeb128(di.dwarf_in_stream);
+ const attr_id = try leb.readULEB128(u64, di.dwarf_in_stream);
+ const form_id = try leb.readULEB128(u64, di.dwarf_in_stream);
if (attr_id == 0 and form_id == 0) break;
try attrs.append(AbbrevAttr{
.attr_id = attr_id,
@@ -1568,8 +1584,28 @@ fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*con
return null;
}
+fn parseDie1(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
+ const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
+ if (abbrev_code == 0) return null;
+ const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
+
+ var result = Die{
+ .tag_id = table_entry.tag_id,
+ .has_children = table_entry.has_children,
+ .attrs = ArrayList(Die.Attr).init(di.allocator()),
+ };
+ try result.attrs.resize(table_entry.attrs.len);
+ for (table_entry.attrs.toSliceConst()) |attr, i| {
+ result.attrs.items[i] = Die.Attr{
+ .id = attr.attr_id,
+ .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64),
+ };
+ }
+ return result;
+}
+
fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die {
- const abbrev_code = try readULeb128(di.dwarf_in_stream);
+ const abbrev_code = try leb.readULEB128(u64, di.dwarf_in_stream);
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
var result = Die{
@@ -1682,9 +1718,9 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
while (true) {
const file_name = readStringMem(&ptr);
if (file_name.len == 0) break;
- const dir_index = try readULeb128Mem(&ptr);
- const mtime = try readULeb128Mem(&ptr);
- const len_bytes = try readULeb128Mem(&ptr);
+ const dir_index = try leb.readULEB128Mem(usize, &ptr);
+ const mtime = try leb.readULEB128Mem(usize, &ptr);
+ const len_bytes = try leb.readULEB128Mem(usize, &ptr);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
@@ -1698,7 +1734,7 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
const opcode = readByteMem(&ptr);
if (opcode == DW.LNS_extended_op) {
- const op_size = try readULeb128Mem(&ptr);
+ const op_size = try leb.readULEB128Mem(u64, &ptr);
if (op_size < 1) return error.InvalidDebugInfo;
var sub_op = readByteMem(&ptr);
switch (sub_op) {
@@ -1713,9 +1749,9 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
},
DW.LNE_define_file => {
const file_name = readStringMem(&ptr);
- const dir_index = try readULeb128Mem(&ptr);
- const mtime = try readULeb128Mem(&ptr);
- const len_bytes = try readULeb128Mem(&ptr);
+ const dir_index = try leb.readULEB128Mem(usize, &ptr);
+ const mtime = try leb.readULEB128Mem(usize, &ptr);
+ const len_bytes = try leb.readULEB128Mem(usize, &ptr);
try file_entries.append(FileEntry{
.file_name = file_name,
.dir_index = dir_index,
@@ -1743,19 +1779,19 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
prog.basic_block = false;
},
DW.LNS_advance_pc => {
- const arg = try readULeb128Mem(&ptr);
+ const arg = try leb.readULEB128Mem(u64, &ptr);
prog.address += arg * minimum_instruction_length;
},
DW.LNS_advance_line => {
- const arg = try readILeb128Mem(&ptr);
+ const arg = try leb.readILEB128Mem(i64, &ptr);
prog.line += arg;
},
DW.LNS_set_file => {
- const arg = try readULeb128Mem(&ptr);
+ const arg = try leb.readULEB128Mem(u64, &ptr);
prog.file = arg;
},
DW.LNS_set_column => {
- const arg = try readULeb128Mem(&ptr);
+ const arg = try leb.readULEB128Mem(u64, &ptr);
prog.column = arg;
},
DW.LNS_negate_stmt => {
@@ -1787,182 +1823,292 @@ fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: u
fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
+ const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
- const debug_line_end = di.debug_line.offset + di.debug_line.size;
- var this_offset = di.debug_line.offset;
- var this_index: usize = 0;
+ assert(line_info_offset < di.debug_line.size);
- while (this_offset < debug_line_end) : (this_index += 1) {
- try di.dwarf_seekable_stream.seekTo(this_offset);
+ try di.dwarf_seekable_stream.seekTo(di.debug_line.offset + line_info_offset);
- var is_64: bool = undefined;
- const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
- if (unit_length == 0) return error.MissingDebugInfo;
- const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
+ var is_64: bool = undefined;
+ const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
+ if (unit_length == 0) {
+ return error.MissingDebugInfo;
+ }
+ const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
- if (compile_unit.index != this_index) {
- this_offset += next_offset;
- continue;
- }
+ const version = try di.dwarf_in_stream.readInt(u16, di.endian);
+ // TODO support 3 and 5
+ if (version != 2 and version != 4) return error.InvalidDebugInfo;
- const version = try di.dwarf_in_stream.readInt(u16, di.endian);
- // TODO support 3 and 5
- if (version != 2 and version != 4) return error.InvalidDebugInfo;
+ const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
+ const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
+
+ const minimum_instruction_length = try di.dwarf_in_stream.readByte();
+ if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
+
+ if (version >= 4) {
+ // maximum_operations_per_instruction
+ _ = try di.dwarf_in_stream.readByte();
+ }
+
+ const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;
+ const line_base = try di.dwarf_in_stream.readByteSigned();
+
+ const line_range = try di.dwarf_in_stream.readByte();
+ if (line_range == 0) return error.InvalidDebugInfo;
- const prologue_length = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
- const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length;
+ const opcode_base = try di.dwarf_in_stream.readByte();
- const minimum_instruction_length = try di.dwarf_in_stream.readByte();
- if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
+ const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
- if (version >= 4) {
- // maximum_operations_per_instruction
- _ = try di.dwarf_in_stream.readByte();
+ {
+ var i: usize = 0;
+ while (i < opcode_base - 1) : (i += 1) {
+ standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte();
}
+ }
- const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0;
- const line_base = try di.dwarf_in_stream.readByteSigned();
+ var include_directories = ArrayList([]u8).init(di.allocator());
+ try include_directories.append(compile_unit_cwd);
+ while (true) {
+ const dir = try di.readString();
+ if (dir.len == 0) break;
+ try include_directories.append(dir);
+ }
+
+ var file_entries = ArrayList(FileEntry).init(di.allocator());
+ var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
- const line_range = try di.dwarf_in_stream.readByte();
- if (line_range == 0) return error.InvalidDebugInfo;
+ while (true) {
+ const file_name = try di.readString();
+ if (file_name.len == 0) break;
+ const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);
+ const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);
+ const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);
+ try file_entries.append(FileEntry{
+ .file_name = file_name,
+ .dir_index = dir_index,
+ .mtime = mtime,
+ .len_bytes = len_bytes,
+ });
+ }
- const opcode_base = try di.dwarf_in_stream.readByte();
+ try di.dwarf_seekable_stream.seekTo(prog_start_offset);
- const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
+ while (true) {
+ const opcode = try di.dwarf_in_stream.readByte();
- {
- var i: usize = 0;
- while (i < opcode_base - 1) : (i += 1) {
- standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte();
+ if (opcode == DW.LNS_extended_op) {
+ const op_size = try leb.readULEB128(u64, di.dwarf_in_stream);
+ if (op_size < 1) return error.InvalidDebugInfo;
+ var sub_op = try di.dwarf_in_stream.readByte();
+ switch (sub_op) {
+ DW.LNE_end_sequence => {
+ prog.end_sequence = true;
+ if (try prog.checkLineMatch()) |info| return info;
+ return error.MissingDebugInfo;
+ },
+ DW.LNE_set_address => {
+ const addr = try di.dwarf_in_stream.readInt(usize, di.endian);
+ prog.address = addr;
+ },
+ DW.LNE_define_file => {
+ const file_name = try di.readString();
+ const dir_index = try leb.readULEB128(usize, di.dwarf_in_stream);
+ const mtime = try leb.readULEB128(usize, di.dwarf_in_stream);
+ const len_bytes = try leb.readULEB128(usize, di.dwarf_in_stream);
+ try file_entries.append(FileEntry{
+ .file_name = file_name,
+ .dir_index = dir_index,
+ .mtime = mtime,
+ .len_bytes = len_bytes,
+ });
+ },
+ else => {
+ const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
+ try di.dwarf_seekable_stream.seekForward(fwd_amt);
+ },
+ }
+ } else if (opcode >= opcode_base) {
+ // special opcodes
+ const adjusted_opcode = opcode - opcode_base;
+ const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
+ const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
+ prog.line += inc_line;
+ prog.address += inc_addr;
+ if (try prog.checkLineMatch()) |info| return info;
+ prog.basic_block = false;
+ } else {
+ switch (opcode) {
+ DW.LNS_copy => {
+ if (try prog.checkLineMatch()) |info| return info;
+ prog.basic_block = false;
+ },
+ DW.LNS_advance_pc => {
+ const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
+ prog.address += arg * minimum_instruction_length;
+ },
+ DW.LNS_advance_line => {
+ const arg = try leb.readILEB128(i64, di.dwarf_in_stream);
+ prog.line += arg;
+ },
+ DW.LNS_set_file => {
+ const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
+ prog.file = arg;
+ },
+ DW.LNS_set_column => {
+ const arg = try leb.readULEB128(u64, di.dwarf_in_stream);
+ prog.column = arg;
+ },
+ DW.LNS_negate_stmt => {
+ prog.is_stmt = !prog.is_stmt;
+ },
+ DW.LNS_set_basic_block => {
+ prog.basic_block = true;
+ },
+ DW.LNS_const_add_pc => {
+ const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
+ prog.address += inc_addr;
+ },
+ DW.LNS_fixed_advance_pc => {
+ const arg = try di.dwarf_in_stream.readInt(u16, di.endian);
+ prog.address += arg;
+ },
+ DW.LNS_set_prologue_end => {},
+ else => {
+ if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
+ const len_bytes = standard_opcode_lengths[opcode - 1];
+ try di.dwarf_seekable_stream.seekForward(len_bytes);
+ },
}
}
+ }
- var include_directories = ArrayList([]u8).init(di.allocator());
- try include_directories.append(compile_unit_cwd);
- while (true) {
- const dir = try di.readString();
- if (dir.len == 0) break;
- try include_directories.append(dir);
- }
+ return error.MissingDebugInfo;
+}
- var file_entries = ArrayList(FileEntry).init(di.allocator());
- var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
+const Func = struct {
+ pc_range: ?PcRange,
+ name: ?[]u8,
+};
- while (true) {
- const file_name = try di.readString();
- if (file_name.len == 0) break;
- const dir_index = try readULeb128(di.dwarf_in_stream);
- const mtime = try readULeb128(di.dwarf_in_stream);
- const len_bytes = try readULeb128(di.dwarf_in_stream);
- try file_entries.append(FileEntry{
- .file_name = file_name,
- .dir_index = dir_index,
- .mtime = mtime,
- .len_bytes = len_bytes,
- });
+fn getSymbolNameDwarf(di: *DwarfInfo, address: u64) ?[]const u8 {
+ for (di.func_list.toSliceConst()) |*func| {
+ if (func.pc_range) |range| {
+ if (address >= range.start and address < range.end) {
+ return func.name;
+ }
}
+ }
+
+ return null;
+}
- try di.dwarf_seekable_stream.seekTo(prog_start_offset);
+fn scanAllFunctions(di: *DwarfInfo) !void {
+ const debug_info_end = di.debug_info.offset + di.debug_info.size;
+ var this_unit_offset = di.debug_info.offset;
- while (true) {
- const opcode = try di.dwarf_in_stream.readByte();
-
- if (opcode == DW.LNS_extended_op) {
- const op_size = try readULeb128(di.dwarf_in_stream);
- if (op_size < 1) return error.InvalidDebugInfo;
- var sub_op = try di.dwarf_in_stream.readByte();
- switch (sub_op) {
- DW.LNE_end_sequence => {
- prog.end_sequence = true;
- if (try prog.checkLineMatch()) |info| return info;
- return error.MissingDebugInfo;
- },
- DW.LNE_set_address => {
- const addr = try di.dwarf_in_stream.readInt(usize, di.endian);
- prog.address = addr;
- },
- DW.LNE_define_file => {
- const file_name = try di.readString();
- const dir_index = try readULeb128(di.dwarf_in_stream);
- const mtime = try readULeb128(di.dwarf_in_stream);
- const len_bytes = try readULeb128(di.dwarf_in_stream);
- try file_entries.append(FileEntry{
- .file_name = file_name,
- .dir_index = dir_index,
- .mtime = mtime,
- .len_bytes = len_bytes,
- });
- },
- else => {
- const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
- try di.dwarf_seekable_stream.seekForward(fwd_amt);
- },
- }
- } else if (opcode >= opcode_base) {
- // special opcodes
- const adjusted_opcode = opcode - opcode_base;
- const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
- const inc_line = i32(line_base) + i32(adjusted_opcode % line_range);
- prog.line += inc_line;
- prog.address += inc_addr;
- if (try prog.checkLineMatch()) |info| return info;
- prog.basic_block = false;
- } else {
- switch (opcode) {
- DW.LNS_copy => {
- if (try prog.checkLineMatch()) |info| return info;
- prog.basic_block = false;
- },
- DW.LNS_advance_pc => {
- const arg = try readULeb128(di.dwarf_in_stream);
- prog.address += arg * minimum_instruction_length;
- },
- DW.LNS_advance_line => {
- const arg = try readILeb128(di.dwarf_in_stream);
- prog.line += arg;
- },
- DW.LNS_set_file => {
- const arg = try readULeb128(di.dwarf_in_stream);
- prog.file = arg;
- },
- DW.LNS_set_column => {
- const arg = try readULeb128(di.dwarf_in_stream);
- prog.column = arg;
- },
- DW.LNS_negate_stmt => {
- prog.is_stmt = !prog.is_stmt;
- },
- DW.LNS_set_basic_block => {
- prog.basic_block = true;
- },
- DW.LNS_const_add_pc => {
- const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
- prog.address += inc_addr;
- },
- DW.LNS_fixed_advance_pc => {
- const arg = try di.dwarf_in_stream.readInt(u16, di.endian);
- prog.address += arg;
- },
- DW.LNS_set_prologue_end => {},
- else => {
- if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
- const len_bytes = standard_opcode_lengths[opcode - 1];
- try di.dwarf_seekable_stream.seekForward(len_bytes);
- },
- }
+ while (this_unit_offset < debug_info_end) {
+ try di.dwarf_seekable_stream.seekTo(this_unit_offset);
+
+ var is_64: bool = undefined;
+ const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64);
+ if (unit_length == 0) return;
+ const next_offset = unit_length + (if (is_64) usize(12) else usize(4));
+
+ const version = try di.dwarf_in_stream.readInt(u16, di.endian);
+ if (version < 2 or version > 5) return error.InvalidDebugInfo;
+
+ const debug_abbrev_offset = if (is_64) try di.dwarf_in_stream.readInt(u64, di.endian) else try di.dwarf_in_stream.readInt(u32, di.endian);
+
+ const address_size = try di.dwarf_in_stream.readByte();
+ if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
+
+ const compile_unit_pos = try di.dwarf_seekable_stream.getPos();
+ const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset);
+
+ try di.dwarf_seekable_stream.seekTo(compile_unit_pos);
+
+ const next_unit_pos = this_unit_offset + next_offset;
+
+ while ((try di.dwarf_seekable_stream.getPos()) < next_unit_pos) {
+ const die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse continue;
+ const after_die_offset = try di.dwarf_seekable_stream.getPos();
+
+ switch (die_obj.tag_id) {
+ DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
+ const fn_name = x: {
+ var depth: i32 = 3;
+ var this_die_obj = die_obj;
+ // Prenvent endless loops
+ while (depth > 0) : (depth -= 1) {
+ if (this_die_obj.getAttr(DW.AT_name)) |_| {
+ const name = try this_die_obj.getAttrString(di, DW.AT_name);
+ break :x name;
+ } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| {
+ // Follow the DIE it points to and repeat
+ const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
+ if (ref_offset > next_offset) return error.InvalidDebugInfo;
+ try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
+ this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+ } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
+ // Follow the DIE it points to and repeat
+ const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
+ if (ref_offset > next_offset) return error.InvalidDebugInfo;
+ try di.dwarf_seekable_stream.seekTo(this_unit_offset + ref_offset);
+ this_die_obj = (try parseDie1(di, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
+ } else {
+ break :x null;
+ }
+ }
+
+ break :x null;
+ };
+
+ const pc_range = x: {
+ if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| {
+ if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| {
+ const pc_end = switch (high_pc_value.*) {
+ FormValue.Address => |value| value,
+ FormValue.Const => |value| b: {
+ const offset = try value.asUnsignedLe();
+ break :b (low_pc + offset);
+ },
+ else => return error.InvalidDebugInfo,
+ };
+ break :x PcRange{
+ .start = low_pc,
+ .end = pc_end,
+ };
+ } else {
+ break :x null;
+ }
+ } else |err| {
+ if (err != error.MissingDebugInfo) return err;
+ break :x null;
+ }
+ };
+
+ try di.func_list.append(Func{
+ .name = fn_name,
+ .pc_range = pc_range,
+ });
+ },
+ else => {
+ continue;
+ },
}
+
+ try di.dwarf_seekable_stream.seekTo(after_die_offset);
}
- this_offset += next_offset;
+ this_unit_offset += next_offset;
}
-
- return error.MissingDebugInfo;
}
fn scanAllCompileUnits(di: *DwarfInfo) !void {
const debug_info_end = di.debug_info.offset + di.debug_info.size;
var this_unit_offset = di.debug_info.offset;
- var cu_index: usize = 0;
while (this_unit_offset < debug_info_end) {
try di.dwarf_seekable_stream.seekTo(this_unit_offset);
@@ -2019,11 +2165,9 @@ fn scanAllCompileUnits(di: *DwarfInfo) !void {
.is_64 = is_64,
.pc_range = pc_range,
.die = compile_unit_die,
- .index = cu_index,
});
this_unit_offset += next_offset;
- cu_index += 1;
}
}
@@ -2098,52 +2242,6 @@ fn readStringMem(ptr: *[*]const u8) []const u8 {
return result;
}
-fn readULeb128Mem(ptr: *[*]const u8) !u64 {
- var result: u64 = 0;
- var shift: usize = 0;
- var i: usize = 0;
-
- while (true) {
- const byte = ptr.*[i];
- i += 1;
-
- var operand: u64 = undefined;
-
- if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
-
- result |= operand;
-
- if ((byte & 0b10000000) == 0) {
- ptr.* += i;
- return result;
- }
-
- shift += 7;
- }
-}
-fn readILeb128Mem(ptr: *[*]const u8) !i64 {
- var result: i64 = 0;
- var shift: usize = 0;
- var i: usize = 0;
-
- while (true) {
- const byte = ptr.*[i];
- i += 1;
-
- var operand: i64 = undefined;
- if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
-
- result |= operand;
- shift += 7;
-
- if ((byte & 0b10000000) == 0) {
- if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
- ptr.* += i;
- return result;
- }
- }
-}
-
fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
const first_32_bits = try in_stream.readIntLittle(u32);
is_64.* = (first_32_bits == 0xffffffff);
@@ -2155,46 +2253,6 @@ fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool)
}
}
-fn readULeb128(in_stream: var) !u64 {
- var result: u64 = 0;
- var shift: usize = 0;
-
- while (true) {
- const byte = try in_stream.readByte();
-
- var operand: u64 = undefined;
-
- if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
-
- result |= operand;
-
- if ((byte & 0b10000000) == 0) return result;
-
- shift += 7;
- }
-}
-
-fn readILeb128(in_stream: var) !i64 {
- var result: i64 = 0;
- var shift: usize = 0;
-
- while (true) {
- const byte = try in_stream.readByte();
-
- var operand: i64 = undefined;
-
- if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo;
-
- result |= operand;
- shift += 7;
-
- if ((byte & 0b10000000) == 0) {
- if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift));
- return result;
- }
- }
-}
-
/// This should only be used in temporary test programs.
pub const global_allocator = &global_fixed_allocator.allocator;
var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]);
diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig
index 7a89460513..5776d23194 100644
--- a/std/debug/failing_allocator.zig
+++ b/std/debug/failing_allocator.zig
@@ -10,6 +10,7 @@ pub const FailingAllocator = struct {
internal_allocator: *mem.Allocator,
allocated_bytes: usize,
freed_bytes: usize,
+ allocations: usize,
deallocations: usize,
pub fn init(allocator: *mem.Allocator, fail_index: usize) FailingAllocator {
@@ -19,6 +20,7 @@ pub const FailingAllocator = struct {
.index = 0,
.allocated_bytes = 0,
.freed_bytes = 0,
+ .allocations = 0,
.deallocations = 0,
.allocator = mem.Allocator{
.reallocFn = realloc,
@@ -39,19 +41,25 @@ pub const FailingAllocator = struct {
new_size,
new_align,
);
- if (new_size <= old_mem.len) {
+ if (new_size < old_mem.len) {
self.freed_bytes += old_mem.len - new_size;
- } else {
+ if (new_size == 0)
+ self.deallocations += 1;
+ } else if (new_size > old_mem.len) {
self.allocated_bytes += new_size - old_mem.len;
+ if (old_mem.len == 0)
+ self.allocations += 1;
}
- self.deallocations += 1;
self.index += 1;
return result;
}
fn shrink(allocator: *mem.Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
- self.freed_bytes += old_mem.len - new_size;
- return self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
+ const r = self.internal_allocator.shrinkFn(self.internal_allocator, old_mem, old_align, new_size, new_align);
+ self.freed_bytes += old_mem.len - r.len;
+ if (new_size == 0)
+ self.deallocations += 1;
+ return r;
}
};
diff --git a/std/debug/leb128.zig b/std/debug/leb128.zig
new file mode 100644
index 0000000000..2801877839
--- /dev/null
+++ b/std/debug/leb128.zig
@@ -0,0 +1,228 @@
+const std = @import("std");
+const testing = std.testing;
+
+pub fn readULEB128(comptime T: type, in_stream: var) !T {
+ const ShiftT = @IntType(false, std.math.log2(T.bit_count));
+
+ var result: T = 0;
+ var shift: usize = 0;
+
+ while (true) {
+ const byte = try in_stream.readByte();
+
+ if (shift > T.bit_count)
+ return error.Overflow;
+
+ var operand: T = undefined;
+ if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
+ return error.Overflow;
+
+ result |= operand;
+
+ if ((byte & 0x80) == 0)
+ return result;
+
+ shift += 7;
+ }
+}
+
+pub fn readULEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
+ const ShiftT = @IntType(false, std.math.log2(T.bit_count));
+
+ var result: T = 0;
+ var shift: usize = 0;
+ var i: usize = 0;
+
+ while (true) : (i += 1) {
+ const byte = ptr.*[i];
+
+ if (shift > T.bit_count)
+ return error.Overflow;
+
+ var operand: T = undefined;
+ if (@shlWithOverflow(T, byte & 0x7f, @intCast(ShiftT, shift), &operand))
+ return error.Overflow;
+
+ result |= operand;
+
+ if ((byte & 0x80) == 0) {
+ ptr.* += i;
+ return result;
+ }
+
+ shift += 7;
+ }
+}
+
+pub fn readILEB128(comptime T: type, in_stream: var) !T {
+ const UT = @IntType(false, T.bit_count);
+ const ShiftT = @IntType(false, std.math.log2(T.bit_count));
+
+ var result: UT = 0;
+ var shift: usize = 0;
+
+ while (true) {
+ const byte = u8(try in_stream.readByte());
+
+ if (shift > T.bit_count)
+ return error.Overflow;
+
+ var operand: UT = undefined;
+ if (@shlWithOverflow(UT, UT(byte & 0x7f), @intCast(ShiftT, shift), &operand)) {
+ if (byte != 0x7f)
+ return error.Overflow;
+ }
+
+ result |= operand;
+
+ shift += 7;
+
+ if ((byte & 0x80) == 0) {
+ if (shift < T.bit_count and (byte & 0x40) != 0) {
+ result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);
+ }
+ return @bitCast(T, result);
+ }
+ }
+}
+
+pub fn readILEB128Mem(comptime T: type, ptr: *[*]const u8) !T {
+ const UT = @IntType(false, T.bit_count);
+ const ShiftT = @IntType(false, std.math.log2(T.bit_count));
+
+ var result: UT = 0;
+ var shift: usize = 0;
+ var i: usize = 0;
+
+ while (true) : (i += 1) {
+ const byte = ptr.*[i];
+
+ if (shift > T.bit_count)
+ return error.Overflow;
+
+ var operand: UT = undefined;
+ if (@shlWithOverflow(UT, UT(byte & 0x7f), @intCast(ShiftT, shift), &operand)) {
+ if (byte != 0x7f)
+ return error.Overflow;
+ }
+
+ result |= operand;
+
+ shift += 7;
+
+ if ((byte & 0x80) == 0) {
+ if (shift < T.bit_count and (byte & 0x40) != 0) {
+ result |= @bitCast(UT, @intCast(T, -1)) << @intCast(ShiftT, shift);
+ }
+ ptr.* += i;
+ return @bitCast(T, result);
+ }
+ }
+}
+
+fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
+ var in_stream = std.io.SliceInStream.init(encoded);
+ return try readILEB128(T, &in_stream.stream);
+}
+
+fn test_read_stream_uleb128(comptime T: type, encoded: []const u8) !T {
+ var in_stream = std.io.SliceInStream.init(encoded);
+ return try readULEB128(T, &in_stream.stream);
+}
+
+fn test_read_ileb128(comptime T: type, encoded: []const u8) !T {
+ var in_stream = std.io.SliceInStream.init(encoded);
+ const v1 = readILEB128(T, &in_stream.stream);
+ var in_ptr = encoded.ptr;
+ const v2 = readILEB128Mem(T, &in_ptr);
+ testing.expectEqual(v1, v2);
+ return v1;
+}
+
+fn test_read_uleb128(comptime T: type, encoded: []const u8) !T {
+ var in_stream = std.io.SliceInStream.init(encoded);
+ const v1 = readULEB128(T, &in_stream.stream);
+ var in_ptr = encoded.ptr;
+ const v2 = readULEB128Mem(T, &in_ptr);
+ testing.expectEqual(v1, v2);
+ return v1;
+}
+
+test "deserialize signed LEB128" {
+ // Truncated
+ testing.expectError(error.EndOfStream, test_read_stream_ileb128(i64, "\x80"));
+
+ // Overflow
+ testing.expectError(error.Overflow, test_read_ileb128(i8, "\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_ileb128(i16, "\x80\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_ileb128(i32, "\x80\x80\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_ileb128(i8, "\xff\x7e"));
+
+ // Decode SLEB128
+ testing.expect((try test_read_ileb128(i64, "\x00")) == 0);
+ testing.expect((try test_read_ileb128(i64, "\x01")) == 1);
+ testing.expect((try test_read_ileb128(i64, "\x3f")) == 63);
+ testing.expect((try test_read_ileb128(i64, "\x40")) == -64);
+ testing.expect((try test_read_ileb128(i64, "\x41")) == -63);
+ testing.expect((try test_read_ileb128(i64, "\x7f")) == -1);
+ testing.expect((try test_read_ileb128(i64, "\x80\x01")) == 128);
+ testing.expect((try test_read_ileb128(i64, "\x81\x01")) == 129);
+ testing.expect((try test_read_ileb128(i64, "\xff\x7e")) == -129);
+ testing.expect((try test_read_ileb128(i64, "\x80\x7f")) == -128);
+ testing.expect((try test_read_ileb128(i64, "\x81\x7f")) == -127);
+ testing.expect((try test_read_ileb128(i64, "\xc0\x00")) == 64);
+ testing.expect((try test_read_ileb128(i64, "\xc7\x9f\x7f")) == -12345);
+ testing.expect((try test_read_ileb128(i8, "\xff\x7f")) == -1);
+ testing.expect((try test_read_ileb128(i16, "\xff\xff\x7f")) == -1);
+ testing.expect((try test_read_ileb128(i32, "\xff\xff\xff\xff\x7f")) == -1);
+ testing.expect((try test_read_ileb128(i32, "\x80\x80\x80\x80\x08")) == -0x80000000);
+ testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == @bitCast(i64, @intCast(u64, 0x8000000000000000)));
+ testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x40")) == -0x4000000000000000);
+ testing.expect((try test_read_ileb128(i64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x7f")) == -0x8000000000000000);
+
+ // Decode unnormalized SLEB128 with extra padding bytes.
+ testing.expect((try test_read_ileb128(i64, "\x80\x00")) == 0);
+ testing.expect((try test_read_ileb128(i64, "\x80\x80\x00")) == 0);
+ testing.expect((try test_read_ileb128(i64, "\xff\x00")) == 0x7f);
+ testing.expect((try test_read_ileb128(i64, "\xff\x80\x00")) == 0x7f);
+ testing.expect((try test_read_ileb128(i64, "\x80\x81\x00")) == 0x80);
+ testing.expect((try test_read_ileb128(i64, "\x80\x81\x80\x00")) == 0x80);
+}
+
+test "deserialize unsigned LEB128" {
+ // Truncated
+ testing.expectError(error.EndOfStream, test_read_stream_uleb128(u64, "\x80"));
+
+ // Overflow
+ testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x02"));
+ testing.expectError(error.Overflow, test_read_uleb128(u8, "\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x84"));
+ testing.expectError(error.Overflow, test_read_uleb128(u16, "\x80\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x90"));
+ testing.expectError(error.Overflow, test_read_uleb128(u32, "\x80\x80\x80\x80\x40"));
+ testing.expectError(error.Overflow, test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x40"));
+
+ // Decode ULEB128
+ testing.expect((try test_read_uleb128(u64, "\x00")) == 0);
+ testing.expect((try test_read_uleb128(u64, "\x01")) == 1);
+ testing.expect((try test_read_uleb128(u64, "\x3f")) == 63);
+ testing.expect((try test_read_uleb128(u64, "\x40")) == 64);
+ testing.expect((try test_read_uleb128(u64, "\x7f")) == 0x7f);
+ testing.expect((try test_read_uleb128(u64, "\x80\x01")) == 0x80);
+ testing.expect((try test_read_uleb128(u64, "\x81\x01")) == 0x81);
+ testing.expect((try test_read_uleb128(u64, "\x90\x01")) == 0x90);
+ testing.expect((try test_read_uleb128(u64, "\xff\x01")) == 0xff);
+ testing.expect((try test_read_uleb128(u64, "\x80\x02")) == 0x100);
+ testing.expect((try test_read_uleb128(u64, "\x81\x02")) == 0x101);
+ testing.expect((try test_read_uleb128(u64, "\x80\xc1\x80\x80\x10")) == 4294975616);
+ testing.expect((try test_read_uleb128(u64, "\x80\x80\x80\x80\x80\x80\x80\x80\x80\x01")) == 0x8000000000000000);
+
+ // Decode ULEB128 with extra padding bytes
+ testing.expect((try test_read_uleb128(u64, "\x80\x00")) == 0);
+ testing.expect((try test_read_uleb128(u64, "\x80\x80\x00")) == 0);
+ testing.expect((try test_read_uleb128(u64, "\xff\x00")) == 0x7f);
+ testing.expect((try test_read_uleb128(u64, "\xff\x80\x00")) == 0x7f);
+ testing.expect((try test_read_uleb128(u64, "\x80\x81\x00")) == 0x80);
+ testing.expect((try test_read_uleb128(u64, "\x80\x81\x80\x00")) == 0x80);
+}
diff --git a/std/dwarf.zig b/std/dwarf.zig
index 2cf8ed953e..2f3b29302d 100644
--- a/std/dwarf.zig
+++ b/std/dwarf.zig
@@ -13,6 +13,7 @@ pub const TAG_reference_type = 0x10;
pub const TAG_compile_unit = 0x11;
pub const TAG_string_type = 0x12;
pub const TAG_structure_type = 0x13;
+pub const TAG_subroutine = 0x14;
pub const TAG_subroutine_type = 0x15;
pub const TAG_typedef = 0x16;
pub const TAG_union_type = 0x17;
@@ -241,6 +242,9 @@ pub const AT_const_expr = 0x6c;
pub const AT_enum_class = 0x6d;
pub const AT_linkage_name = 0x6e;
+// DWARF 5
+pub const AT_alignment = 0x88;
+
pub const AT_lo_user = 0x2000; // Implementation-defined range start.
pub const AT_hi_user = 0x3fff; // Implementation-defined range end.
diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig
index 698b0eb216..b2064311db 100644
--- a/std/dynamic_library.zig
+++ b/std/dynamic_library.zig
@@ -19,6 +19,89 @@ pub const DynLib = switch (builtin.os) {
else => void,
};
+// The link_map structure is not completely specified beside the fields
+// reported below, any libc is free to store additional data in the remaining
+// space.
+// An iterator is provided in order to traverse the linked list in a idiomatic
+// fashion.
+const LinkMap = extern struct {
+ l_addr: usize,
+ l_name: [*]const u8,
+ l_ld: ?*elf.Dyn,
+ l_next: ?*LinkMap,
+ l_prev: ?*LinkMap,
+
+ pub const Iterator = struct {
+ current: ?*LinkMap,
+
+ fn end(self: *Iterator) bool {
+ return self.current == null;
+ }
+
+ fn next(self: *Iterator) ?*LinkMap {
+ if (self.current) |it| {
+ self.current = it.l_next;
+ return it;
+ }
+ return null;
+ }
+ };
+};
+
+const RDebug = extern struct {
+ r_version: i32,
+ r_map: ?*LinkMap,
+ r_brk: usize,
+ r_ldbase: usize,
+};
+
+fn elf_get_va_offset(phdrs: []elf.Phdr) !usize {
+ for (phdrs) |*phdr| {
+ if (phdr.p_type == elf.PT_LOAD) {
+ return @ptrToInt(phdr) - phdr.p_vaddr;
+ }
+ }
+ return error.InvalidExe;
+}
+
+pub fn linkmap_iterator(phdrs: []elf.Phdr) !LinkMap.Iterator {
+ const va_offset = try elf_get_va_offset(phdrs);
+
+ const dyn_table = init: {
+ for (phdrs) |*phdr| {
+ if (phdr.p_type == elf.PT_DYNAMIC) {
+ const ptr = @intToPtr([*]elf.Dyn, va_offset + phdr.p_vaddr);
+ break :init ptr[0..phdr.p_memsz / @sizeOf(elf.Dyn)];
+ }
+ }
+ // No PT_DYNAMIC means this is either a statically-linked program or a
+ // badly corrupted one
+ return LinkMap.Iterator{.current = null};
+ };
+
+ const link_map_ptr = init: {
+ for (dyn_table) |*dyn| {
+ switch (dyn.d_tag) {
+ elf.DT_DEBUG => {
+ const r_debug = @intToPtr(*RDebug, dyn.d_un.d_ptr);
+ if (r_debug.r_version != 1) return error.InvalidExe;
+ break :init r_debug.r_map;
+ },
+ elf.DT_PLTGOT => {
+ const got_table = @intToPtr([*]usize, dyn.d_un.d_ptr);
+ // The address to the link_map structure is stored in the
+ // second slot
+ break :init @intToPtr(?*LinkMap, got_table[1]);
+ },
+ else => { }
+ }
+ }
+ return error.InvalidExe;
+ };
+
+ return LinkMap.Iterator{.current = link_map_ptr};
+}
+
pub const LinuxDynLib = struct {
elf_lib: ElfLib,
fd: i32,
diff --git a/std/elf.zig b/std/elf.zig
index d0ec5fb2ae..49f2f7d137 100644
--- a/std/elf.zig
+++ b/std/elf.zig
@@ -877,6 +877,11 @@ pub const Phdr = switch (@sizeOf(usize)) {
8 => Elf64_Phdr,
else => @compileError("expected pointer size of 32 or 64"),
};
+pub const Dyn = switch (@sizeOf(usize)) {
+ 4 => Elf32_Dyn,
+ 8 => Elf64_Dyn,
+ else => @compileError("expected pointer size of 32 or 64"),
+};
pub const Shdr = switch (@sizeOf(usize)) {
4 => Elf32_Shdr,
8 => Elf64_Shdr,
diff --git a/std/fmt.zig b/std/fmt.zig
index 6402271563..e4738d430d 100644
--- a/std/fmt.zig
+++ b/std/fmt.zig
@@ -8,6 +8,8 @@ const builtin = @import("builtin");
const errol = @import("fmt/errol.zig");
const lossyCast = std.math.lossyCast;
+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.
@@ -49,7 +51,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
start_index = i;
},
'}' => {
- try formatType(args[next_arg], fmt[0..0], context, Errors, output);
+ try formatType(args[next_arg], fmt[0..0], context, Errors, output, default_max_depth);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -69,7 +71,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
State.FormatString => switch (c) {
'}' => {
const s = start_index + 1;
- try formatType(args[next_arg], fmt[s..i], context, Errors, output);
+ try formatType(args[next_arg], fmt[s..i], context, Errors, output, default_max_depth);
next_arg += 1;
state = State.Start;
start_index = i + 1;
@@ -108,6 +110,7 @@ pub fn formatType(
context: var,
comptime Errors: type,
output: fn (@typeOf(context), []const u8) Errors!void,
+ max_depth: usize,
) Errors!void {
const T = @typeOf(value);
switch (@typeInfo(T)) {
@@ -122,16 +125,16 @@ pub fn formatType(
},
builtin.TypeId.Optional => {
if (value) |payload| {
- return formatType(payload, fmt, context, Errors, output);
+ return formatType(payload, fmt, context, Errors, output, max_depth);
} else {
return output(context, "null");
}
},
builtin.TypeId.ErrorUnion => {
if (value) |payload| {
- return formatType(payload, fmt, context, Errors, output);
+ return formatType(payload, fmt, context, Errors, output, max_depth);
} else |err| {
- return formatType(err, fmt, context, Errors, output);
+ return formatType(err, fmt, context, Errors, output, max_depth);
}
},
builtin.TypeId.ErrorSet => {
@@ -164,10 +167,13 @@ pub fn formatType(
switch (comptime @typeId(T)) {
builtin.TypeId.Enum => {
try output(context, ".");
- try formatType(@tagName(value), "", context, Errors, output);
+ 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) {
@@ -177,11 +183,14 @@ pub fn formatType(
}
try output(context, @memberName(T, field_i));
try output(context, " = ");
- try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output);
+ 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, "{ .");
@@ -189,7 +198,7 @@ pub fn formatType(
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);
+ try formatType(@field(value, u_field.name), "", context, Errors, output, max_depth-1);
}
}
try output(context, " }");
@@ -210,7 +219,7 @@ pub fn formatType(
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
},
builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
- return formatType(value.*, fmt, context, Errors, output);
+ return formatType(value.*, fmt, context, Errors, output, max_depth);
},
else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
},
@@ -986,17 +995,17 @@ test "fmt.format" {
{
var buf1: [32]u8 = undefined;
var context = BufPrintContext{ .remaining = buf1[0..] };
- try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite);
+ try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
var res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "1234"));
context = BufPrintContext{ .remaining = buf1[0..] };
- try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite);
+ try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "a"));
context = BufPrintContext{ .remaining = buf1[0..] };
- try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite);
+ try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite, default_max_depth);
res = buf1[0 .. buf1.len - context.remaining.len];
testing.expect(mem.eql(u8, res, "1100"));
}
@@ -1364,6 +1373,20 @@ test "fmt.format" {
try testFmt("E.Two", "{}", inst);
}
+ //self-referential struct format
+ {
+ 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);
+ }
//print bytes as hex
{
const some_bytes = "\xCA\xFE\xBA\xBE";
@@ -1449,3 +1472,64 @@ test "fmt.formatIntValue with comptime_int" {
try formatIntValue(value, "", &buf, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append);
assert(mem.eql(u8, buf.toSlice(), "123456789123456789"));
}
+
+test "fmt.formatType max_depth" {
+ 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 {
+ return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y);
+ }
+ };
+ const E = enum {
+ One,
+ Two,
+ Three,
+ };
+ const TU = union(enum) {
+ const SelfType = @This();
+ float: f32,
+ int: u32,
+ ptr: ?*SelfType,
+ };
+ const S = struct {
+ const SelfType = @This();
+ a: ?*SelfType,
+ tu: TU,
+ e: E,
+ vec: Vec2,
+ };
+
+ var inst = S{
+ .a = null,
+ .tu = TU{ .ptr = null },
+ .e = E.Two,
+ .vec = Vec2{ .x = 10.2, .y = 2.22 },
+ };
+ inst.a = &inst;
+ inst.tu.ptr = &inst.tu;
+
+ var buf0 = try std.Buffer.init(std.debug.global_allocator, "");
+ try formatType(inst, "", &buf0, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 0);
+ assert(mem.eql(u8, buf0.toSlice(), "S{ ... }"));
+
+ var buf1 = try std.Buffer.init(std.debug.global_allocator, "");
+ try formatType(inst, "", &buf1, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 1);
+ assert(mem.eql(u8, buf1.toSlice(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
+
+ var buf2 = try std.Buffer.init(std.debug.global_allocator, "");
+ try formatType(inst, "", &buf2, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 2);
+ assert(mem.eql(u8, buf2.toSlice(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
+
+ var buf3 = try std.Buffer.init(std.debug.global_allocator, "");
+ try formatType(inst, "", &buf3, @typeOf(std.Buffer.append).ReturnType.ErrorSet, std.Buffer.append, 3);
+ assert(mem.eql(u8, buf3.toSlice(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
+}
diff --git a/std/hash_map.zig b/std/hash_map.zig
index f4c0b87167..9cd1ea052c 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -118,7 +118,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
};
}
self.incrementModificationCount();
- try self.ensureCapacity();
+ try self.autoCapacity();
const put_result = self.internalPut(key);
assert(put_result.old_kv == null);
return GetOrPutResult{
@@ -135,15 +135,37 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
return res.kv;
}
- fn ensureCapacity(self: *Self) !void {
- if (self.entries.len == 0) {
- return self.initCapacity(16);
+ fn optimizedCapacity(expected_count: usize) usize {
+ // ensure that the hash map will be at most 60% full if
+ // expected_count items are put into it
+ var optimized_capacity = expected_count * 5 / 3;
+ // round capacity to the next power of two
+ const pow = math.log2_int_ceil(usize, optimized_capacity);
+ return math.pow(usize, 2, pow);
+ }
+
+ /// Increases capacity so that the hash map will be at most
+ /// 60% full when expected_count items are put into it
+ pub fn ensureCapacity(self: *Self, expected_count: usize) !void {
+ const optimized_capacity = optimizedCapacity(expected_count);
+ return self.ensureCapacityExact(optimized_capacity);
+ }
+
+ /// Sets the capacity to the new capacity if the new
+ /// capacity is greater than the current capacity.
+ /// New capacity must be a power of two.
+ fn ensureCapacityExact(self: *Self, new_capacity: usize) !void {
+ const is_power_of_two = new_capacity & (new_capacity-1) == 0;
+ assert(is_power_of_two);
+
+ if (new_capacity <= self.entries.len) {
+ return;
}
- // if we get too full (60%), double the capacity
- if (self.size * 5 >= self.entries.len * 3) {
- const old_entries = self.entries;
- try self.initCapacity(self.entries.len * 2);
+ const old_entries = self.entries;
+ try self.initCapacity(new_capacity);
+ self.incrementModificationCount();
+ if (old_entries.len > 0) {
// dump all of the old elements into the new table
for (old_entries) |*old_entry| {
if (old_entry.used) {
@@ -156,8 +178,13 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
/// Returns the kv pair that was already there.
pub fn put(self: *Self, key: K, value: V) !?KV {
+ try self.autoCapacity();
+ return putAssumeCapacity(self, key, value);
+ }
+
+ pub fn putAssumeCapacity(self: *Self, key: K, value: V) ?KV {
+ assert(self.count() < self.entries.len);
self.incrementModificationCount();
- try self.ensureCapacity();
const put_result = self.internalPut(key);
put_result.new_entry.kv.value = value;
@@ -175,7 +202,7 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
return hm.get(key) != null;
}
- pub fn remove(hm: *Self, key: K) ?*KV {
+ pub fn remove(hm: *Self, key: K) ?KV {
if (hm.entries.len == 0) return null;
hm.incrementModificationCount();
const start_index = hm.keyToIndex(key);
@@ -189,13 +216,14 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
if (!eql(entry.kv.key, key)) continue;
+ const removed_kv = entry.kv;
while (roll_over < hm.entries.len) : (roll_over += 1) {
const next_index = (start_index + roll_over + 1) % hm.entries.len;
const next_entry = &hm.entries[next_index];
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
entry.used = false;
hm.size -= 1;
- return &entry.kv;
+ return removed_kv;
}
entry.* = next_entry.*;
entry.distance_from_start_index -= 1;
@@ -226,6 +254,16 @@ pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u3
return other;
}
+ fn autoCapacity(self: *Self) !void {
+ if (self.entries.len == 0) {
+ return self.ensureCapacityExact(16);
+ }
+ // if we get too full (60%), double the capacity
+ if (self.size * 5 >= self.entries.len * 3) {
+ return self.ensureCapacityExact(self.entries.len * 2);
+ }
+ }
+
fn initCapacity(hm: *Self, capacity: usize) !void {
hm.entries = try hm.allocator.alloc(Entry, capacity);
hm.size = 0;
@@ -371,7 +409,10 @@ test "basic hash map usage" {
testing.expect(map.contains(2));
testing.expect(map.get(2).?.value == 22);
- _ = map.remove(2);
+
+ const rmv1 = map.remove(2);
+ testing.expect(rmv1.?.key == 2);
+ testing.expect(rmv1.?.value == 22);
testing.expect(map.remove(2) == null);
testing.expect(map.get(2) == null);
}
@@ -423,6 +464,24 @@ test "iterator hash map" {
testing.expect(entry.value == values[0]);
}
+test "ensure capacity" {
+ var direct_allocator = std.heap.DirectAllocator.init();
+ defer direct_allocator.deinit();
+
+ var map = AutoHashMap(i32, i32).init(&direct_allocator.allocator);
+ defer map.deinit();
+
+ try map.ensureCapacity(20);
+ const initialCapacity = map.entries.len;
+ testing.expect(initialCapacity >= 20);
+ var i : i32 = 0;
+ while (i < 20) : (i += 1) {
+ testing.expect(map.putAssumeCapacity(i, i+10) == null);
+ }
+ // shouldn't resize from putAssumeCapacity
+ testing.expect(initialCapacity == map.entries.len);
+}
+
pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) {
return struct {
fn hash(key: K) u32 {
diff --git a/std/heap.zig b/std/heap.zig
index dd5cfa4cd7..8e3cccc365 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -34,9 +34,6 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new
/// Thread-safe and lock-free.
pub const DirectAllocator = struct {
allocator: Allocator,
- heap_handle: ?HeapHandle,
-
- const HeapHandle = if (builtin.os == Os.windows) os.windows.HANDLE else void;
pub fn init() DirectAllocator {
return DirectAllocator{
@@ -44,21 +41,15 @@ pub const DirectAllocator = struct {
.reallocFn = realloc,
.shrinkFn = shrink,
},
- .heap_handle = if (builtin.os == Os.windows) null else {},
};
}
- pub fn deinit(self: *DirectAllocator) void {
- switch (builtin.os) {
- Os.windows => if (self.heap_handle) |heap_handle| {
- _ = os.windows.HeapDestroy(heap_handle);
- },
- else => {},
- }
- }
+ pub fn deinit(self: *DirectAllocator) void {}
fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
+ if (n == 0)
+ return (([*]u8)(undefined))[0..0];
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
@@ -68,39 +59,76 @@ pub const DirectAllocator = struct {
if (addr == p.MAP_FAILED) return error.OutOfMemory;
if (alloc_size == n) return @intToPtr([*]u8, addr)[0..n];
- const aligned_addr = (addr & ~usize(alignment - 1)) + alignment;
-
- // We can unmap the unused portions of our mmap, but we must only
- // pass munmap bytes that exist outside our allocated pages or it
- // will happily eat us too.
-
- // Since alignment > page_size, we are by definition on a page boundary.
- const unused_start = addr;
- const unused_len = aligned_addr - 1 - unused_start;
-
- const err = p.munmap(unused_start, unused_len);
- assert(p.getErrno(err) == 0);
+ const aligned_addr = mem.alignForward(addr, alignment);
- // It is impossible that there is an unoccupied page at the top of our
- // mmap.
+ // Unmap the extra bytes that were only requested in order to guarantee
+ // that the range of memory we were provided had a proper alignment in
+ // it somewhere. The extra bytes could be at the beginning, or end, or both.
+ const unused_start_len = aligned_addr - addr;
+ if (unused_start_len != 0) {
+ const err = p.munmap(addr, unused_start_len);
+ assert(p.getErrno(err) == 0);
+ }
+ const aligned_end_addr = std.mem.alignForward(aligned_addr + n, os.page_size);
+ const unused_end_len = addr + alloc_size - aligned_end_addr;
+ if (unused_end_len != 0) {
+ const err = p.munmap(aligned_end_addr, unused_end_len);
+ assert(p.getErrno(err) == 0);
+ }
return @intToPtr([*]u8, aligned_addr)[0..n];
},
- Os.windows => {
- const amt = n + alignment + @sizeOf(usize);
- const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
- const heap_handle = optional_heap_handle orelse blk: {
- const hh = os.windows.HeapCreate(0, amt, 0) orelse return error.OutOfMemory;
- const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
- _ = os.windows.HeapDestroy(hh);
- break :blk other_hh.?; // can't be null because of the cmpxchg
- };
- const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
- const root_addr = @ptrToInt(ptr);
- const adjusted_addr = mem.alignForward(root_addr, alignment);
- const record_addr = adjusted_addr + n;
- @intToPtr(*align(1) usize, record_addr).* = root_addr;
- return @intToPtr([*]u8, adjusted_addr)[0..n];
+ .windows => {
+ const w = os.windows;
+
+ // Although officially it's at least aligned to page boundary,
+ // Windows is known to reserve pages on a 64K boundary. It's
+ // even more likely that the requested alignment is <= 64K than
+ // 4K, so we're just allocating blindly and hoping for the best.
+ // see https://devblogs.microsoft.com/oldnewthing/?p=42223
+ const addr = w.VirtualAlloc(
+ null,
+ n,
+ w.MEM_COMMIT | w.MEM_RESERVE,
+ w.PAGE_READWRITE,
+ ) orelse return error.OutOfMemory;
+
+ // If the allocation is sufficiently aligned, use it.
+ if (@ptrToInt(addr) & (alignment - 1) == 0) {
+ return @ptrCast([*]u8, addr)[0..n];
+ }
+
+ // If it wasn't, actually do an explicitely aligned allocation.
+ if (w.VirtualFree(addr, 0, w.MEM_RELEASE) == 0) unreachable;
+ const alloc_size = n + alignment;
+
+ const final_addr = while (true) {
+ // Reserve a range of memory large enough to find a sufficiently
+ // aligned address.
+ const reserved_addr = w.VirtualAlloc(
+ null,
+ alloc_size,
+ w.MEM_RESERVE,
+ w.PAGE_NOACCESS,
+ ) orelse return error.OutOfMemory;
+ const aligned_addr = mem.alignForward(@ptrToInt(reserved_addr), alignment);
+
+ // Release the reserved pages (not actually used).
+ if (w.VirtualFree(reserved_addr, 0, w.MEM_RELEASE) == 0) unreachable;
+
+ // At this point, it is possible that another thread has
+ // obtained some memory space that will cause the next
+ // VirtualAlloc call to fail. To handle this, we will retry
+ // until it succeeds.
+ if (w.VirtualAlloc(
+ @intToPtr(*c_void, aligned_addr),
+ n,
+ w.MEM_COMMIT | w.MEM_RESERVE,
+ w.PAGE_READWRITE,
+ )) |ptr| break ptr;
+ } else unreachable; // TODO else unreachable should not be necessary
+
+ return @ptrCast([*]u8, final_addr)[0..n];
},
else => @compileError("Unsupported OS"),
}
@@ -118,13 +146,31 @@ pub const DirectAllocator = struct {
}
return old_mem[0..new_size];
},
- Os.windows => return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
- const old_adjusted_addr = @ptrToInt(old_mem.ptr);
- const old_record_addr = old_adjusted_addr + old_mem.len;
- const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
- const old_ptr = @intToPtr(*c_void, root_addr);
- const new_record_addr = old_record_addr - new_size + old_mem.len;
- @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
+ .windows => {
+ const w = os.windows;
+ if (new_size == 0) {
+ // From the docs:
+ // "If the dwFreeType parameter is MEM_RELEASE, this parameter
+ // must be 0 (zero). The function frees the entire region that
+ // is reserved in the initial allocation call to VirtualAlloc."
+ // So we can only use MEM_RELEASE when actually releasing the
+ // whole allocation.
+ if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable;
+ } else {
+ const base_addr = @ptrToInt(old_mem.ptr);
+ const old_addr_end = base_addr + old_mem.len;
+ const new_addr_end = base_addr + new_size;
+ const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
+ if (old_addr_end > new_addr_end_rounded) {
+ // For shrinking that is not releasing, we will only
+ // decommit the pages not needed anymore.
+ if (w.VirtualFree(
+ @intToPtr(*c_void, new_addr_end_rounded),
+ old_addr_end - new_addr_end_rounded,
+ w.MEM_DECOMMIT,
+ ) == 0) unreachable;
+ }
+ }
return old_mem[0..new_size];
},
else => @compileError("Unsupported OS"),
@@ -138,36 +184,168 @@ pub const DirectAllocator = struct {
return shrink(allocator, old_mem, old_align, new_size, new_align);
}
const result = try alloc(allocator, new_size, new_align);
- mem.copy(u8, result, old_mem);
- _ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len);
+ if (old_mem.len != 0) {
+ @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
+ _ = os.posix.munmap(@ptrToInt(old_mem.ptr), old_mem.len);
+ }
return result;
},
- Os.windows => {
- if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
+ .windows => {
+ if (old_mem.len == 0) {
+ return alloc(allocator, new_size, new_align);
+ }
+
+ if (new_size <= old_mem.len and new_align <= old_align) {
+ return shrink(allocator, old_mem, old_align, new_size, new_align);
+ }
+
+ const w = os.windows;
+ const base_addr = @ptrToInt(old_mem.ptr);
+
+ if (new_align > old_align and base_addr & (new_align - 1) != 0) {
+ // Current allocation doesn't satisfy the new alignment.
+ // For now we'll do a new one no matter what, but maybe
+ // there is something smarter to do instead.
+ const result = try alloc(allocator, new_size, new_align);
+ assert(old_mem.len != 0);
+ @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
+ if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable;
- const self = @fieldParentPtr(DirectAllocator, "allocator", allocator);
+ return result;
+ }
+
+ const old_addr_end = base_addr + old_mem.len;
+ const old_addr_end_rounded = mem.alignForward(old_addr_end, os.page_size);
+ const new_addr_end = base_addr + new_size;
+ const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
+ if (new_addr_end_rounded == old_addr_end_rounded) {
+ // The reallocation fits in the already allocated pages.
+ return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
+ }
+ assert(new_addr_end_rounded > old_addr_end_rounded);
+
+ // We need to commit new pages.
+ const additional_size = new_addr_end - old_addr_end_rounded;
+ const realloc_addr = w.VirtualAlloc(
+ @intToPtr(*c_void, old_addr_end_rounded),
+ additional_size,
+ w.MEM_COMMIT | w.MEM_RESERVE,
+ w.PAGE_READWRITE,
+ ) orelse {
+ // Committing new pages at the end of the existing allocation
+ // failed, we need to try a new one.
+ const new_alloc_mem = try alloc(allocator, new_size, new_align);
+ @memcpy(new_alloc_mem.ptr, old_mem.ptr, old_mem.len);
+ if (w.VirtualFree(old_mem.ptr, 0, w.MEM_RELEASE) == 0) unreachable;
+
+ return new_alloc_mem;
+ };
+
+ assert(@ptrToInt(realloc_addr) == old_addr_end_rounded);
+ return @ptrCast([*]u8, old_mem.ptr)[0..new_size];
+ },
+ else => @compileError("Unsupported OS"),
+ }
+ }
+};
+
+pub const HeapAllocator = switch (builtin.os) {
+ .windows => struct {
+ allocator: Allocator,
+ heap_handle: ?HeapHandle,
+
+ const HeapHandle = os.windows.HANDLE;
+
+ pub fn init() HeapAllocator {
+ return HeapAllocator{
+ .allocator = Allocator{
+ .reallocFn = realloc,
+ .shrinkFn = shrink,
+ },
+ .heap_handle = null,
+ };
+ }
+
+ pub fn deinit(self: *HeapAllocator) void {
+ if (self.heap_handle) |heap_handle| {
+ _ = os.windows.HeapDestroy(heap_handle);
+ }
+ }
+
+ fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
+ const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
+ if (n == 0)
+ return (([*]u8)(undefined))[0..0];
+
+ const amt = n + alignment + @sizeOf(usize);
+ const optional_heap_handle = @atomicLoad(?HeapHandle, &self.heap_handle, builtin.AtomicOrder.SeqCst);
+ const heap_handle = optional_heap_handle orelse blk: {
+ const options = if (builtin.single_threaded) os.windows.HEAP_NO_SERIALIZE else 0;
+ const hh = os.windows.HeapCreate(options, amt, 0) orelse return error.OutOfMemory;
+ const other_hh = @cmpxchgStrong(?HeapHandle, &self.heap_handle, null, hh, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse break :blk hh;
+ _ = os.windows.HeapDestroy(hh);
+ break :blk other_hh.?; // can't be null because of the cmpxchg
+ };
+ const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
+ const root_addr = @ptrToInt(ptr);
+ const adjusted_addr = mem.alignForward(root_addr, alignment);
+ const record_addr = adjusted_addr + n;
+ @intToPtr(*align(1) usize, record_addr).* = root_addr;
+ return @intToPtr([*]u8, adjusted_addr)[0..n];
+ }
+
+ fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
+ return realloc(allocator, old_mem, old_align, new_size, new_align) catch {
const old_adjusted_addr = @ptrToInt(old_mem.ptr);
const old_record_addr = old_adjusted_addr + old_mem.len;
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
const old_ptr = @intToPtr(*c_void, root_addr);
- const amt = new_size + new_align + @sizeOf(usize);
- const new_ptr = os.windows.HeapReAlloc(
- self.heap_handle.?,
- 0,
- old_ptr,
- amt,
- ) orelse return error.OutOfMemory;
- const offset = old_adjusted_addr - root_addr;
- const new_root_addr = @ptrToInt(new_ptr);
- const new_adjusted_addr = new_root_addr + offset;
- assert(new_adjusted_addr % new_align == 0);
- const new_record_addr = new_adjusted_addr + new_size;
- @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
- return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
- },
- else => @compileError("Unsupported OS"),
+ const new_record_addr = old_record_addr - new_size + old_mem.len;
+ @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
+ return old_mem[0..new_size];
+ };
}
- }
+
+ fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
+ if (old_mem.len == 0) return alloc(allocator, new_size, new_align);
+
+ const self = @fieldParentPtr(HeapAllocator, "allocator", allocator);
+ const old_adjusted_addr = @ptrToInt(old_mem.ptr);
+ const old_record_addr = old_adjusted_addr + old_mem.len;
+ const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
+ const old_ptr = @intToPtr(*c_void, root_addr);
+
+ if (new_size == 0) {
+ if (os.windows.HeapFree(self.heap_handle.?, 0, old_ptr) == 0) unreachable;
+ return old_mem[0..0];
+ }
+
+ const amt = new_size + new_align + @sizeOf(usize);
+ const new_ptr = os.windows.HeapReAlloc(
+ self.heap_handle.?,
+ 0,
+ old_ptr,
+ amt,
+ ) orelse return error.OutOfMemory;
+ const offset = old_adjusted_addr - root_addr;
+ const new_root_addr = @ptrToInt(new_ptr);
+ var new_adjusted_addr = new_root_addr + offset;
+ const offset_is_valid = new_adjusted_addr + new_size + @sizeOf(usize) <= new_root_addr + amt;
+ const offset_is_aligned = new_adjusted_addr % new_align == 0;
+ if (!offset_is_valid or !offset_is_aligned) {
+ // If HeapReAlloc didn't happen to move the memory to the new alignment,
+ // or the memory starting at the old offset would be outside of the new allocation,
+ // then we need to copy the memory to a valid aligned address and use that
+ const new_aligned_addr = mem.alignForward(new_root_addr, new_align);
+ @memcpy(@intToPtr([*]u8, new_aligned_addr), @intToPtr([*]u8, new_adjusted_addr), std.math.min(old_mem.len, new_size));
+ new_adjusted_addr = new_aligned_addr;
+ }
+ const new_record_addr = new_adjusted_addr + new_size;
+ @intToPtr(*align(1) usize, new_record_addr).* = new_root_addr;
+ return @intToPtr([*]u8, new_adjusted_addr)[0..new_size];
+ }
+ },
+ else => @compileError("Unsupported OS"),
};
/// This allocator takes an existing allocator, wraps it, and provides an interface
@@ -250,7 +428,7 @@ pub const ArenaAllocator = struct {
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
- mem.copy(u8, result, old_mem);
+ @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
return result;
}
}
@@ -308,6 +486,103 @@ pub const FixedBufferAllocator = struct {
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
+ @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
+ return result;
+ }
+ }
+
+ fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
+ return old_mem[0..new_size];
+ }
+};
+
+// FIXME: Exposed LLVM intrinsics is a bug
+// See: https://github.com/ziglang/zig/issues/2291
+extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
+extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;
+
+pub const wasm_allocator = &wasm_allocator_state.allocator;
+var wasm_allocator_state = WasmAllocator{
+ .allocator = Allocator{
+ .reallocFn = WasmAllocator.realloc,
+ .shrinkFn = WasmAllocator.shrink,
+ },
+ .start_ptr = undefined,
+ .num_pages = 0,
+ .end_index = 0,
+};
+
+const WasmAllocator = struct {
+ allocator: Allocator,
+ start_ptr: [*]u8,
+ num_pages: usize,
+ end_index: usize,
+
+ comptime {
+ if (builtin.arch != .wasm32) {
+ @compileError("WasmAllocator is only available for wasm32 arch");
+ }
+ }
+
+ fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 {
+ const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
+
+ const addr = @ptrToInt(self.start_ptr) + self.end_index;
+ const adjusted_addr = mem.alignForward(addr, alignment);
+ const adjusted_index = self.end_index + (adjusted_addr - addr);
+ const new_end_index = adjusted_index + size;
+
+ if (new_end_index > self.num_pages * os.page_size) {
+ const required_memory = new_end_index - (self.num_pages * os.page_size);
+
+ var num_pages: usize = required_memory / os.page_size;
+ if (required_memory % os.page_size != 0) {
+ num_pages += 1;
+ }
+
+ const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages));
+ if (prev_page == -1) {
+ return error.OutOfMemory;
+ }
+
+ self.num_pages += num_pages;
+ }
+
+ const result = self.start_ptr[adjusted_index..new_end_index];
+ self.end_index = new_end_index;
+
+ return result;
+ }
+
+ // Check if memory is the last "item" and is aligned correctly
+ fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool {
+ const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
+ return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr);
+ }
+
+ fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
+ const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
+
+ // Initialize start_ptr at the first realloc
+ if (self.num_pages == 0) {
+ self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * os.page_size);
+ }
+
+ if (is_last_item(allocator, old_mem, new_align)) {
+ const start_index = self.end_index - old_mem.len;
+ const new_end_index = start_index + new_size;
+
+ if (new_end_index > self.num_pages * os.page_size) {
+ _ = try alloc(allocator, new_end_index - self.end_index, new_align);
+ }
+ const result = self.start_ptr[start_index..new_end_index];
+
+ self.end_index = new_end_index;
+ return result;
+ } else if (new_size <= old_mem.len and new_align <= old_align) {
+ return error.OutOfMemory;
+ } else {
+ const result = try alloc(allocator, new_size, new_align);
mem.copy(u8, result, old_mem);
return result;
}
@@ -360,7 +635,7 @@ pub const ThreadSafeFixedBufferAllocator = blk: {
return error.OutOfMemory;
} else {
const result = try alloc(allocator, new_size, new_align);
- mem.copy(u8, result, old_mem);
+ @memcpy(result.ptr, old_mem.ptr, std.math.min(old_mem.len, result.len));
return result;
}
}
@@ -470,6 +745,31 @@ test "DirectAllocator" {
try testAllocator(allocator);
try testAllocatorAligned(allocator, 16);
try testAllocatorLargeAlignment(allocator);
+ try testAllocatorAlignedShrink(allocator);
+
+ if (builtin.os == .windows) {
+ // Trying really large alignment. As mentionned in the implementation,
+ // VirtualAlloc returns 64K aligned addresses. We want to make sure
+ // DirectAllocator works beyond that, as it's not tested by
+ // `testAllocatorLargeAlignment`.
+ const slice = try allocator.alignedAlloc(u8, 1 << 20, 128);
+ slice[0] = 0x12;
+ slice[127] = 0x34;
+ allocator.free(slice);
+ }
+}
+
+test "HeapAllocator" {
+ if (builtin.os == .windows) {
+ var heap_allocator = HeapAllocator.init();
+ defer heap_allocator.deinit();
+
+ const allocator = &heap_allocator.allocator;
+ try testAllocator(allocator);
+ try testAllocatorAligned(allocator, 16);
+ try testAllocatorLargeAlignment(allocator);
+ try testAllocatorAlignedShrink(allocator);
+ }
}
test "ArenaAllocator" {
@@ -482,15 +782,17 @@ test "ArenaAllocator" {
try testAllocator(&arena_allocator.allocator);
try testAllocatorAligned(&arena_allocator.allocator, 16);
try testAllocatorLargeAlignment(&arena_allocator.allocator);
+ try testAllocatorAlignedShrink(&arena_allocator.allocator);
}
-var test_fixed_buffer_allocator_memory: [30000 * @sizeOf(usize)]u8 = undefined;
+var test_fixed_buffer_allocator_memory: [80000 * @sizeOf(u64)]u8 = undefined;
test "FixedBufferAllocator" {
var fixed_buffer_allocator = FixedBufferAllocator.init(test_fixed_buffer_allocator_memory[0..]);
try testAllocator(&fixed_buffer_allocator.allocator);
try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
+ try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
}
test "FixedBufferAllocator Reuse memory on realloc" {
@@ -528,6 +830,7 @@ test "ThreadSafeFixedBufferAllocator" {
try testAllocator(&fixed_buffer_allocator.allocator);
try testAllocatorAligned(&fixed_buffer_allocator.allocator, 16);
try testAllocatorLargeAlignment(&fixed_buffer_allocator.allocator);
+ try testAllocatorAlignedShrink(&fixed_buffer_allocator.allocator);
}
fn testAllocator(allocator: *mem.Allocator) !void {
@@ -610,3 +913,32 @@ fn testAllocatorLargeAlignment(allocator: *mem.Allocator) mem.Allocator.Error!vo
allocator.free(slice);
}
+
+fn testAllocatorAlignedShrink(allocator: *mem.Allocator) mem.Allocator.Error!void {
+ var debug_buffer: [1000]u8 = undefined;
+ const debug_allocator = &FixedBufferAllocator.init(&debug_buffer).allocator;
+
+ const alloc_size = os.page_size * 2 + 50;
+ var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
+ defer allocator.free(slice);
+
+ var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
+ // On Windows, VirtualAlloc returns addresses aligned to a 64K boundary,
+ // which is 16 pages, hence the 32. This test may require to increase
+ // the size of the allocations feeding the `allocator` parameter if they
+ // fail, because of this high over-alignment we want to have.
+ while (@ptrToInt(slice.ptr) == mem.alignForward(@ptrToInt(slice.ptr), os.page_size * 32)) {
+ try stuff_to_free.append(slice);
+ slice = try allocator.alignedAlloc(u8, 16, alloc_size);
+ }
+ while (stuff_to_free.popOrNull()) |item| {
+ allocator.free(item);
+ }
+ slice[0] = 0x12;
+ slice[60] = 0x34;
+
+ // realloc to a smaller size but with a larger alignment
+ slice = try allocator.alignedRealloc(slice, os.page_size * 32, alloc_size / 2);
+ testing.expect(slice[0] == 0x12);
+ testing.expect(slice[60] == 0x34);
+}
diff --git a/std/io.zig b/std/io.zig
index afbd8198fd..1f363b47b1 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -36,6 +36,7 @@ pub fn getStdIn() GetStdIoErrs!File {
}
pub const SeekableStream = @import("io/seekable_stream.zig").SeekableStream;
+pub const COutStream = @import("io/c_out_stream.zig").COutStream;
pub fn InStream(comptime ReadError: type) type {
return struct {
@@ -194,8 +195,8 @@ pub fn InStream(comptime ReadError: type) type {
return mem.readVarInt(ReturnType, bytes, endian);
}
- pub fn skipBytes(self: *Self, num_bytes: usize) !void {
- var i: usize = 0;
+ pub fn skipBytes(self: *Self, num_bytes: u64) !void {
+ var i: u64 = 0;
while (i < num_bytes) : (i += 1) {
_ = try self.readByte();
}
@@ -289,7 +290,7 @@ pub fn readFileAllocAligned(allocator: *mem.Allocator, path: []const u8, comptim
var file = try File.openRead(path);
defer file.close();
- const size = try file.getEndPos();
+ const size = try math.cast(usize, try file.getEndPos());
const buf = try allocator.alignedAlloc(u8, A, size);
errdefer allocator.free(buf);
@@ -742,7 +743,7 @@ pub fn CountingOutStream(comptime OutStreamError: type) type {
pub const Error = OutStreamError;
pub stream: Stream,
- pub bytes_written: usize,
+ pub bytes_written: u64,
child_stream: *Stream,
pub fn init(child_stream: *Stream) Self {
@@ -1089,8 +1090,11 @@ test "io.readLineSliceFrom" {
}
pub const Packing = enum {
- Byte, /// Pack data to byte alignment
- Bit, /// Pack data to bit alignment
+ /// Pack data to byte alignment
+ Byte,
+
+ /// Pack data to bit alignment
+ Bit,
};
/// Creates a deserializer that deserializes types from any stream.
@@ -1111,10 +1115,12 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
pub const Stream = InStream(Error);
pub fn init(in_stream: *Stream) Self {
- return Self{ .in_stream = switch (packing) {
- .Bit => BitInStream(endian, Stream.Error).init(in_stream),
- .Byte => in_stream,
- } };
+ return Self{
+ .in_stream = switch (packing) {
+ .Bit => BitInStream(endian, Stream.Error).init(in_stream),
+ .Byte => in_stream,
+ },
+ };
}
pub fn alignToByte(self: *Self) void {
@@ -1281,7 +1287,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, comptime packing: Packing,
ptr.* = null;
return;
}
-
+
ptr.* = OC(undefined); //make it non-null so the following .? is guaranteed safe
const val_ptr = &ptr.*.?;
try self.deserializeInto(val_ptr);
@@ -1320,10 +1326,12 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
pub const Stream = OutStream(Error);
pub fn init(out_stream: *Stream) Self {
- return Self{ .out_stream = switch (packing) {
- .Bit => BitOutStream(endian, Stream.Error).init(out_stream),
- .Byte => out_stream,
- } };
+ return Self{
+ .out_stream = switch (packing) {
+ .Bit => BitOutStream(endian, Stream.Error).init(out_stream),
+ .Byte => out_stream,
+ },
+ };
}
/// Flushes any unwritten bits to the stream
@@ -1447,7 +1455,6 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
test "import io tests" {
comptime {
- _ = @import("io_test.zig");
+ _ = @import("io/test.zig");
}
}
-
diff --git a/std/io/c_out_stream.zig b/std/io/c_out_stream.zig
new file mode 100644
index 0000000000..c66b342f1e
--- /dev/null
+++ b/std/io/c_out_stream.zig
@@ -0,0 +1,48 @@
+const std = @import("../std.zig");
+const OutStream = std.io.OutStream;
+const builtin = @import("builtin");
+const posix = std.os.posix;
+
+/// TODO make std.os.FILE use *FILE when linking libc and this just becomes
+/// std.io.FileOutStream because std.os.File.write would do this when linking
+/// libc.
+pub const COutStream = struct {
+ pub const Error = std.os.File.WriteError;
+ pub const Stream = OutStream(Error);
+
+ stream: Stream,
+ c_file: *std.c.FILE,
+
+ pub fn init(c_file: *std.c.FILE) COutStream {
+ return COutStream{
+ .c_file = c_file,
+ .stream = Stream{ .writeFn = writeFn },
+ };
+ }
+
+ fn writeFn(out_stream: *Stream, bytes: []const u8) Error!void {
+ const self = @fieldParentPtr(COutStream, "stream", out_stream);
+ const amt_written = std.c.fwrite(bytes.ptr, 1, bytes.len, self.c_file);
+ if (amt_written == bytes.len) return;
+ // TODO errno on windows. should we have a posix layer for windows?
+ if (builtin.os == .windows) {
+ return error.InputOutput;
+ }
+ const errno = std.c._errno().*;
+ switch (errno) {
+ 0 => unreachable,
+ posix.EINVAL => unreachable,
+ posix.EFAULT => unreachable,
+ posix.EAGAIN => unreachable, // this is a blocking API
+ posix.EBADF => unreachable, // always a race condition
+ posix.EDESTADDRREQ => unreachable, // connect was never called
+ posix.EDQUOT => return error.DiskQuota,
+ posix.EFBIG => return error.FileTooBig,
+ posix.EIO => return error.InputOutput,
+ posix.ENOSPC => return error.NoSpaceLeft,
+ posix.EPERM => return error.AccessDenied,
+ posix.EPIPE => return error.BrokenPipe,
+ else => return std.os.unexpectedErrorPosix(@intCast(usize, errno)),
+ }
+ }
+};
diff --git a/std/io/seekable_stream.zig b/std/io/seekable_stream.zig
index 5529e42ff1..baf479891c 100644
--- a/std/io/seekable_stream.zig
+++ b/std/io/seekable_stream.zig
@@ -7,25 +7,25 @@ pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType
pub const SeekError = SeekErrorType;
pub const GetSeekPosError = GetSeekPosErrorType;
- seekToFn: fn (self: *Self, pos: usize) SeekError!void,
- seekForwardFn: fn (self: *Self, pos: isize) SeekError!void,
+ seekToFn: fn (self: *Self, pos: u64) SeekError!void,
+ seekForwardFn: fn (self: *Self, pos: i64) SeekError!void,
- getPosFn: fn (self: *Self) GetSeekPosError!usize,
- getEndPosFn: fn (self: *Self) GetSeekPosError!usize,
+ getPosFn: fn (self: *Self) GetSeekPosError!u64,
+ getEndPosFn: fn (self: *Self) GetSeekPosError!u64,
- pub fn seekTo(self: *Self, pos: usize) SeekError!void {
+ pub fn seekTo(self: *Self, pos: u64) SeekError!void {
return self.seekToFn(self, pos);
}
- pub fn seekForward(self: *Self, amt: isize) SeekError!void {
+ pub fn seekForward(self: *Self, amt: i64) SeekError!void {
return self.seekForwardFn(self, amt);
}
- pub fn getEndPos(self: *Self) GetSeekPosError!usize {
+ pub fn getEndPos(self: *Self) GetSeekPosError!u64 {
return self.getEndPosFn(self);
}
- pub fn getPos(self: *Self) GetSeekPosError!usize {
+ pub fn getPos(self: *Self) GetSeekPosError!u64 {
return self.getPosFn(self);
}
};
diff --git a/std/io_test.zig b/std/io/test.zig
index d6f2264a56..07a3c0e8dd 100644
--- a/std/io_test.zig
+++ b/std/io/test.zig
@@ -1,4 +1,4 @@
-const std = @import("std.zig");
+const std = @import("../std.zig");
const io = std.io;
const meta = std.meta;
const trait = std.trait;
@@ -589,3 +589,14 @@ test "Deserializer bad data" {
try testBadData(.Big, .Bit);
try testBadData(.Little, .Bit);
}
+
+test "c out stream" {
+ if (!builtin.link_libc) return error.SkipZigTest;
+
+ const filename = c"tmp_io_test_file.txt";
+ const out_file = std.c.fopen(filename, c"w") orelse return error.UnableToOpenTestFile;
+ defer std.os.deleteFileC(filename) catch {};
+
+ const out_stream = &io.COutStream.init(out_file).stream;
+ try out_stream.print("hi: {}\n", i32(123));
+}
diff --git a/std/json.zig b/std/json.zig
index 551c3a8da7..8d42d1bcf0 100644
--- a/std/json.zig
+++ b/std/json.zig
@@ -1400,3 +1400,7 @@ test "json.parser.dynamic" {
const double = image.Object.get("double").?.value;
testing.expect(double.Float == 1.3412);
}
+
+test "import more json tests" {
+ _ = @import("json/test.zig");
+}
diff --git a/std/json/test.zig b/std/json/test.zig
new file mode 100644
index 0000000000..7c89dcd123
--- /dev/null
+++ b/std/json/test.zig
@@ -0,0 +1,1904 @@
+// RFC 8529 conformance tests.
+//
+// Tests are taken from https://github.com/nst/JSONTestSuite
+// Read also http://seriot.ch/parsing_json.php for a good overview.
+
+const std = @import("../std.zig");
+
+fn ok(comptime s: []const u8) void {
+ std.testing.expect(std.json.validate(s));
+}
+
+fn err(comptime s: []const u8) void {
+ std.testing.expect(!std.json.validate(s));
+}
+
+fn any(comptime s: []const u8) void {
+ std.testing.expect(true);
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Additional tests not part of test JSONTestSuite.
+
+test "y_trailing_comma_after_empty" {
+ ok(
+ \\{"1":[],"2":{},"3":"4"}
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+test "y_array_arraysWithSpaces" {
+ ok(
+ \\[[] ]
+ );
+}
+
+test "y_array_empty" {
+ ok(
+ \\[]
+ );
+}
+
+test "y_array_empty-string" {
+ ok(
+ \\[""]
+ );
+}
+
+test "y_array_ending_with_newline" {
+ ok(
+ \\["a"]
+ );
+}
+
+test "y_array_false" {
+ ok(
+ \\[false]
+ );
+}
+
+test "y_array_heterogeneous" {
+ ok(
+ \\[null, 1, "1", {}]
+ );
+}
+
+test "y_array_null" {
+ ok(
+ \\[null]
+ );
+}
+
+test "y_array_with_1_and_newline" {
+ ok(
+ \\[1
+ \\]
+ );
+}
+
+test "y_array_with_leading_space" {
+ ok(
+ \\ [1]
+ );
+}
+
+test "y_array_with_several_null" {
+ ok(
+ \\[1,null,null,null,2]
+ );
+}
+
+test "y_array_with_trailing_space" {
+ ok("[2] ");
+}
+
+test "y_number_0e+1" {
+ ok(
+ \\[0e+1]
+ );
+}
+
+test "y_number_0e1" {
+ ok(
+ \\[0e1]
+ );
+}
+
+test "y_number_after_space" {
+ ok(
+ \\[ 4]
+ );
+}
+
+test "y_number_double_close_to_zero" {
+ ok(
+ \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
+ );
+}
+
+test "y_number_int_with_exp" {
+ ok(
+ \\[20e1]
+ );
+}
+
+test "y_number" {
+ ok(
+ \\[123e65]
+ );
+}
+
+test "y_number_minus_zero" {
+ ok(
+ \\[-0]
+ );
+}
+
+test "y_number_negative_int" {
+ ok(
+ \\[-123]
+ );
+}
+
+test "y_number_negative_one" {
+ ok(
+ \\[-1]
+ );
+}
+
+test "y_number_negative_zero" {
+ ok(
+ \\[-0]
+ );
+}
+
+test "y_number_real_capital_e" {
+ ok(
+ \\[1E22]
+ );
+}
+
+test "y_number_real_capital_e_neg_exp" {
+ ok(
+ \\[1E-2]
+ );
+}
+
+test "y_number_real_capital_e_pos_exp" {
+ ok(
+ \\[1E+2]
+ );
+}
+
+test "y_number_real_exponent" {
+ ok(
+ \\[123e45]
+ );
+}
+
+test "y_number_real_fraction_exponent" {
+ ok(
+ \\[123.456e78]
+ );
+}
+
+test "y_number_real_neg_exp" {
+ ok(
+ \\[1e-2]
+ );
+}
+
+test "y_number_real_pos_exponent" {
+ ok(
+ \\[1e+2]
+ );
+}
+
+test "y_number_simple_int" {
+ ok(
+ \\[123]
+ );
+}
+
+test "y_number_simple_real" {
+ ok(
+ \\[123.456789]
+ );
+}
+
+test "y_object_basic" {
+ ok(
+ \\{"asd":"sdf"}
+ );
+}
+
+test "y_object_duplicated_key_and_value" {
+ ok(
+ \\{"a":"b","a":"b"}
+ );
+}
+
+test "y_object_duplicated_key" {
+ ok(
+ \\{"a":"b","a":"c"}
+ );
+}
+
+test "y_object_empty" {
+ ok(
+ \\{}
+ );
+}
+
+test "y_object_empty_key" {
+ ok(
+ \\{"":0}
+ );
+}
+
+test "y_object_escaped_null_in_key" {
+ ok(
+ \\{"foo\u0000bar": 42}
+ );
+}
+
+test "y_object_extreme_numbers" {
+ ok(
+ \\{ "min": -1.0e+28, "max": 1.0e+28 }
+ );
+}
+
+test "y_object" {
+ ok(
+ \\{"asd":"sdf", "dfg":"fgh"}
+ );
+}
+
+test "y_object_long_strings" {
+ ok(
+ \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
+ );
+}
+
+test "y_object_simple" {
+ ok(
+ \\{"a":[]}
+ );
+}
+
+test "y_object_string_unicode" {
+ ok(
+ \\{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }
+ );
+}
+
+test "y_object_with_newlines" {
+ ok(
+ \\{
+ \\"a": "b"
+ \\}
+ );
+}
+
+test "y_string_1_2_3_bytes_UTF-8_sequences" {
+ ok(
+ \\["\u0060\u012a\u12AB"]
+ );
+}
+
+test "y_string_accepted_surrogate_pair" {
+ ok(
+ \\["\uD801\udc37"]
+ );
+}
+
+test "y_string_accepted_surrogate_pairs" {
+ ok(
+ \\["\ud83d\ude39\ud83d\udc8d"]
+ );
+}
+
+test "y_string_allowed_escapes" {
+ ok(
+ \\["\"\\\/\b\f\n\r\t"]
+ );
+}
+
+test "y_string_backslash_and_u_escaped_zero" {
+ ok(
+ \\["\\u0000"]
+ );
+}
+
+test "y_string_backslash_doublequotes" {
+ ok(
+ \\["\""]
+ );
+}
+
+test "y_string_comments" {
+ ok(
+ \\["a/*b*/c/*d//e"]
+ );
+}
+
+test "y_string_double_escape_a" {
+ ok(
+ \\["\\a"]
+ );
+}
+
+test "y_string_double_escape_n" {
+ ok(
+ \\["\\n"]
+ );
+}
+
+test "y_string_escaped_control_character" {
+ ok(
+ \\["\u0012"]
+ );
+}
+
+test "y_string_escaped_noncharacter" {
+ ok(
+ \\["\uFFFF"]
+ );
+}
+
+test "y_string_in_array" {
+ ok(
+ \\["asd"]
+ );
+}
+
+test "y_string_in_array_with_leading_space" {
+ ok(
+ \\[ "asd"]
+ );
+}
+
+test "y_string_last_surrogates_1_and_2" {
+ ok(
+ \\["\uDBFF\uDFFF"]
+ );
+}
+
+test "y_string_nbsp_uescaped" {
+ ok(
+ \\["new\u00A0line"]
+ );
+}
+
+test "y_string_nonCharacterInUTF-8_U+10FFFF" {
+ ok(
+ \\["􏿿"]
+ );
+}
+
+test "y_string_nonCharacterInUTF-8_U+FFFF" {
+ ok(
+ \\["￿"]
+ );
+}
+
+test "y_string_null_escape" {
+ ok(
+ \\["\u0000"]
+ );
+}
+
+test "y_string_one-byte-utf-8" {
+ ok(
+ \\["\u002c"]
+ );
+}
+
+test "y_string_pi" {
+ ok(
+ \\["π"]
+ );
+}
+
+test "y_string_reservedCharacterInUTF-8_U+1BFFF" {
+ ok(
+ \\["𛿿"]
+ );
+}
+
+test "y_string_simple_ascii" {
+ ok(
+ \\["asd "]
+ );
+}
+
+test "y_string_space" {
+ ok(
+ \\" "
+ );
+}
+
+test "y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" {
+ ok(
+ \\["\uD834\uDd1e"]
+ );
+}
+
+test "y_string_three-byte-utf-8" {
+ ok(
+ \\["\u0821"]
+ );
+}
+
+test "y_string_two-byte-utf-8" {
+ ok(
+ \\["\u0123"]
+ );
+}
+
+test "y_string_u+2028_line_sep" {
+ ok("[\"\xe2\x80\xa8\"]");
+}
+
+test "y_string_u+2029_par_sep" {
+ ok("[\"\xe2\x80\xa9\"]");
+}
+
+test "y_string_uescaped_newline" {
+ ok(
+ \\["new\u000Aline"]
+ );
+}
+
+test "y_string_uEscape" {
+ ok(
+ \\["\u0061\u30af\u30EA\u30b9"]
+ );
+}
+
+test "y_string_unescaped_char_delete" {
+ ok("[\"\x7f\"]");
+}
+
+test "y_string_unicode_2" {
+ ok(
+ \\["⍂㈴⍂"]
+ );
+}
+
+test "y_string_unicodeEscapedBackslash" {
+ ok(
+ \\["\u005C"]
+ );
+}
+
+test "y_string_unicode_escaped_double_quote" {
+ ok(
+ \\["\u0022"]
+ );
+}
+
+test "y_string_unicode" {
+ ok(
+ \\["\uA66D"]
+ );
+}
+
+test "y_string_unicode_U+10FFFE_nonchar" {
+ ok(
+ \\["\uDBFF\uDFFE"]
+ );
+}
+
+test "y_string_unicode_U+1FFFE_nonchar" {
+ ok(
+ \\["\uD83F\uDFFE"]
+ );
+}
+
+test "y_string_unicode_U+200B_ZERO_WIDTH_SPACE" {
+ ok(
+ \\["\u200B"]
+ );
+}
+
+test "y_string_unicode_U+2064_invisible_plus" {
+ ok(
+ \\["\u2064"]
+ );
+}
+
+test "y_string_unicode_U+FDD0_nonchar" {
+ ok(
+ \\["\uFDD0"]
+ );
+}
+
+test "y_string_unicode_U+FFFE_nonchar" {
+ ok(
+ \\["\uFFFE"]
+ );
+}
+
+test "y_string_utf8" {
+ ok(
+ \\["€𝄞"]
+ );
+}
+
+test "y_string_with_del_character" {
+ ok("[\"a\x7fa\"]");
+}
+
+test "y_structure_lonely_false" {
+ ok(
+ \\false
+ );
+}
+
+test "y_structure_lonely_int" {
+ ok(
+ \\42
+ );
+}
+
+test "y_structure_lonely_negative_real" {
+ ok(
+ \\-0.1
+ );
+}
+
+test "y_structure_lonely_null" {
+ ok(
+ \\null
+ );
+}
+
+test "y_structure_lonely_string" {
+ ok(
+ \\"asd"
+ );
+}
+
+test "y_structure_lonely_true" {
+ ok(
+ \\true
+ );
+}
+
+test "y_structure_string_empty" {
+ ok(
+ \\""
+ );
+}
+
+test "y_structure_trailing_newline" {
+ ok(
+ \\["a"]
+ );
+}
+
+test "y_structure_true_in_array" {
+ ok(
+ \\[true]
+ );
+}
+
+test "y_structure_whitespace_array" {
+ ok(" [] ");
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+test "n_array_1_true_without_comma" {
+ err(
+ \\[1 true]
+ );
+}
+
+test "n_array_a_invalid_utf8" {
+ err(
+ \\[aå]
+ );
+}
+
+test "n_array_colon_instead_of_comma" {
+ err(
+ \\["": 1]
+ );
+}
+
+test "n_array_comma_after_close" {
+ //err(
+ // \\[""],
+ //);
+}
+
+test "n_array_comma_and_number" {
+ err(
+ \\[,1]
+ );
+}
+
+test "n_array_double_comma" {
+ err(
+ \\[1,,2]
+ );
+}
+
+test "n_array_double_extra_comma" {
+ err(
+ \\["x",,]
+ );
+}
+
+test "n_array_extra_close" {
+ err(
+ \\["x"]]
+ );
+}
+
+test "n_array_extra_comma" {
+ //err(
+ // \\["",]
+ //);
+}
+
+test "n_array_incomplete_invalid_value" {
+ err(
+ \\[x
+ );
+}
+
+test "n_array_incomplete" {
+ err(
+ \\["x"
+ );
+}
+
+test "n_array_inner_array_no_comma" {
+ err(
+ \\[3[4]]
+ );
+}
+
+test "n_array_invalid_utf8" {
+ err(
+ \\[ÿ]
+ );
+}
+
+test "n_array_items_separated_by_semicolon" {
+ err(
+ \\[1:2]
+ );
+}
+
+test "n_array_just_comma" {
+ err(
+ \\[,]
+ );
+}
+
+test "n_array_just_minus" {
+ err(
+ \\[-]
+ );
+}
+
+test "n_array_missing_value" {
+ err(
+ \\[ , ""]
+ );
+}
+
+test "n_array_newlines_unclosed" {
+ err(
+ \\["a",
+ \\4
+ \\,1,
+ );
+}
+
+test "n_array_number_and_comma" {
+ err(
+ \\[1,]
+ );
+}
+
+test "n_array_number_and_several_commas" {
+ err(
+ \\[1,,]
+ );
+}
+
+test "n_array_spaces_vertical_tab_formfeed" {
+ err("[\"\x0aa\"\\f]");
+}
+
+test "n_array_star_inside" {
+ err(
+ \\[*]
+ );
+}
+
+test "n_array_unclosed" {
+ err(
+ \\[""
+ );
+}
+
+test "n_array_unclosed_trailing_comma" {
+ err(
+ \\[1,
+ );
+}
+
+test "n_array_unclosed_with_new_lines" {
+ err(
+ \\[1,
+ \\1
+ \\,1
+ );
+}
+
+test "n_array_unclosed_with_object_inside" {
+ err(
+ \\[{}
+ );
+}
+
+test "n_incomplete_false" {
+ err(
+ \\[fals]
+ );
+}
+
+test "n_incomplete_null" {
+ err(
+ \\[nul]
+ );
+}
+
+test "n_incomplete_true" {
+ err(
+ \\[tru]
+ );
+}
+
+test "n_multidigit_number_then_00" {
+ err("123\x00");
+}
+
+test "n_number_0.1.2" {
+ err(
+ \\[0.1.2]
+ );
+}
+
+test "n_number_-01" {
+ err(
+ \\[-01]
+ );
+}
+
+test "n_number_0.3e" {
+ err(
+ \\[0.3e]
+ );
+}
+
+test "n_number_0.3e+" {
+ err(
+ \\[0.3e+]
+ );
+}
+
+test "n_number_0_capital_E" {
+ err(
+ \\[0E]
+ );
+}
+
+test "n_number_0_capital_E+" {
+ err(
+ \\[0E+]
+ );
+}
+
+test "n_number_0.e1" {
+ err(
+ \\[0.e1]
+ );
+}
+
+test "n_number_0e" {
+ err(
+ \\[0e]
+ );
+}
+
+test "n_number_0e+" {
+ err(
+ \\[0e+]
+ );
+}
+
+test "n_number_1_000" {
+ err(
+ \\[1 000.0]
+ );
+}
+
+test "n_number_1.0e-" {
+ err(
+ \\[1.0e-]
+ );
+}
+
+test "n_number_1.0e" {
+ err(
+ \\[1.0e]
+ );
+}
+
+test "n_number_1.0e+" {
+ err(
+ \\[1.0e+]
+ );
+}
+
+test "n_number_-1.0." {
+ err(
+ \\[-1.0.]
+ );
+}
+
+test "n_number_1eE2" {
+ err(
+ \\[1eE2]
+ );
+}
+
+test "n_number_.-1" {
+ err(
+ \\[.-1]
+ );
+}
+
+test "n_number_+1" {
+ err(
+ \\[+1]
+ );
+}
+
+test "n_number_.2e-3" {
+ err(
+ \\[.2e-3]
+ );
+}
+
+test "n_number_2.e-3" {
+ err(
+ \\[2.e-3]
+ );
+}
+
+test "n_number_2.e+3" {
+ err(
+ \\[2.e+3]
+ );
+}
+
+test "n_number_2.e3" {
+ err(
+ \\[2.e3]
+ );
+}
+
+test "n_number_-2." {
+ err(
+ \\[-2.]
+ );
+}
+
+test "n_number_9.e+" {
+ err(
+ \\[9.e+]
+ );
+}
+
+test "n_number_expression" {
+ err(
+ \\[1+2]
+ );
+}
+
+test "n_number_hex_1_digit" {
+ err(
+ \\[0x1]
+ );
+}
+
+test "n_number_hex_2_digits" {
+ err(
+ \\[0x42]
+ );
+}
+
+test "n_number_infinity" {
+ err(
+ \\[Infinity]
+ );
+}
+
+test "n_number_+Inf" {
+ err(
+ \\[+Inf]
+ );
+}
+
+test "n_number_Inf" {
+ err(
+ \\[Inf]
+ );
+}
+
+test "n_number_invalid+-" {
+ err(
+ \\[0e+-1]
+ );
+}
+
+test "n_number_invalid-negative-real" {
+ err(
+ \\[-123.123foo]
+ );
+}
+
+test "n_number_invalid-utf-8-in-bigger-int" {
+ err(
+ \\[123å]
+ );
+}
+
+test "n_number_invalid-utf-8-in-exponent" {
+ err(
+ \\[1e1å]
+ );
+}
+
+test "n_number_invalid-utf-8-in-int" {
+ err(
+ \\[0å]
+ );
+}
+
+test "n_number_++" {
+ err(
+ \\[++1234]
+ );
+}
+
+test "n_number_minus_infinity" {
+ err(
+ \\[-Infinity]
+ );
+}
+
+test "n_number_minus_sign_with_trailing_garbage" {
+ err(
+ \\[-foo]
+ );
+}
+
+test "n_number_minus_space_1" {
+ err(
+ \\[- 1]
+ );
+}
+
+test "n_number_-NaN" {
+ err(
+ \\[-NaN]
+ );
+}
+
+test "n_number_NaN" {
+ err(
+ \\[NaN]
+ );
+}
+
+test "n_number_neg_int_starting_with_zero" {
+ err(
+ \\[-012]
+ );
+}
+
+test "n_number_neg_real_without_int_part" {
+ err(
+ \\[-.123]
+ );
+}
+
+test "n_number_neg_with_garbage_at_end" {
+ err(
+ \\[-1x]
+ );
+}
+
+test "n_number_real_garbage_after_e" {
+ err(
+ \\[1ea]
+ );
+}
+
+test "n_number_real_with_invalid_utf8_after_e" {
+ err(
+ \\[1eå]
+ );
+}
+
+test "n_number_real_without_fractional_part" {
+ err(
+ \\[1.]
+ );
+}
+
+test "n_number_starting_with_dot" {
+ err(
+ \\[.123]
+ );
+}
+
+test "n_number_U+FF11_fullwidth_digit_one" {
+ err(
+ \\[1]
+ );
+}
+
+test "n_number_with_alpha_char" {
+ err(
+ \\[1.8011670033376514H-308]
+ );
+}
+
+test "n_number_with_alpha" {
+ err(
+ \\[1.2a-3]
+ );
+}
+
+test "n_number_with_leading_zero" {
+ err(
+ \\[012]
+ );
+}
+
+test "n_object_bad_value" {
+ err(
+ \\["x", truth]
+ );
+}
+
+test "n_object_bracket_key" {
+ err(
+ \\{[: "x"}
+ );
+}
+
+test "n_object_comma_instead_of_colon" {
+ err(
+ \\{"x", null}
+ );
+}
+
+test "n_object_double_colon" {
+ err(
+ \\{"x"::"b"}
+ );
+}
+
+test "n_object_emoji" {
+ err(
+ \\{🇨🇭}
+ );
+}
+
+test "n_object_garbage_at_end" {
+ err(
+ \\{"a":"a" 123}
+ );
+}
+
+test "n_object_key_with_single_quotes" {
+ err(
+ \\{key: 'value'}
+ );
+}
+
+test "n_object_lone_continuation_byte_in_key_and_trailing_comma" {
+ err(
+ \\{"¹":"0",}
+ );
+}
+
+test "n_object_missing_colon" {
+ err(
+ \\{"a" b}
+ );
+}
+
+test "n_object_missing_key" {
+ err(
+ \\{:"b"}
+ );
+}
+
+test "n_object_missing_semicolon" {
+ err(
+ \\{"a" "b"}
+ );
+}
+
+test "n_object_missing_value" {
+ err(
+ \\{"a":
+ );
+}
+
+test "n_object_no-colon" {
+ err(
+ \\{"a"
+ );
+}
+
+test "n_object_non_string_key_but_huge_number_instead" {
+ err(
+ \\{9999E9999:1}
+ );
+}
+
+test "n_object_non_string_key" {
+ err(
+ \\{1:1}
+ );
+}
+
+test "n_object_repeated_null_null" {
+ err(
+ \\{null:null,null:null}
+ );
+}
+
+test "n_object_several_trailing_commas" {
+ err(
+ \\{"id":0,,,,,}
+ );
+}
+
+test "n_object_single_quote" {
+ err(
+ \\{'a':0}
+ );
+}
+
+test "n_object_trailing_comma" {
+ err(
+ \\{"id":0,}
+ );
+}
+
+test "n_object_trailing_comment" {
+ err(
+ \\{"a":"b"}/**/
+ );
+}
+
+test "n_object_trailing_comment_open" {
+ err(
+ \\{"a":"b"}/**//
+ );
+}
+
+test "n_object_trailing_comment_slash_open_incomplete" {
+ err(
+ \\{"a":"b"}/
+ );
+}
+
+test "n_object_trailing_comment_slash_open" {
+ err(
+ \\{"a":"b"}//
+ );
+}
+
+test "n_object_two_commas_in_a_row" {
+ err(
+ \\{"a":"b",,"c":"d"}
+ );
+}
+
+test "n_object_unquoted_key" {
+ err(
+ \\{a: "b"}
+ );
+}
+
+test "n_object_unterminated-value" {
+ err(
+ \\{"a":"a
+ );
+}
+
+test "n_object_with_single_string" {
+ err(
+ \\{ "foo" : "bar", "a" }
+ );
+}
+
+test "n_object_with_trailing_garbage" {
+ err(
+ \\{"a":"b"}#
+ );
+}
+
+test "n_single_space" {
+ err(" ");
+}
+
+test "n_string_1_surrogate_then_escape" {
+ err(
+ \\["\uD800\"]
+ );
+}
+
+test "n_string_1_surrogate_then_escape_u1" {
+ err(
+ \\["\uD800\u1"]
+ );
+}
+
+test "n_string_1_surrogate_then_escape_u1x" {
+ err(
+ \\["\uD800\u1x"]
+ );
+}
+
+test "n_string_1_surrogate_then_escape_u" {
+ err(
+ \\["\uD800\u"]
+ );
+}
+
+test "n_string_accentuated_char_no_quotes" {
+ err(
+ \\[é]
+ );
+}
+
+test "n_string_backslash_00" {
+ err("[\"\x00\"]");
+}
+
+test "n_string_escaped_backslash_bad" {
+ err(
+ \\["\\\"]
+ );
+}
+
+test "n_string_escaped_ctrl_char_tab" {
+ err("\x5b\x22\x5c\x09\x22\x5d");
+}
+
+test "n_string_escaped_emoji" {
+ err("[\"\x5c\xc3\xb0\xc2\x9f\xc2\x8c\xc2\x80\"]");
+}
+
+test "n_string_escape_x" {
+ err(
+ \\["\x00"]
+ );
+}
+
+test "n_string_incomplete_escaped_character" {
+ err(
+ \\["\u00A"]
+ );
+}
+
+test "n_string_incomplete_escape" {
+ err(
+ \\["\"]
+ );
+}
+
+test "n_string_incomplete_surrogate_escape_invalid" {
+ err(
+ \\["\uD800\uD800\x"]
+ );
+}
+
+test "n_string_incomplete_surrogate" {
+ err(
+ \\["\uD834\uDd"]
+ );
+}
+
+test "n_string_invalid_backslash_esc" {
+ err(
+ \\["\a"]
+ );
+}
+
+test "n_string_invalid_unicode_escape" {
+ err(
+ \\["\uqqqq"]
+ );
+}
+
+test "n_string_invalid_utf8_after_escape" {
+ err("[\"\\\x75\xc3\xa5\"]");
+}
+
+test "n_string_invalid-utf-8-in-escape" {
+ err(
+ \\["\uå"]
+ );
+}
+
+test "n_string_leading_uescaped_thinspace" {
+ err(
+ \\[\u0020"asd"]
+ );
+}
+
+test "n_string_no_quotes_with_bad_escape" {
+ err(
+ \\[\n]
+ );
+}
+
+test "n_string_single_doublequote" {
+ err(
+ \\"
+ );
+}
+
+test "n_string_single_quote" {
+ err(
+ \\['single quote']
+ );
+}
+
+test "n_string_single_string_no_double_quotes" {
+ err(
+ \\abc
+ );
+}
+
+test "n_string_start_escape_unclosed" {
+ err(
+ \\["\
+ );
+}
+
+test "n_string_unescaped_crtl_char" {
+ err("[\"a\x00a\"]");
+}
+
+test "n_string_unescaped_newline" {
+ err(
+ \\["new
+ \\line"]
+ );
+}
+
+test "n_string_unescaped_tab" {
+ err("[\"\t\"]");
+}
+
+test "n_string_unicode_CapitalU" {
+ err(
+ \\"\UA66D"
+ );
+}
+
+test "n_string_with_trailing_garbage" {
+ err(
+ \\""x
+ );
+}
+
+test "n_structure_100000_opening_arrays" {
+ err("[" ** 100000);
+}
+
+test "n_structure_angle_bracket_." {
+ err(
+ \\<.>
+ );
+}
+
+test "n_structure_angle_bracket_null" {
+ err(
+ \\[<null>]
+ );
+}
+
+test "n_structure_array_trailing_garbage" {
+ err(
+ \\[1]x
+ );
+}
+
+test "n_structure_array_with_extra_array_close" {
+ err(
+ \\[1]]
+ );
+}
+
+test "n_structure_array_with_unclosed_string" {
+ err(
+ \\["asd]
+ );
+}
+
+test "n_structure_ascii-unicode-identifier" {
+ err(
+ \\aå
+ );
+}
+
+test "n_structure_capitalized_True" {
+ err(
+ \\[True]
+ );
+}
+
+test "n_structure_close_unopened_array" {
+ err(
+ \\1]
+ );
+}
+
+test "n_structure_comma_instead_of_closing_brace" {
+ err(
+ \\{"x": true,
+ );
+}
+
+test "n_structure_double_array" {
+ err(
+ \\[][]
+ );
+}
+
+test "n_structure_end_array" {
+ err(
+ \\]
+ );
+}
+
+test "n_structure_incomplete_UTF8_BOM" {
+ err(
+ \\ï»{}
+ );
+}
+
+test "n_structure_lone-invalid-utf-8" {
+ err(
+ \\å
+ );
+}
+
+test "n_structure_lone-open-bracket" {
+ err(
+ \\[
+ );
+}
+
+test "n_structure_no_data" {
+ err(
+ \\
+ );
+}
+
+test "n_structure_null-byte-outside-string" {
+ err("[\x00]");
+}
+
+test "n_structure_number_with_trailing_garbage" {
+ err(
+ \\2@
+ );
+}
+
+test "n_structure_object_followed_by_closing_object" {
+ err(
+ \\{}}
+ );
+}
+
+test "n_structure_object_unclosed_no_value" {
+ err(
+ \\{"":
+ );
+}
+
+test "n_structure_object_with_comment" {
+ err(
+ \\{"a":/*comment*/"b"}
+ );
+}
+
+test "n_structure_object_with_trailing_garbage" {
+ err(
+ \\{"a": true} "x"
+ );
+}
+
+test "n_structure_open_array_apostrophe" {
+ err(
+ \\['
+ );
+}
+
+test "n_structure_open_array_comma" {
+ err(
+ \\[,
+ );
+}
+
+test "n_structure_open_array_object" {
+ err("[{\"\":" ** 50000);
+}
+
+test "n_structure_open_array_open_object" {
+ err(
+ \\[{
+ );
+}
+
+test "n_structure_open_array_open_string" {
+ err(
+ \\["a
+ );
+}
+
+test "n_structure_open_array_string" {
+ err(
+ \\["a"
+ );
+}
+
+test "n_structure_open_object_close_array" {
+ err(
+ \\{]
+ );
+}
+
+test "n_structure_open_object_comma" {
+ err(
+ \\{,
+ );
+}
+
+test "n_structure_open_object" {
+ err(
+ \\{
+ );
+}
+
+test "n_structure_open_object_open_array" {
+ err(
+ \\{[
+ );
+}
+
+test "n_structure_open_object_open_string" {
+ err(
+ \\{"a
+ );
+}
+
+test "n_structure_open_object_string_with_apostrophes" {
+ err(
+ \\{'a'
+ );
+}
+
+test "n_structure_open_open" {
+ err(
+ \\["\{["\{["\{["\{
+ );
+}
+
+test "n_structure_single_eacute" {
+ err(
+ \\é
+ );
+}
+
+test "n_structure_single_star" {
+ err(
+ \\*
+ );
+}
+
+test "n_structure_trailing_#" {
+ err(
+ \\{"a":"b"}#{}
+ );
+}
+
+test "n_structure_U+2060_word_joined" {
+ err(
+ \\[⁠]
+ );
+}
+
+test "n_structure_uescaped_LF_before_string" {
+ err(
+ \\[\u000A""]
+ );
+}
+
+test "n_structure_unclosed_array" {
+ err(
+ \\[1
+ );
+}
+
+test "n_structure_unclosed_array_partial_null" {
+ err(
+ \\[ false, nul
+ );
+}
+
+test "n_structure_unclosed_array_unfinished_false" {
+ err(
+ \\[ true, fals
+ );
+}
+
+test "n_structure_unclosed_array_unfinished_true" {
+ err(
+ \\[ false, tru
+ );
+}
+
+test "n_structure_unclosed_object" {
+ err(
+ \\{"asd":"asd"
+ );
+}
+
+test "n_structure_unicode-identifier" {
+ err(
+ \\Ã¥
+ );
+}
+
+test "n_structure_UTF8_BOM_no_data" {
+ err(
+ \\
+ );
+}
+
+test "n_structure_whitespace_formfeed" {
+ err("[\x0c]");
+}
+
+test "n_structure_whitespace_U+2060_word_joiner" {
+ err(
+ \\[⁠]
+ );
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////
+
+test "i_number_double_huge_neg_exp" {
+ any(
+ \\[123.456e-789]
+ );
+}
+
+test "i_number_huge_exp" {
+ any(
+ \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
+ );
+}
+
+test "i_number_neg_int_huge_exp" {
+ any(
+ \\[-1e+9999]
+ );
+}
+
+test "i_number_pos_double_huge_exp" {
+ any(
+ \\[1.5e+9999]
+ );
+}
+
+test "i_number_real_neg_overflow" {
+ any(
+ \\[-123123e100000]
+ );
+}
+
+test "i_number_real_pos_overflow" {
+ any(
+ \\[123123e100000]
+ );
+}
+
+test "i_number_real_underflow" {
+ any(
+ \\[123e-10000000]
+ );
+}
+
+test "i_number_too_big_neg_int" {
+ any(
+ \\[-123123123123123123123123123123]
+ );
+}
+
+test "i_number_too_big_pos_int" {
+ any(
+ \\[100000000000000000000]
+ );
+}
+
+test "i_number_very_big_negative_int" {
+ any(
+ \\[-237462374673276894279832749832423479823246327846]
+ );
+}
+
+test "i_object_key_lone_2nd_surrogate" {
+ any(
+ \\{"\uDFAA":0}
+ );
+}
+
+test "i_string_1st_surrogate_but_2nd_missing" {
+ any(
+ \\["\uDADA"]
+ );
+}
+
+test "i_string_1st_valid_surrogate_2nd_invalid" {
+ any(
+ \\["\uD888\u1234"]
+ );
+}
+
+test "i_string_incomplete_surrogate_and_escape_valid" {
+ any(
+ \\["\uD800\n"]
+ );
+}
+
+test "i_string_incomplete_surrogate_pair" {
+ any(
+ \\["\uDd1ea"]
+ );
+}
+
+test "i_string_incomplete_surrogates_escape_valid" {
+ any(
+ \\["\uD800\uD800\n"]
+ );
+}
+
+test "i_string_invalid_lonely_surrogate" {
+ any(
+ \\["\ud800"]
+ );
+}
+
+test "i_string_invalid_surrogate" {
+ any(
+ \\["\ud800abc"]
+ );
+}
+
+test "i_string_invalid_utf-8" {
+ any(
+ \\["ÿ"]
+ );
+}
+
+test "i_string_inverted_surrogates_U+1D11E" {
+ any(
+ \\["\uDd1e\uD834"]
+ );
+}
+
+test "i_string_iso_latin_1" {
+ any(
+ \\["é"]
+ );
+}
+
+test "i_string_lone_second_surrogate" {
+ any(
+ \\["\uDFAA"]
+ );
+}
+
+test "i_string_lone_utf8_continuation_byte" {
+ any(
+ \\[""]
+ );
+}
+
+test "i_string_not_in_unicode_range" {
+ any(
+ \\["ô¿¿¿"]
+ );
+}
+
+test "i_string_overlong_sequence_2_bytes" {
+ any(
+ \\["À¯"]
+ );
+}
+
+test "i_string_overlong_sequence_6_bytes" {
+ any(
+ \\["üƒ¿¿¿¿"]
+ );
+}
+
+test "i_string_overlong_sequence_6_bytes_null" {
+ any(
+ \\["ü€€€€€"]
+ );
+}
+
+test "i_string_truncated-utf-8" {
+ any(
+ \\["àÿ"]
+ );
+}
+
+test "i_string_utf16BE_no_BOM" {
+ any("\x00\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d");
+}
+
+test "i_string_utf16LE_no_BOM" {
+ any("\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d\x00");
+}
+
+test "i_string_UTF-16LE_with_BOM" {
+ any("\xc3\xbf\xc3\xbe\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d\x00");
+}
+
+test "i_string_UTF-8_invalid_sequence" {
+ any(
+ \\["日шú"]
+ );
+}
+
+test "i_string_UTF8_surrogate_U+D800" {
+ any(
+ \\["í €"]
+ );
+}
+
+test "i_structure_500_nested_arrays" {
+ any(("[" ** 500) ++ ("]" ** 500));
+}
+
+test "i_structure_UTF-8_BOM_empty_object" {
+ any(
+ \\{}
+ );
+}
diff --git a/std/json_test.zig b/std/json_test.zig
deleted file mode 100644
index a323e6e979..0000000000
--- a/std/json_test.zig
+++ /dev/null
@@ -1,1904 +0,0 @@
-// RFC 8529 conformance tests.
-//
-// Tests are taken from https://github.com/nst/JSONTestSuite
-// Read also http://seriot.ch/parsing_json.php for a good overview.
-
-const std = @import("std.zig");
-
-fn ok(comptime s: []const u8) void {
- std.testing.expect(std.json.validate(s));
-}
-
-fn err(comptime s: []const u8) void {
- std.testing.expect(!std.json.validate(s));
-}
-
-fn any(comptime s: []const u8) void {
- std.testing.expect(true);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-//
-// Additional tests not part of test JSONTestSuite.
-
-test "json.test.y_trailing_comma_after_empty" {
- ok(
- \\{"1":[],"2":{},"3":"4"}
- );
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-test "json.test.y_array_arraysWithSpaces" {
- ok(
- \\[[] ]
- );
-}
-
-test "json.test.y_array_empty" {
- ok(
- \\[]
- );
-}
-
-test "json.test.y_array_empty-string" {
- ok(
- \\[""]
- );
-}
-
-test "json.test.y_array_ending_with_newline" {
- ok(
- \\["a"]
- );
-}
-
-test "json.test.y_array_false" {
- ok(
- \\[false]
- );
-}
-
-test "json.test.y_array_heterogeneous" {
- ok(
- \\[null, 1, "1", {}]
- );
-}
-
-test "json.test.y_array_null" {
- ok(
- \\[null]
- );
-}
-
-test "json.test.y_array_with_1_and_newline" {
- ok(
- \\[1
- \\]
- );
-}
-
-test "json.test.y_array_with_leading_space" {
- ok(
- \\ [1]
- );
-}
-
-test "json.test.y_array_with_several_null" {
- ok(
- \\[1,null,null,null,2]
- );
-}
-
-test "json.test.y_array_with_trailing_space" {
- ok("[2] ");
-}
-
-test "json.test.y_number_0e+1" {
- ok(
- \\[0e+1]
- );
-}
-
-test "json.test.y_number_0e1" {
- ok(
- \\[0e1]
- );
-}
-
-test "json.test.y_number_after_space" {
- ok(
- \\[ 4]
- );
-}
-
-test "json.test.y_number_double_close_to_zero" {
- ok(
- \\[-0.000000000000000000000000000000000000000000000000000000000000000000000000000001]
- );
-}
-
-test "json.test.y_number_int_with_exp" {
- ok(
- \\[20e1]
- );
-}
-
-test "json.test.y_number" {
- ok(
- \\[123e65]
- );
-}
-
-test "json.test.y_number_minus_zero" {
- ok(
- \\[-0]
- );
-}
-
-test "json.test.y_number_negative_int" {
- ok(
- \\[-123]
- );
-}
-
-test "json.test.y_number_negative_one" {
- ok(
- \\[-1]
- );
-}
-
-test "json.test.y_number_negative_zero" {
- ok(
- \\[-0]
- );
-}
-
-test "json.test.y_number_real_capital_e" {
- ok(
- \\[1E22]
- );
-}
-
-test "json.test.y_number_real_capital_e_neg_exp" {
- ok(
- \\[1E-2]
- );
-}
-
-test "json.test.y_number_real_capital_e_pos_exp" {
- ok(
- \\[1E+2]
- );
-}
-
-test "json.test.y_number_real_exponent" {
- ok(
- \\[123e45]
- );
-}
-
-test "json.test.y_number_real_fraction_exponent" {
- ok(
- \\[123.456e78]
- );
-}
-
-test "json.test.y_number_real_neg_exp" {
- ok(
- \\[1e-2]
- );
-}
-
-test "json.test.y_number_real_pos_exponent" {
- ok(
- \\[1e+2]
- );
-}
-
-test "json.test.y_number_simple_int" {
- ok(
- \\[123]
- );
-}
-
-test "json.test.y_number_simple_real" {
- ok(
- \\[123.456789]
- );
-}
-
-test "json.test.y_object_basic" {
- ok(
- \\{"asd":"sdf"}
- );
-}
-
-test "json.test.y_object_duplicated_key_and_value" {
- ok(
- \\{"a":"b","a":"b"}
- );
-}
-
-test "json.test.y_object_duplicated_key" {
- ok(
- \\{"a":"b","a":"c"}
- );
-}
-
-test "json.test.y_object_empty" {
- ok(
- \\{}
- );
-}
-
-test "json.test.y_object_empty_key" {
- ok(
- \\{"":0}
- );
-}
-
-test "json.test.y_object_escaped_null_in_key" {
- ok(
- \\{"foo\u0000bar": 42}
- );
-}
-
-test "json.test.y_object_extreme_numbers" {
- ok(
- \\{ "min": -1.0e+28, "max": 1.0e+28 }
- );
-}
-
-test "json.test.y_object" {
- ok(
- \\{"asd":"sdf", "dfg":"fgh"}
- );
-}
-
-test "json.test.y_object_long_strings" {
- ok(
- \\{"x":[{"id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}], "id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}
- );
-}
-
-test "json.test.y_object_simple" {
- ok(
- \\{"a":[]}
- );
-}
-
-test "json.test.y_object_string_unicode" {
- ok(
- \\{"title":"\u041f\u043e\u043b\u0442\u043e\u0440\u0430 \u0417\u0435\u043c\u043b\u0435\u043a\u043e\u043f\u0430" }
- );
-}
-
-test "json.test.y_object_with_newlines" {
- ok(
- \\{
- \\"a": "b"
- \\}
- );
-}
-
-test "json.test.y_string_1_2_3_bytes_UTF-8_sequences" {
- ok(
- \\["\u0060\u012a\u12AB"]
- );
-}
-
-test "json.test.y_string_accepted_surrogate_pair" {
- ok(
- \\["\uD801\udc37"]
- );
-}
-
-test "json.test.y_string_accepted_surrogate_pairs" {
- ok(
- \\["\ud83d\ude39\ud83d\udc8d"]
- );
-}
-
-test "json.test.y_string_allowed_escapes" {
- ok(
- \\["\"\\\/\b\f\n\r\t"]
- );
-}
-
-test "json.test.y_string_backslash_and_u_escaped_zero" {
- ok(
- \\["\\u0000"]
- );
-}
-
-test "json.test.y_string_backslash_doublequotes" {
- ok(
- \\["\""]
- );
-}
-
-test "json.test.y_string_comments" {
- ok(
- \\["a/*b*/c/*d//e"]
- );
-}
-
-test "json.test.y_string_double_escape_a" {
- ok(
- \\["\\a"]
- );
-}
-
-test "json.test.y_string_double_escape_n" {
- ok(
- \\["\\n"]
- );
-}
-
-test "json.test.y_string_escaped_control_character" {
- ok(
- \\["\u0012"]
- );
-}
-
-test "json.test.y_string_escaped_noncharacter" {
- ok(
- \\["\uFFFF"]
- );
-}
-
-test "json.test.y_string_in_array" {
- ok(
- \\["asd"]
- );
-}
-
-test "json.test.y_string_in_array_with_leading_space" {
- ok(
- \\[ "asd"]
- );
-}
-
-test "json.test.y_string_last_surrogates_1_and_2" {
- ok(
- \\["\uDBFF\uDFFF"]
- );
-}
-
-test "json.test.y_string_nbsp_uescaped" {
- ok(
- \\["new\u00A0line"]
- );
-}
-
-test "json.test.y_string_nonCharacterInUTF-8_U+10FFFF" {
- ok(
- \\["􏿿"]
- );
-}
-
-test "json.test.y_string_nonCharacterInUTF-8_U+FFFF" {
- ok(
- \\["￿"]
- );
-}
-
-test "json.test.y_string_null_escape" {
- ok(
- \\["\u0000"]
- );
-}
-
-test "json.test.y_string_one-byte-utf-8" {
- ok(
- \\["\u002c"]
- );
-}
-
-test "json.test.y_string_pi" {
- ok(
- \\["π"]
- );
-}
-
-test "json.test.y_string_reservedCharacterInUTF-8_U+1BFFF" {
- ok(
- \\["𛿿"]
- );
-}
-
-test "json.test.y_string_simple_ascii" {
- ok(
- \\["asd "]
- );
-}
-
-test "json.test.y_string_space" {
- ok(
- \\" "
- );
-}
-
-test "json.test.y_string_surrogates_U+1D11E_MUSICAL_SYMBOL_G_CLEF" {
- ok(
- \\["\uD834\uDd1e"]
- );
-}
-
-test "json.test.y_string_three-byte-utf-8" {
- ok(
- \\["\u0821"]
- );
-}
-
-test "json.test.y_string_two-byte-utf-8" {
- ok(
- \\["\u0123"]
- );
-}
-
-test "json.test.y_string_u+2028_line_sep" {
- ok("[\"\xe2\x80\xa8\"]");
-}
-
-test "json.test.y_string_u+2029_par_sep" {
- ok("[\"\xe2\x80\xa9\"]");
-}
-
-test "json.test.y_string_uescaped_newline" {
- ok(
- \\["new\u000Aline"]
- );
-}
-
-test "json.test.y_string_uEscape" {
- ok(
- \\["\u0061\u30af\u30EA\u30b9"]
- );
-}
-
-test "json.test.y_string_unescaped_char_delete" {
- ok("[\"\x7f\"]");
-}
-
-test "json.test.y_string_unicode_2" {
- ok(
- \\["⍂㈴⍂"]
- );
-}
-
-test "json.test.y_string_unicodeEscapedBackslash" {
- ok(
- \\["\u005C"]
- );
-}
-
-test "json.test.y_string_unicode_escaped_double_quote" {
- ok(
- \\["\u0022"]
- );
-}
-
-test "json.test.y_string_unicode" {
- ok(
- \\["\uA66D"]
- );
-}
-
-test "json.test.y_string_unicode_U+10FFFE_nonchar" {
- ok(
- \\["\uDBFF\uDFFE"]
- );
-}
-
-test "json.test.y_string_unicode_U+1FFFE_nonchar" {
- ok(
- \\["\uD83F\uDFFE"]
- );
-}
-
-test "json.test.y_string_unicode_U+200B_ZERO_WIDTH_SPACE" {
- ok(
- \\["\u200B"]
- );
-}
-
-test "json.test.y_string_unicode_U+2064_invisible_plus" {
- ok(
- \\["\u2064"]
- );
-}
-
-test "json.test.y_string_unicode_U+FDD0_nonchar" {
- ok(
- \\["\uFDD0"]
- );
-}
-
-test "json.test.y_string_unicode_U+FFFE_nonchar" {
- ok(
- \\["\uFFFE"]
- );
-}
-
-test "json.test.y_string_utf8" {
- ok(
- \\["€𝄞"]
- );
-}
-
-test "json.test.y_string_with_del_character" {
- ok("[\"a\x7fa\"]");
-}
-
-test "json.test.y_structure_lonely_false" {
- ok(
- \\false
- );
-}
-
-test "json.test.y_structure_lonely_int" {
- ok(
- \\42
- );
-}
-
-test "json.test.y_structure_lonely_negative_real" {
- ok(
- \\-0.1
- );
-}
-
-test "json.test.y_structure_lonely_null" {
- ok(
- \\null
- );
-}
-
-test "json.test.y_structure_lonely_string" {
- ok(
- \\"asd"
- );
-}
-
-test "json.test.y_structure_lonely_true" {
- ok(
- \\true
- );
-}
-
-test "json.test.y_structure_string_empty" {
- ok(
- \\""
- );
-}
-
-test "json.test.y_structure_trailing_newline" {
- ok(
- \\["a"]
- );
-}
-
-test "json.test.y_structure_true_in_array" {
- ok(
- \\[true]
- );
-}
-
-test "json.test.y_structure_whitespace_array" {
- ok(" [] ");
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-test "json.test.n_array_1_true_without_comma" {
- err(
- \\[1 true]
- );
-}
-
-test "json.test.n_array_a_invalid_utf8" {
- err(
- \\[aå]
- );
-}
-
-test "json.test.n_array_colon_instead_of_comma" {
- err(
- \\["": 1]
- );
-}
-
-test "json.test.n_array_comma_after_close" {
- //err(
- // \\[""],
- //);
-}
-
-test "json.test.n_array_comma_and_number" {
- err(
- \\[,1]
- );
-}
-
-test "json.test.n_array_double_comma" {
- err(
- \\[1,,2]
- );
-}
-
-test "json.test.n_array_double_extra_comma" {
- err(
- \\["x",,]
- );
-}
-
-test "json.test.n_array_extra_close" {
- err(
- \\["x"]]
- );
-}
-
-test "json.test.n_array_extra_comma" {
- //err(
- // \\["",]
- //);
-}
-
-test "json.test.n_array_incomplete_invalid_value" {
- err(
- \\[x
- );
-}
-
-test "json.test.n_array_incomplete" {
- err(
- \\["x"
- );
-}
-
-test "json.test.n_array_inner_array_no_comma" {
- err(
- \\[3[4]]
- );
-}
-
-test "json.test.n_array_invalid_utf8" {
- err(
- \\[ÿ]
- );
-}
-
-test "json.test.n_array_items_separated_by_semicolon" {
- err(
- \\[1:2]
- );
-}
-
-test "json.test.n_array_just_comma" {
- err(
- \\[,]
- );
-}
-
-test "json.test.n_array_just_minus" {
- err(
- \\[-]
- );
-}
-
-test "json.test.n_array_missing_value" {
- err(
- \\[ , ""]
- );
-}
-
-test "json.test.n_array_newlines_unclosed" {
- err(
- \\["a",
- \\4
- \\,1,
- );
-}
-
-test "json.test.n_array_number_and_comma" {
- err(
- \\[1,]
- );
-}
-
-test "json.test.n_array_number_and_several_commas" {
- err(
- \\[1,,]
- );
-}
-
-test "json.test.n_array_spaces_vertical_tab_formfeed" {
- err("[\"\x0aa\"\\f]");
-}
-
-test "json.test.n_array_star_inside" {
- err(
- \\[*]
- );
-}
-
-test "json.test.n_array_unclosed" {
- err(
- \\[""
- );
-}
-
-test "json.test.n_array_unclosed_trailing_comma" {
- err(
- \\[1,
- );
-}
-
-test "json.test.n_array_unclosed_with_new_lines" {
- err(
- \\[1,
- \\1
- \\,1
- );
-}
-
-test "json.test.n_array_unclosed_with_object_inside" {
- err(
- \\[{}
- );
-}
-
-test "json.test.n_incomplete_false" {
- err(
- \\[fals]
- );
-}
-
-test "json.test.n_incomplete_null" {
- err(
- \\[nul]
- );
-}
-
-test "json.test.n_incomplete_true" {
- err(
- \\[tru]
- );
-}
-
-test "json.test.n_multidigit_number_then_00" {
- err("123\x00");
-}
-
-test "json.test.n_number_0.1.2" {
- err(
- \\[0.1.2]
- );
-}
-
-test "json.test.n_number_-01" {
- err(
- \\[-01]
- );
-}
-
-test "json.test.n_number_0.3e" {
- err(
- \\[0.3e]
- );
-}
-
-test "json.test.n_number_0.3e+" {
- err(
- \\[0.3e+]
- );
-}
-
-test "json.test.n_number_0_capital_E" {
- err(
- \\[0E]
- );
-}
-
-test "json.test.n_number_0_capital_E+" {
- err(
- \\[0E+]
- );
-}
-
-test "json.test.n_number_0.e1" {
- err(
- \\[0.e1]
- );
-}
-
-test "json.test.n_number_0e" {
- err(
- \\[0e]
- );
-}
-
-test "json.test.n_number_0e+" {
- err(
- \\[0e+]
- );
-}
-
-test "json.test.n_number_1_000" {
- err(
- \\[1 000.0]
- );
-}
-
-test "json.test.n_number_1.0e-" {
- err(
- \\[1.0e-]
- );
-}
-
-test "json.test.n_number_1.0e" {
- err(
- \\[1.0e]
- );
-}
-
-test "json.test.n_number_1.0e+" {
- err(
- \\[1.0e+]
- );
-}
-
-test "json.test.n_number_-1.0." {
- err(
- \\[-1.0.]
- );
-}
-
-test "json.test.n_number_1eE2" {
- err(
- \\[1eE2]
- );
-}
-
-test "json.test.n_number_.-1" {
- err(
- \\[.-1]
- );
-}
-
-test "json.test.n_number_+1" {
- err(
- \\[+1]
- );
-}
-
-test "json.test.n_number_.2e-3" {
- err(
- \\[.2e-3]
- );
-}
-
-test "json.test.n_number_2.e-3" {
- err(
- \\[2.e-3]
- );
-}
-
-test "json.test.n_number_2.e+3" {
- err(
- \\[2.e+3]
- );
-}
-
-test "json.test.n_number_2.e3" {
- err(
- \\[2.e3]
- );
-}
-
-test "json.test.n_number_-2." {
- err(
- \\[-2.]
- );
-}
-
-test "json.test.n_number_9.e+" {
- err(
- \\[9.e+]
- );
-}
-
-test "json.test.n_number_expression" {
- err(
- \\[1+2]
- );
-}
-
-test "json.test.n_number_hex_1_digit" {
- err(
- \\[0x1]
- );
-}
-
-test "json.test.n_number_hex_2_digits" {
- err(
- \\[0x42]
- );
-}
-
-test "json.test.n_number_infinity" {
- err(
- \\[Infinity]
- );
-}
-
-test "json.test.n_number_+Inf" {
- err(
- \\[+Inf]
- );
-}
-
-test "json.test.n_number_Inf" {
- err(
- \\[Inf]
- );
-}
-
-test "json.test.n_number_invalid+-" {
- err(
- \\[0e+-1]
- );
-}
-
-test "json.test.n_number_invalid-negative-real" {
- err(
- \\[-123.123foo]
- );
-}
-
-test "json.test.n_number_invalid-utf-8-in-bigger-int" {
- err(
- \\[123å]
- );
-}
-
-test "json.test.n_number_invalid-utf-8-in-exponent" {
- err(
- \\[1e1å]
- );
-}
-
-test "json.test.n_number_invalid-utf-8-in-int" {
- err(
- \\[0å]
- );
-}
-
-test "json.test.n_number_++" {
- err(
- \\[++1234]
- );
-}
-
-test "json.test.n_number_minus_infinity" {
- err(
- \\[-Infinity]
- );
-}
-
-test "json.test.n_number_minus_sign_with_trailing_garbage" {
- err(
- \\[-foo]
- );
-}
-
-test "json.test.n_number_minus_space_1" {
- err(
- \\[- 1]
- );
-}
-
-test "json.test.n_number_-NaN" {
- err(
- \\[-NaN]
- );
-}
-
-test "json.test.n_number_NaN" {
- err(
- \\[NaN]
- );
-}
-
-test "json.test.n_number_neg_int_starting_with_zero" {
- err(
- \\[-012]
- );
-}
-
-test "json.test.n_number_neg_real_without_int_part" {
- err(
- \\[-.123]
- );
-}
-
-test "json.test.n_number_neg_with_garbage_at_end" {
- err(
- \\[-1x]
- );
-}
-
-test "json.test.n_number_real_garbage_after_e" {
- err(
- \\[1ea]
- );
-}
-
-test "json.test.n_number_real_with_invalid_utf8_after_e" {
- err(
- \\[1eå]
- );
-}
-
-test "json.test.n_number_real_without_fractional_part" {
- err(
- \\[1.]
- );
-}
-
-test "json.test.n_number_starting_with_dot" {
- err(
- \\[.123]
- );
-}
-
-test "json.test.n_number_U+FF11_fullwidth_digit_one" {
- err(
- \\[1]
- );
-}
-
-test "json.test.n_number_with_alpha_char" {
- err(
- \\[1.8011670033376514H-308]
- );
-}
-
-test "json.test.n_number_with_alpha" {
- err(
- \\[1.2a-3]
- );
-}
-
-test "json.test.n_number_with_leading_zero" {
- err(
- \\[012]
- );
-}
-
-test "json.test.n_object_bad_value" {
- err(
- \\["x", truth]
- );
-}
-
-test "json.test.n_object_bracket_key" {
- err(
- \\{[: "x"}
- );
-}
-
-test "json.test.n_object_comma_instead_of_colon" {
- err(
- \\{"x", null}
- );
-}
-
-test "json.test.n_object_double_colon" {
- err(
- \\{"x"::"b"}
- );
-}
-
-test "json.test.n_object_emoji" {
- err(
- \\{🇨🇭}
- );
-}
-
-test "json.test.n_object_garbage_at_end" {
- err(
- \\{"a":"a" 123}
- );
-}
-
-test "json.test.n_object_key_with_single_quotes" {
- err(
- \\{key: 'value'}
- );
-}
-
-test "json.test.n_object_lone_continuation_byte_in_key_and_trailing_comma" {
- err(
- \\{"¹":"0",}
- );
-}
-
-test "json.test.n_object_missing_colon" {
- err(
- \\{"a" b}
- );
-}
-
-test "json.test.n_object_missing_key" {
- err(
- \\{:"b"}
- );
-}
-
-test "json.test.n_object_missing_semicolon" {
- err(
- \\{"a" "b"}
- );
-}
-
-test "json.test.n_object_missing_value" {
- err(
- \\{"a":
- );
-}
-
-test "json.test.n_object_no-colon" {
- err(
- \\{"a"
- );
-}
-
-test "json.test.n_object_non_string_key_but_huge_number_instead" {
- err(
- \\{9999E9999:1}
- );
-}
-
-test "json.test.n_object_non_string_key" {
- err(
- \\{1:1}
- );
-}
-
-test "json.test.n_object_repeated_null_null" {
- err(
- \\{null:null,null:null}
- );
-}
-
-test "json.test.n_object_several_trailing_commas" {
- err(
- \\{"id":0,,,,,}
- );
-}
-
-test "json.test.n_object_single_quote" {
- err(
- \\{'a':0}
- );
-}
-
-test "json.test.n_object_trailing_comma" {
- err(
- \\{"id":0,}
- );
-}
-
-test "json.test.n_object_trailing_comment" {
- err(
- \\{"a":"b"}/**/
- );
-}
-
-test "json.test.n_object_trailing_comment_open" {
- err(
- \\{"a":"b"}/**//
- );
-}
-
-test "json.test.n_object_trailing_comment_slash_open_incomplete" {
- err(
- \\{"a":"b"}/
- );
-}
-
-test "json.test.n_object_trailing_comment_slash_open" {
- err(
- \\{"a":"b"}//
- );
-}
-
-test "json.test.n_object_two_commas_in_a_row" {
- err(
- \\{"a":"b",,"c":"d"}
- );
-}
-
-test "json.test.n_object_unquoted_key" {
- err(
- \\{a: "b"}
- );
-}
-
-test "json.test.n_object_unterminated-value" {
- err(
- \\{"a":"a
- );
-}
-
-test "json.test.n_object_with_single_string" {
- err(
- \\{ "foo" : "bar", "a" }
- );
-}
-
-test "json.test.n_object_with_trailing_garbage" {
- err(
- \\{"a":"b"}#
- );
-}
-
-test "json.test.n_single_space" {
- err(" ");
-}
-
-test "json.test.n_string_1_surrogate_then_escape" {
- err(
- \\["\uD800\"]
- );
-}
-
-test "json.test.n_string_1_surrogate_then_escape_u1" {
- err(
- \\["\uD800\u1"]
- );
-}
-
-test "json.test.n_string_1_surrogate_then_escape_u1x" {
- err(
- \\["\uD800\u1x"]
- );
-}
-
-test "json.test.n_string_1_surrogate_then_escape_u" {
- err(
- \\["\uD800\u"]
- );
-}
-
-test "json.test.n_string_accentuated_char_no_quotes" {
- err(
- \\[é]
- );
-}
-
-test "json.test.n_string_backslash_00" {
- err("[\"\x00\"]");
-}
-
-test "json.test.n_string_escaped_backslash_bad" {
- err(
- \\["\\\"]
- );
-}
-
-test "json.test.n_string_escaped_ctrl_char_tab" {
- err("\x5b\x22\x5c\x09\x22\x5d");
-}
-
-test "json.test.n_string_escaped_emoji" {
- err("[\"\x5c\xc3\xb0\xc2\x9f\xc2\x8c\xc2\x80\"]");
-}
-
-test "json.test.n_string_escape_x" {
- err(
- \\["\x00"]
- );
-}
-
-test "json.test.n_string_incomplete_escaped_character" {
- err(
- \\["\u00A"]
- );
-}
-
-test "json.test.n_string_incomplete_escape" {
- err(
- \\["\"]
- );
-}
-
-test "json.test.n_string_incomplete_surrogate_escape_invalid" {
- err(
- \\["\uD800\uD800\x"]
- );
-}
-
-test "json.test.n_string_incomplete_surrogate" {
- err(
- \\["\uD834\uDd"]
- );
-}
-
-test "json.test.n_string_invalid_backslash_esc" {
- err(
- \\["\a"]
- );
-}
-
-test "json.test.n_string_invalid_unicode_escape" {
- err(
- \\["\uqqqq"]
- );
-}
-
-test "json.test.n_string_invalid_utf8_after_escape" {
- err("[\"\\\x75\xc3\xa5\"]");
-}
-
-test "json.test.n_string_invalid-utf-8-in-escape" {
- err(
- \\["\uå"]
- );
-}
-
-test "json.test.n_string_leading_uescaped_thinspace" {
- err(
- \\[\u0020"asd"]
- );
-}
-
-test "json.test.n_string_no_quotes_with_bad_escape" {
- err(
- \\[\n]
- );
-}
-
-test "json.test.n_string_single_doublequote" {
- err(
- \\"
- );
-}
-
-test "json.test.n_string_single_quote" {
- err(
- \\['single quote']
- );
-}
-
-test "json.test.n_string_single_string_no_double_quotes" {
- err(
- \\abc
- );
-}
-
-test "json.test.n_string_start_escape_unclosed" {
- err(
- \\["\
- );
-}
-
-test "json.test.n_string_unescaped_crtl_char" {
- err("[\"a\x00a\"]");
-}
-
-test "json.test.n_string_unescaped_newline" {
- err(
- \\["new
- \\line"]
- );
-}
-
-test "json.test.n_string_unescaped_tab" {
- err("[\"\t\"]");
-}
-
-test "json.test.n_string_unicode_CapitalU" {
- err(
- \\"\UA66D"
- );
-}
-
-test "json.test.n_string_with_trailing_garbage" {
- err(
- \\""x
- );
-}
-
-test "json.test.n_structure_100000_opening_arrays" {
- err("[" ** 100000);
-}
-
-test "json.test.n_structure_angle_bracket_." {
- err(
- \\<.>
- );
-}
-
-test "json.test.n_structure_angle_bracket_null" {
- err(
- \\[<null>]
- );
-}
-
-test "json.test.n_structure_array_trailing_garbage" {
- err(
- \\[1]x
- );
-}
-
-test "json.test.n_structure_array_with_extra_array_close" {
- err(
- \\[1]]
- );
-}
-
-test "json.test.n_structure_array_with_unclosed_string" {
- err(
- \\["asd]
- );
-}
-
-test "json.test.n_structure_ascii-unicode-identifier" {
- err(
- \\aå
- );
-}
-
-test "json.test.n_structure_capitalized_True" {
- err(
- \\[True]
- );
-}
-
-test "json.test.n_structure_close_unopened_array" {
- err(
- \\1]
- );
-}
-
-test "json.test.n_structure_comma_instead_of_closing_brace" {
- err(
- \\{"x": true,
- );
-}
-
-test "json.test.n_structure_double_array" {
- err(
- \\[][]
- );
-}
-
-test "json.test.n_structure_end_array" {
- err(
- \\]
- );
-}
-
-test "json.test.n_structure_incomplete_UTF8_BOM" {
- err(
- \\ï»{}
- );
-}
-
-test "json.test.n_structure_lone-invalid-utf-8" {
- err(
- \\å
- );
-}
-
-test "json.test.n_structure_lone-open-bracket" {
- err(
- \\[
- );
-}
-
-test "json.test.n_structure_no_data" {
- err(
- \\
- );
-}
-
-test "json.test.n_structure_null-byte-outside-string" {
- err("[\x00]");
-}
-
-test "json.test.n_structure_number_with_trailing_garbage" {
- err(
- \\2@
- );
-}
-
-test "json.test.n_structure_object_followed_by_closing_object" {
- err(
- \\{}}
- );
-}
-
-test "json.test.n_structure_object_unclosed_no_value" {
- err(
- \\{"":
- );
-}
-
-test "json.test.n_structure_object_with_comment" {
- err(
- \\{"a":/*comment*/"b"}
- );
-}
-
-test "json.test.n_structure_object_with_trailing_garbage" {
- err(
- \\{"a": true} "x"
- );
-}
-
-test "json.test.n_structure_open_array_apostrophe" {
- err(
- \\['
- );
-}
-
-test "json.test.n_structure_open_array_comma" {
- err(
- \\[,
- );
-}
-
-test "json.test.n_structure_open_array_object" {
- err("[{\"\":" ** 50000);
-}
-
-test "json.test.n_structure_open_array_open_object" {
- err(
- \\[{
- );
-}
-
-test "json.test.n_structure_open_array_open_string" {
- err(
- \\["a
- );
-}
-
-test "json.test.n_structure_open_array_string" {
- err(
- \\["a"
- );
-}
-
-test "json.test.n_structure_open_object_close_array" {
- err(
- \\{]
- );
-}
-
-test "json.test.n_structure_open_object_comma" {
- err(
- \\{,
- );
-}
-
-test "json.test.n_structure_open_object" {
- err(
- \\{
- );
-}
-
-test "json.test.n_structure_open_object_open_array" {
- err(
- \\{[
- );
-}
-
-test "json.test.n_structure_open_object_open_string" {
- err(
- \\{"a
- );
-}
-
-test "json.test.n_structure_open_object_string_with_apostrophes" {
- err(
- \\{'a'
- );
-}
-
-test "json.test.n_structure_open_open" {
- err(
- \\["\{["\{["\{["\{
- );
-}
-
-test "json.test.n_structure_single_eacute" {
- err(
- \\é
- );
-}
-
-test "json.test.n_structure_single_star" {
- err(
- \\*
- );
-}
-
-test "json.test.n_structure_trailing_#" {
- err(
- \\{"a":"b"}#{}
- );
-}
-
-test "json.test.n_structure_U+2060_word_joined" {
- err(
- \\[⁠]
- );
-}
-
-test "json.test.n_structure_uescaped_LF_before_string" {
- err(
- \\[\u000A""]
- );
-}
-
-test "json.test.n_structure_unclosed_array" {
- err(
- \\[1
- );
-}
-
-test "json.test.n_structure_unclosed_array_partial_null" {
- err(
- \\[ false, nul
- );
-}
-
-test "json.test.n_structure_unclosed_array_unfinished_false" {
- err(
- \\[ true, fals
- );
-}
-
-test "json.test.n_structure_unclosed_array_unfinished_true" {
- err(
- \\[ false, tru
- );
-}
-
-test "json.test.n_structure_unclosed_object" {
- err(
- \\{"asd":"asd"
- );
-}
-
-test "json.test.n_structure_unicode-identifier" {
- err(
- \\Ã¥
- );
-}
-
-test "json.test.n_structure_UTF8_BOM_no_data" {
- err(
- \\
- );
-}
-
-test "json.test.n_structure_whitespace_formfeed" {
- err("[\x0c]");
-}
-
-test "json.test.n_structure_whitespace_U+2060_word_joiner" {
- err(
- \\[⁠]
- );
-}
-
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
-test "json.test.i_number_double_huge_neg_exp" {
- any(
- \\[123.456e-789]
- );
-}
-
-test "json.test.i_number_huge_exp" {
- any(
- \\[0.4e00669999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999969999999006]
- );
-}
-
-test "json.test.i_number_neg_int_huge_exp" {
- any(
- \\[-1e+9999]
- );
-}
-
-test "json.test.i_number_pos_double_huge_exp" {
- any(
- \\[1.5e+9999]
- );
-}
-
-test "json.test.i_number_real_neg_overflow" {
- any(
- \\[-123123e100000]
- );
-}
-
-test "json.test.i_number_real_pos_overflow" {
- any(
- \\[123123e100000]
- );
-}
-
-test "json.test.i_number_real_underflow" {
- any(
- \\[123e-10000000]
- );
-}
-
-test "json.test.i_number_too_big_neg_int" {
- any(
- \\[-123123123123123123123123123123]
- );
-}
-
-test "json.test.i_number_too_big_pos_int" {
- any(
- \\[100000000000000000000]
- );
-}
-
-test "json.test.i_number_very_big_negative_int" {
- any(
- \\[-237462374673276894279832749832423479823246327846]
- );
-}
-
-test "json.test.i_object_key_lone_2nd_surrogate" {
- any(
- \\{"\uDFAA":0}
- );
-}
-
-test "json.test.i_string_1st_surrogate_but_2nd_missing" {
- any(
- \\["\uDADA"]
- );
-}
-
-test "json.test.i_string_1st_valid_surrogate_2nd_invalid" {
- any(
- \\["\uD888\u1234"]
- );
-}
-
-test "json.test.i_string_incomplete_surrogate_and_escape_valid" {
- any(
- \\["\uD800\n"]
- );
-}
-
-test "json.test.i_string_incomplete_surrogate_pair" {
- any(
- \\["\uDd1ea"]
- );
-}
-
-test "json.test.i_string_incomplete_surrogates_escape_valid" {
- any(
- \\["\uD800\uD800\n"]
- );
-}
-
-test "json.test.i_string_invalid_lonely_surrogate" {
- any(
- \\["\ud800"]
- );
-}
-
-test "json.test.i_string_invalid_surrogate" {
- any(
- \\["\ud800abc"]
- );
-}
-
-test "json.test.i_string_invalid_utf-8" {
- any(
- \\["ÿ"]
- );
-}
-
-test "json.test.i_string_inverted_surrogates_U+1D11E" {
- any(
- \\["\uDd1e\uD834"]
- );
-}
-
-test "json.test.i_string_iso_latin_1" {
- any(
- \\["é"]
- );
-}
-
-test "json.test.i_string_lone_second_surrogate" {
- any(
- \\["\uDFAA"]
- );
-}
-
-test "json.test.i_string_lone_utf8_continuation_byte" {
- any(
- \\[""]
- );
-}
-
-test "json.test.i_string_not_in_unicode_range" {
- any(
- \\["ô¿¿¿"]
- );
-}
-
-test "json.test.i_string_overlong_sequence_2_bytes" {
- any(
- \\["À¯"]
- );
-}
-
-test "json.test.i_string_overlong_sequence_6_bytes" {
- any(
- \\["üƒ¿¿¿¿"]
- );
-}
-
-test "json.test.i_string_overlong_sequence_6_bytes_null" {
- any(
- \\["ü€€€€€"]
- );
-}
-
-test "json.test.i_string_truncated-utf-8" {
- any(
- \\["àÿ"]
- );
-}
-
-test "json.test.i_string_utf16BE_no_BOM" {
- any("\x00\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d");
-}
-
-test "json.test.i_string_utf16LE_no_BOM" {
- any("\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d\x00");
-}
-
-test "json.test.i_string_UTF-16LE_with_BOM" {
- any("\xc3\xbf\xc3\xbe\x5b\x00\x22\x00\xc3\xa9\x00\x22\x00\x5d\x00");
-}
-
-test "json.test.i_string_UTF-8_invalid_sequence" {
- any(
- \\["日шú"]
- );
-}
-
-test "json.test.i_string_UTF8_surrogate_U+D800" {
- any(
- \\["í €"]
- );
-}
-
-test "json.test.i_structure_500_nested_arrays" {
- any(("[" ** 500) ++ ("]" ** 500));
-}
-
-test "json.test.i_structure_UTF-8_BOM_empty_object" {
- any(
- \\{}
- );
-}
diff --git a/std/math/acos.zig b/std/math/acos.zig
index 763d9d8abd..de07da8fe0 100644
--- a/std/math/acos.zig
+++ b/std/math/acos.zig
@@ -1,11 +1,17 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - acos(x) = nan if x < -1 or x > 1
+// https://git.musl-libc.org/cgit/musl/tree/src/math/acosf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/acos.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the arc-cosine of x.
+///
+/// Special cases:
+/// - acos(x) = nan if x < -1 or x > 1
pub fn acos(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/acosh.zig b/std/math/acosh.zig
index a2a9926863..503c0433fc 100644
--- a/std/math/acosh.zig
+++ b/std/math/acosh.zig
@@ -1,13 +1,19 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - acosh(x) = snan if x < 1
-// - acosh(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/acoshf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/acosh.c
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the hyperbolic arc-cosine of x.
+///
+/// Special cases:
+/// - acosh(x) = snan if x < 1
+/// - acosh(nan) = nan
pub fn acosh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/asin.zig b/std/math/asin.zig
index b01a1ac49e..2db9f86ff1 100644
--- a/std/math/asin.zig
+++ b/std/math/asin.zig
@@ -1,12 +1,18 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - asin(+-0) = +-0
-// - asin(x) = nan if x < -1 or x > 1
+// https://git.musl-libc.org/cgit/musl/tree/src/math/asinf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/asin.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the arc-sin of x.
+///
+/// Special Cases:
+/// - asin(+-0) = +-0
+/// - asin(x) = nan if x < -1 or x > 1
pub fn asin(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/asinh.zig b/std/math/asinh.zig
index cef9bfb23f..0fb51d1b43 100644
--- a/std/math/asinh.zig
+++ b/std/math/asinh.zig
@@ -1,14 +1,20 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - asinh(+-0) = +-0
-// - asinh(+-inf) = +-inf
-// - asinh(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/asinhf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/asinh.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns the hyperbolic arc-sin of x.
+///
+/// Special Cases:
+/// - asinh(+-0) = +-0
+/// - asinh(+-inf) = +-inf
+/// - asinh(nan) = nan
pub fn asinh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/atan.zig b/std/math/atan.zig
index ba5a11dd10..cc4cad0dd4 100644
--- a/std/math/atan.zig
+++ b/std/math/atan.zig
@@ -1,12 +1,18 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - atan(+-0) = +-0
-// - atan(+-inf) = +-pi/2
+// https://git.musl-libc.org/cgit/musl/tree/src/math/atanf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/atan.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the arc-tangent of x.
+///
+/// Special Cases:
+/// - atan(+-0) = +-0
+/// - atan(+-inf) = +-pi/2
pub fn atan(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/atan2.zig b/std/math/atan2.zig
index 7f13507402..68e381607d 100644
--- a/std/math/atan2.zig
+++ b/std/math/atan2.zig
@@ -1,27 +1,33 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// atan2(y, nan) = nan
-// atan2(nan, x) = nan
-// atan2(+0, x>=0) = +0
-// atan2(-0, x>=0) = -0
-// atan2(+0, x<=-0) = +pi
-// atan2(-0, x<=-0) = -pi
-// atan2(y>0, 0) = +pi/2
-// atan2(y<0, 0) = -pi/2
-// atan2(+inf, +inf) = +pi/4
-// atan2(-inf, +inf) = -pi/4
-// atan2(+inf, -inf) = 3pi/4
-// atan2(-inf, -inf) = -3pi/4
-// atan2(y, +inf) = 0
-// atan2(y>0, -inf) = +pi
-// atan2(y<0, -inf) = -pi
-// atan2(+inf, x) = +pi/2
-// atan2(-inf, x) = -pi/2
+// https://git.musl-libc.org/cgit/musl/tree/src/math/atan2f.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/atan2.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the arc-tangent of y/x.
+///
+/// Special Cases:
+/// - atan2(y, nan) = nan
+/// - atan2(nan, x) = nan
+/// - atan2(+0, x>=0) = +0
+/// - atan2(-0, x>=0) = -0
+/// - atan2(+0, x<=-0) = +pi
+/// - atan2(-0, x<=-0) = -pi
+/// - atan2(y>0, 0) = +pi/2
+/// - atan2(y<0, 0) = -pi/2
+/// - atan2(+inf, +inf) = +pi/4
+/// - atan2(-inf, +inf) = -pi/4
+/// - atan2(+inf, -inf) = 3pi/4
+/// - atan2(-inf, -inf) = -3pi/4
+/// - atan2(y, +inf) = 0
+/// - atan2(y>0, -inf) = +pi
+/// - atan2(y<0, -inf) = -pi
+/// - atan2(+inf, x) = +pi/2
+/// - atan2(-inf, x) = -pi/2
pub fn atan2(comptime T: type, y: T, x: T) T {
return switch (T) {
f32 => atan2_32(y, x),
diff --git a/std/math/atanh.zig b/std/math/atanh.zig
index 9056064f5a..8ba29be761 100644
--- a/std/math/atanh.zig
+++ b/std/math/atanh.zig
@@ -1,14 +1,20 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - atanh(+-1) = +-inf with signal
-// - atanh(x) = nan if |x| > 1 with signal
-// - atanh(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/atanhf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/atanh.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns the hyperbolic arc-tangent of x.
+///
+/// Special Cases:
+/// - atanh(+-1) = +-inf with signal
+/// - atanh(x) = nan if |x| > 1 with signal
+/// - atanh(nan) = nan
pub fn atanh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/big.zig b/std/math/big.zig
index 94b6d864e7..44b5ce675f 100644
--- a/std/math/big.zig
+++ b/std/math/big.zig
@@ -1,5 +1,7 @@
pub use @import("big/int.zig");
+pub use @import("big/rational.zig");
test "math.big" {
_ = @import("big/int.zig");
+ _ = @import("big/rational.zig");
}
diff --git a/std/math/big/int.zig b/std/math/big/int.zig
index 8800c2c7a9..beac3c85fe 100644
--- a/std/math/big/int.zig
+++ b/std/math/big/int.zig
@@ -21,78 +21,160 @@ comptime {
debug.assert(Limb.is_signed == false);
}
+/// An arbitrary-precision big integer.
+///
+/// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an
+/// Int is bounded only by available memory.
pub const Int = struct {
- allocator: *Allocator,
- positive: bool,
- // - little-endian ordered
- // - len >= 1 always
- // - zero value -> len == 1 with limbs[0] == 0
+ const sign_bit: usize = 1 << (usize.bit_count - 1);
+
+ /// Default number of limbs to allocate on creation of an Int.
+ pub const default_capacity = 4;
+
+ /// Allocator used by the Int when requesting memory.
+ allocator: ?*Allocator,
+
+ /// Raw digits. These are:
+ ///
+ /// * Little-endian ordered
+ /// * limbs.len >= 1
+ /// * Zero is represent as Int.len() == 1 with limbs[0] == 0.
+ ///
+ /// Accessing limbs directly should be avoided.
limbs: []Limb,
- len: usize,
- const default_capacity = 4;
+ /// High bit is the sign bit. If set, Int is negative, else Int is positive.
+ /// The remaining bits represent the number of limbs used by Int.
+ metadata: usize,
+ /// Creates a new Int. default_capacity limbs will be allocated immediately.
+ /// Int will be zeroed.
pub fn init(allocator: *Allocator) !Int {
return try Int.initCapacity(allocator, default_capacity);
}
+ /// Creates a new Int. Int will be set to `value`.
+ ///
+ /// This is identical to an `init`, followed by a `set`.
pub fn initSet(allocator: *Allocator, value: var) !Int {
var s = try Int.init(allocator);
try s.set(value);
return s;
}
+ /// Creates a new Int with a specific capacity. If capacity < default_capacity then the
+ /// default capacity will be used instead.
pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int {
return Int{
.allocator = allocator,
- .positive = true,
+ .metadata = 1,
.limbs = block: {
var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity));
limbs[0] = 0;
break :block limbs;
},
- .len = 1,
};
}
+ /// Returns the number of limbs currently in use.
+ pub fn len(self: Int) usize {
+ return self.metadata & ~sign_bit;
+ }
+
+ /// Returns whether an Int is positive.
+ pub fn isPositive(self: Int) bool {
+ return self.metadata & sign_bit == 0;
+ }
+
+ /// Sets the sign of an Int.
+ pub fn setSign(self: *Int, positive: bool) void {
+ if (positive) {
+ self.metadata &= ~sign_bit;
+ } else {
+ self.metadata |= sign_bit;
+ }
+ }
+
+ /// Sets the length of an Int.
+ ///
+ /// If setLen is used, then the Int must be normalized to suit.
+ pub fn setLen(self: *Int, new_len: usize) void {
+ self.metadata &= sign_bit;
+ self.metadata |= new_len;
+ }
+
+ /// Returns an Int backed by a fixed set of limb values.
+ /// This is read-only and cannot be used as a result argument. If the Int tries to allocate
+ /// memory a runtime panic will occur.
+ pub fn initFixed(limbs: []const Limb) Int {
+ var self = Int{
+ .allocator = null,
+ .metadata = limbs.len,
+ // Cast away the const, invalid use to pass as a pointer argument.
+ .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len],
+ };
+
+ self.normalize(limbs.len);
+ return self;
+ }
+
+ /// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have
+ /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested
+ /// capacity is only greater than the current capacity by one limb.
pub fn ensureCapacity(self: *Int, capacity: usize) !void {
+ self.assertWritable();
if (capacity <= self.limbs.len) {
return;
}
- self.limbs = try self.allocator.realloc(self.limbs, capacity);
+ self.limbs = try self.allocator.?.realloc(self.limbs, capacity);
}
+ fn assertWritable(self: Int) void {
+ if (self.allocator == null) {
+ @panic("provided Int value is read-only but must be writable");
+ }
+ }
+
+ /// Frees all memory associated with an Int.
pub fn deinit(self: *Int) void {
- self.allocator.free(self.limbs);
+ self.assertWritable();
+ self.allocator.?.free(self.limbs);
self.* = undefined;
}
+ /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and
+ /// can be modified separately from the original.
pub fn clone(other: Int) !Int {
+ other.assertWritable();
return Int{
.allocator = other.allocator,
- .positive = other.positive,
+ .metadata = other.metadata,
.limbs = block: {
- var limbs = try other.allocator.alloc(Limb, other.len);
- mem.copy(Limb, limbs[0..], other.limbs[0..other.len]);
+ var limbs = try other.allocator.?.alloc(Limb, other.len());
+ mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]);
break :block limbs;
},
- .len = other.len,
};
}
+ /// Copies the value of an Int to an existing Int so that they both have the same value.
+ /// Extra memory will be allocated if the receiver does not have enough capacity.
pub fn copy(self: *Int, other: Int) !void {
- if (self == &other) {
+ self.assertWritable();
+ if (self.limbs.ptr == other.limbs.ptr) {
return;
}
- self.positive = other.positive;
- try self.ensureCapacity(other.len);
- mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len]);
- self.len = other.len;
+ try self.ensureCapacity(other.len());
+ mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len()]);
+ self.metadata = other.metadata;
}
+ /// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not
+ /// performed. The address of the limbs field will not be the same after this function.
pub fn swap(self: *Int, other: *Int) void {
+ self.assertWritable();
mem.swap(Int, self, other);
}
@@ -103,45 +185,49 @@ pub const Int = struct {
debug.warn("\n");
}
- pub fn negate(r: *Int) void {
- r.positive = !r.positive;
+ /// Negate the sign of an Int.
+ pub fn negate(self: *Int) void {
+ self.metadata ^= sign_bit;
}
- pub fn abs(r: *Int) void {
- r.positive = true;
+ /// Make an Int positive.
+ pub fn abs(self: *Int) void {
+ self.metadata &= ~sign_bit;
}
- pub fn isOdd(r: Int) bool {
- return r.limbs[0] & 1 != 0;
+ /// Returns true if an Int is odd.
+ pub fn isOdd(self: Int) bool {
+ return self.limbs[0] & 1 != 0;
}
- pub fn isEven(r: Int) bool {
- return !r.isOdd();
+ /// Returns true if an Int is even.
+ pub fn isEven(self: Int) bool {
+ return !self.isOdd();
}
- // Returns the number of bits required to represent the absolute value of self.
+ /// Returns the number of bits required to represent the absolute value an Int.
fn bitCountAbs(self: Int) usize {
- return (self.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len - 1]));
+ return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(self.limbs[self.len() - 1]));
}
- // Returns the number of bits required to represent the integer in twos-complement form.
- //
- // If the integer is negative the value returned is the number of bits needed by a signed
- // integer to represent the value. If positive the value is the number of bits for an
- // unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
- // one greater than the returned value.
- //
- // e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
+ /// Returns the number of bits required to represent the integer in twos-complement form.
+ ///
+ /// If the integer is negative the value returned is the number of bits needed by a signed
+ /// integer to represent the value. If positive the value is the number of bits for an
+ /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount
+ /// one greater than the returned value.
+ ///
+ /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7.
fn bitCountTwosComp(self: Int) usize {
var bits = self.bitCountAbs();
// If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos
// complement requires one less bit.
- if (!self.positive) block: {
+ if (!self.isPositive()) block: {
bits += 1;
- if (@popCount(self.limbs[self.len - 1]) == 1) {
- for (self.limbs[0 .. self.len - 1]) |limb| {
+ if (@popCount(self.limbs[self.len() - 1]) == 1) {
+ for (self.limbs[0 .. self.len() - 1]) |limb| {
if (@popCount(limb) != 0) {
break :block;
}
@@ -154,31 +240,34 @@ pub const Int = struct {
return bits;
}
- pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
+ fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool {
if (self.eqZero()) {
return true;
}
- if (!is_signed and !self.positive) {
+ if (!is_signed and !self.isPositive()) {
return false;
}
- const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed);
+ const req_bits = self.bitCountTwosComp() + @boolToInt(self.isPositive() and is_signed);
return bit_count >= req_bits;
}
+ /// Returns whether self can fit into an integer of the requested type.
pub fn fits(self: Int, comptime T: type) bool {
return self.fitsInTwosComp(T.is_signed, T.bit_count);
}
- // Returns the approximate size of the integer in the given base. Negative values accommodate for
- // the minus sign. This is used for determining the number of characters needed to print the
- // value. It is inexact and will exceed the given value by 1-2 digits.
+ /// Returns the approximate size of the integer in the given base. Negative values accommodate for
+ /// the minus sign. This is used for determining the number of characters needed to print the
+ /// value. It is inexact and may exceed the given value by ~1-2 bytes.
pub fn sizeInBase(self: Int, base: usize) usize {
- const bit_count = usize(@boolToInt(!self.positive)) + self.bitCountAbs();
+ const bit_count = usize(@boolToInt(!self.isPositive())) + self.bitCountAbs();
return (bit_count / math.log2(base)) + 1;
}
+ /// Sets an Int to value. Value must be an primitive integer type.
pub fn set(self: *Int, value: var) Allocator.Error!void {
+ self.assertWritable();
const T = @typeOf(value);
switch (@typeInfo(T)) {
@@ -186,19 +275,19 @@ pub const Int = struct {
const UT = if (T.is_signed) @IntType(false, T.bit_count - 1) else T;
try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb));
- self.positive = value >= 0;
- self.len = 0;
+ self.metadata = 0;
+ self.setSign(value >= 0);
var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value);
if (info.bits <= Limb.bit_count) {
self.limbs[0] = Limb(w_value);
- self.len = 1;
+ self.metadata += 1;
} else {
var i: usize = 0;
while (w_value != 0) : (i += 1) {
self.limbs[i] = @truncate(Limb, w_value);
- self.len += 1;
+ self.metadata += 1;
// TODO: shift == 64 at compile-time fails. Fails on u128 limbs.
w_value >>= Limb.bit_count / 2;
@@ -212,8 +301,8 @@ pub const Int = struct {
const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1;
try self.ensureCapacity(req_limbs);
- self.positive = value >= 0;
- self.len = req_limbs;
+ self.metadata = req_limbs;
+ self.setSign(value >= 0);
if (w_value <= maxInt(Limb)) {
self.limbs[0] = w_value;
@@ -240,6 +329,9 @@ pub const Int = struct {
TargetTooSmall,
};
+ /// Convert self to type T.
+ ///
+ /// Returns an error if self cannot be narrowed into the requested type without truncation.
pub fn to(self: Int, comptime T: type) ConvertError!T {
switch (@typeId(T)) {
TypeId.Int => {
@@ -254,17 +346,17 @@ pub const Int = struct {
if (@sizeOf(UT) <= @sizeOf(Limb)) {
r = @intCast(UT, self.limbs[0]);
} else {
- for (self.limbs[0..self.len]) |_, ri| {
- const limb = self.limbs[self.len - ri - 1];
+ for (self.limbs[0..self.len()]) |_, ri| {
+ const limb = self.limbs[self.len() - ri - 1];
r <<= Limb.bit_count;
r |= limb;
}
}
if (!T.is_signed) {
- return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned;
+ return if (self.isPositive()) @intCast(T, r) else error.NegativeIntoUnsigned;
} else {
- if (self.positive) {
+ if (self.isPositive()) {
return @intCast(T, r);
} else {
if (math.cast(T, r)) |ok| {
@@ -303,7 +395,15 @@ pub const Int = struct {
};
}
+ /// Set self from the string representation `value`.
+ ///
+ /// value must contain only digits <= `base`. Base prefixes are not allowed (e.g. 0x43 should
+ /// simply be 43).
+ ///
+ /// Returns an error if memory could not be allocated or `value` has invalid digits for the
+ /// requested base.
pub fn setString(self: *Int, base: u8, value: []const u8) !void {
+ self.assertWritable();
if (base < 2 or base > 16) {
return error.InvalidBase;
}
@@ -315,27 +415,22 @@ pub const Int = struct {
i += 1;
}
- // TODO values less than limb size should guarantee non allocating
- var base_buffer: [512]u8 = undefined;
- const base_al = &std.heap.FixedBufferAllocator.init(base_buffer[0..]).allocator;
- const base_ap = try Int.initSet(base_al, base);
-
- var d_buffer: [512]u8 = undefined;
- var d_fba = std.heap.FixedBufferAllocator.init(d_buffer[0..]);
- const d_al = &d_fba.allocator;
-
+ const ap_base = Int.initFixed(([]Limb{base})[0..]);
try self.set(0);
+
for (value[i..]) |ch| {
const d = try charToDigit(ch, base);
- d_fba.end_index = 0;
- const d_ap = try Int.initSet(d_al, d);
- try self.mul(self.*, base_ap);
- try self.add(self.*, d_ap);
+ const ap_d = Int.initFixed(([]Limb{d})[0..]);
+
+ try self.mul(self.*, ap_base);
+ try self.add(self.*, ap_d);
}
- self.positive = positive;
+ self.setSign(positive);
}
+ /// Converts self to a string in the requested base. Memory is allocated from the provided
+ /// allocator and not the one present in self.
/// TODO make this call format instead of the other way around
pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
if (base < 2 or base > 16) {
@@ -355,7 +450,7 @@ pub const Int = struct {
if (base & (base - 1) == 0) {
const base_shift = math.log2_int(Limb, base);
- for (self.limbs[0..self.len]) |limb| {
+ for (self.limbs[0..self.len()]) |limb| {
var shift: usize = 0;
while (shift < Limb.bit_count) : (shift += base_shift) {
const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & Limb(base - 1));
@@ -382,11 +477,11 @@ pub const Int = struct {
}
var q = try self.clone();
- q.positive = true;
+ q.abs();
var r = try Int.init(allocator);
var b = try Int.initSet(allocator, limb_base);
- while (q.len >= 2) {
+ while (q.len() >= 2) {
try Int.divTrunc(&q, &r, q, b);
var r_word = r.limbs[0];
@@ -399,7 +494,7 @@ pub const Int = struct {
}
{
- debug.assert(q.len == 1);
+ debug.assert(q.len() == 1);
var r_word = q.limbs[0];
while (r_word != 0) {
@@ -410,7 +505,7 @@ pub const Int = struct {
}
}
- if (!self.positive) {
+ if (!self.isPositive()) {
try digits.append('-');
}
@@ -419,7 +514,7 @@ pub const Int = struct {
return s;
}
- /// for the std lib format function
+ /// To allow `std.fmt.printf` to work with Int.
/// TODO make this non-allocating
pub fn format(
self: Int,
@@ -428,22 +523,24 @@ pub const Int = struct {
comptime FmtError: type,
output: fn (@typeOf(context), []const u8) FmtError!void,
) FmtError!void {
+ self.assertWritable();
// TODO look at fmt and support other bases
- const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
- defer self.allocator.free(str);
+ // TODO support read-only fixed integers
+ const str = self.toString(self.allocator.?, 10) catch @panic("TODO make this non allocating");
+ defer self.allocator.?.free(str);
return output(context, str);
}
- // returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
+ /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
pub fn cmpAbs(a: Int, b: Int) i8 {
- if (a.len < b.len) {
+ if (a.len() < b.len()) {
return -1;
}
- if (a.len > b.len) {
+ if (a.len() > b.len()) {
return 1;
}
- var i: usize = a.len - 1;
+ var i: usize = a.len() - 1;
while (i != 0) : (i -= 1) {
if (a.limbs[i] != b.limbs[i]) {
break;
@@ -459,53 +556,37 @@ pub const Int = struct {
}
}
- // returns -1, 0, 1 if a < b, a == b or a > b respectively.
+ /// Returns -1, 0, 1 if a < b, a == b or a > b respectively.
pub fn cmp(a: Int, b: Int) i8 {
- if (a.positive != b.positive) {
- return if (a.positive) i8(1) else -1;
+ if (a.isPositive() != b.isPositive()) {
+ return if (a.isPositive()) i8(1) else -1;
} else {
const r = cmpAbs(a, b);
- return if (a.positive) r else -r;
+ return if (a.isPositive()) r else -r;
}
}
- // if a == 0
+ /// Returns true if a == 0.
pub fn eqZero(a: Int) bool {
- return a.len == 1 and a.limbs[0] == 0;
+ return a.len() == 1 and a.limbs[0] == 0;
}
- // if |a| == |b|
+ /// Returns true if |a| == |b|.
pub fn eqAbs(a: Int, b: Int) bool {
return cmpAbs(a, b) == 0;
}
- // if a == b
+ /// Returns true if a == b.
pub fn eq(a: Int, b: Int) bool {
return cmp(a, b) == 0;
}
- // Normalize for a possible single carry digit.
- //
- // [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
- // [1, 2, 3, 4, 5] -> [1, 2, 3, 4, 5]
- // [0] -> [0]
- fn norm1(r: *Int, length: usize) void {
- debug.assert(length > 0);
- debug.assert(length <= r.limbs.len);
-
- if (r.limbs[length - 1] == 0) {
- r.len = if (length > 1) length - 1 else 1;
- } else {
- r.len = length;
- }
- }
-
// Normalize a possible sequence of leading zeros.
//
// [1, 2, 3, 4, 0] -> [1, 2, 3, 4]
// [1, 2, 0, 0, 0] -> [1, 2]
// [0, 0, 0, 0, 0] -> [0]
- fn normN(r: *Int, length: usize) void {
+ fn normalize(r: *Int, length: usize) void {
debug.assert(length > 0);
debug.assert(length <= r.limbs.len);
@@ -517,11 +598,25 @@ pub const Int = struct {
}
// Handle zero
- r.len = if (j != 0) j else 1;
+ r.setLen(if (j != 0) j else 1);
}
- // r = a + b
+ // Cannot be used as a result argument to any function.
+ fn readOnlyPositive(a: Int) Int {
+ return Int{
+ .allocator = null,
+ .metadata = a.len(),
+ .limbs = a.limbs,
+ };
+ }
+
+ /// r = a + b
+ ///
+ /// r, a and b may be aliases.
+ ///
+ /// Returns an error if memory could not be allocated.
pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void {
+ r.assertWritable();
if (a.eqZero()) {
try r.copy(b);
return;
@@ -530,38 +625,26 @@ pub const Int = struct {
return;
}
- if (a.positive != b.positive) {
- if (a.positive) {
+ if (a.isPositive() != b.isPositive()) {
+ if (a.isPositive()) {
// (a) + (-b) => a - b
- const bp = Int{
- .allocator = undefined,
- .positive = true,
- .limbs = b.limbs,
- .len = b.len,
- };
- try r.sub(a, bp);
+ try r.sub(a, readOnlyPositive(b));
} else {
// (-a) + (b) => b - a
- const ap = Int{
- .allocator = undefined,
- .positive = true,
- .limbs = a.limbs,
- .len = a.len,
- };
- try r.sub(b, ap);
+ try r.sub(b, readOnlyPositive(a));
}
} else {
- if (a.len >= b.len) {
- try r.ensureCapacity(a.len + 1);
- lladd(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
- r.norm1(a.len + 1);
+ if (a.len() >= b.len()) {
+ try r.ensureCapacity(a.len() + 1);
+ lladd(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
+ r.normalize(a.len() + 1);
} else {
- try r.ensureCapacity(b.len + 1);
- lladd(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
- r.norm1(b.len + 1);
+ try r.ensureCapacity(b.len() + 1);
+ lladd(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
+ r.normalize(b.len() + 1);
}
- r.positive = a.positive;
+ r.setSign(a.isPositive());
}
}
@@ -589,55 +672,48 @@ pub const Int = struct {
r[i] = carry;
}
- // r = a - b
+ /// r = a - b
+ ///
+ /// r, a and b may be aliases.
+ ///
+ /// Returns an error if memory could not be allocated.
pub fn sub(r: *Int, a: Int, b: Int) !void {
- if (a.positive != b.positive) {
- if (a.positive) {
+ r.assertWritable();
+ if (a.isPositive() != b.isPositive()) {
+ if (a.isPositive()) {
// (a) - (-b) => a + b
- const bp = Int{
- .allocator = undefined,
- .positive = true,
- .limbs = b.limbs,
- .len = b.len,
- };
- try r.add(a, bp);
+ try r.add(a, readOnlyPositive(b));
} else {
// (-a) - (b) => -(a + b)
- const ap = Int{
- .allocator = undefined,
- .positive = true,
- .limbs = a.limbs,
- .len = a.len,
- };
- try r.add(ap, b);
- r.positive = false;
+ try r.add(readOnlyPositive(a), b);
+ r.setSign(false);
}
} else {
- if (a.positive) {
+ if (a.isPositive()) {
// (a) - (b) => a - b
if (a.cmp(b) >= 0) {
- try r.ensureCapacity(a.len + 1);
- llsub(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
- r.normN(a.len);
- r.positive = true;
+ try r.ensureCapacity(a.len() + 1);
+ llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
+ r.normalize(a.len());
+ r.setSign(true);
} else {
- try r.ensureCapacity(b.len + 1);
- llsub(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
- r.normN(b.len);
- r.positive = false;
+ try r.ensureCapacity(b.len() + 1);
+ llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
+ r.normalize(b.len());
+ r.setSign(false);
}
} else {
// (-a) - (-b) => -(a - b)
if (a.cmp(b) < 0) {
- try r.ensureCapacity(a.len + 1);
- llsub(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
- r.normN(a.len);
- r.positive = false;
+ try r.ensureCapacity(a.len() + 1);
+ llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
+ r.normalize(a.len());
+ r.setSign(false);
} else {
- try r.ensureCapacity(b.len + 1);
- llsub(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
- r.normN(b.len);
- r.positive = true;
+ try r.ensureCapacity(b.len() + 1);
+ llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
+ r.normalize(b.len());
+ r.setSign(true);
}
}
}
@@ -667,16 +743,20 @@ pub const Int = struct {
debug.assert(borrow == 0);
}
- // rma = a * b
- //
- // For greatest efficiency, ensure rma does not alias a or b.
+ /// rma = a * b
+ ///
+ /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
+ ///
+ /// Returns an error if memory could not be allocated.
pub fn mul(rma: *Int, a: Int, b: Int) !void {
+ rma.assertWritable();
+
var r = rma;
var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr;
var sr: Int = undefined;
if (aliased) {
- sr = try Int.initCapacity(rma.allocator, a.len + b.len);
+ sr = try Int.initCapacity(rma.allocator.?, a.len() + b.len());
r = &sr;
aliased = true;
}
@@ -685,16 +765,16 @@ pub const Int = struct {
r.deinit();
};
- try r.ensureCapacity(a.len + b.len);
+ try r.ensureCapacity(a.len() + b.len());
- if (a.len >= b.len) {
- llmul(r.limbs, a.limbs[0..a.len], b.limbs[0..b.len]);
+ if (a.len() >= b.len()) {
+ llmul(r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]);
} else {
- llmul(r.limbs, b.limbs[0..b.len], a.limbs[0..a.len]);
+ llmul(r.limbs, b.limbs[0..b.len()], a.limbs[0..a.len()]);
}
- r.positive = a.positive == b.positive;
- r.normN(a.len + b.len);
+ r.normalize(a.len() + b.len());
+ r.setSign(a.isPositive() == b.isPositive());
}
// a + b * c + *carry, sets carry to the overflow bits
@@ -740,29 +820,34 @@ pub const Int = struct {
}
}
+ /// q = a / b (rem r)
+ ///
+ /// a / b are floored (rounded towards 0).
pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void {
try div(q, r, a, b);
// Trunc -> Floor.
- if (!q.positive) {
- // TODO values less than limb size should guarantee non allocating
- var one_buffer: [512]u8 = undefined;
- const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator;
- const one_ap = try Int.initSet(one_al, 1);
-
- try q.sub(q.*, one_ap);
- try r.add(q.*, one_ap);
+ if (!q.isPositive()) {
+ const one = Int.initFixed(([]Limb{1})[0..]);
+ try q.sub(q.*, one);
+ try r.add(q.*, one);
}
- r.positive = b.positive;
+ r.setSign(b.isPositive());
}
+ /// q = a / b (rem r)
+ ///
+ /// a / b are truncated (rounded towards -inf).
pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void {
try div(q, r, a, b);
- r.positive = a.positive;
+ r.setSign(a.isPositive());
}
// Truncates by default.
fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void {
+ quo.assertWritable();
+ rem.assertWritable();
+
if (b.eqZero()) {
@panic("division by zero");
}
@@ -773,36 +858,67 @@ pub const Int = struct {
if (a.cmpAbs(b) < 0) {
// quo may alias a so handle rem first
try rem.copy(a);
- rem.positive = a.positive == b.positive;
+ rem.setSign(a.isPositive() == b.isPositive());
- quo.positive = true;
- quo.len = 1;
+ quo.metadata = 1;
quo.limbs[0] = 0;
return;
}
- if (b.len == 1) {
- try quo.ensureCapacity(a.len);
+ // Handle trailing zero-words of divisor/dividend. These are not handled in the following
+ // algorithms.
+ const a_zero_limb_count = blk: {
+ var i: usize = 0;
+ while (i < a.len()) : (i += 1) {
+ if (a.limbs[i] != 0) break;
+ }
+ break :blk i;
+ };
+ const b_zero_limb_count = blk: {
+ var i: usize = 0;
+ while (i < b.len()) : (i += 1) {
+ if (b.limbs[i] != 0) break;
+ }
+ break :blk i;
+ };
- lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[0..a.len], b.limbs[0]);
- quo.norm1(a.len);
- quo.positive = a.positive == b.positive;
+ const ab_zero_limb_count = std.math.min(a_zero_limb_count, b_zero_limb_count);
- rem.len = 1;
- rem.positive = true;
+ if (b.len() - ab_zero_limb_count == 1) {
+ try quo.ensureCapacity(a.len());
+
+ lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len()], b.limbs[b.len() - 1]);
+ quo.normalize(a.len() - ab_zero_limb_count);
+ quo.setSign(a.isPositive() == b.isPositive());
+
+ rem.metadata = 1;
} else {
// x and y are modified during division
- var x = try a.clone();
+ var x = try Int.initCapacity(quo.allocator.?, a.len());
defer x.deinit();
+ try x.copy(a);
- var y = try b.clone();
+ var y = try Int.initCapacity(quo.allocator.?, b.len());
defer y.deinit();
+ try y.copy(b);
// x may grow one limb during normalization
- try quo.ensureCapacity(a.len + y.len);
- try divN(quo.allocator, quo, rem, &x, &y);
+ try quo.ensureCapacity(a.len() + y.len());
+
+ // Shrink x, y such that the trailing zero limbs shared between are removed.
+ if (ab_zero_limb_count != 0) {
+ std.mem.copy(Limb, x.limbs[0..], x.limbs[ab_zero_limb_count..]);
+ std.mem.copy(Limb, y.limbs[0..], y.limbs[ab_zero_limb_count..]);
+ x.metadata -= ab_zero_limb_count;
+ y.metadata -= ab_zero_limb_count;
+ }
- quo.positive = a.positive == b.positive;
+ try divN(quo.allocator.?, quo, rem, &x, &y);
+ quo.setSign(a.isPositive() == b.isPositive());
+ }
+
+ if (ab_zero_limb_count != 0) {
+ try rem.shiftLeft(rem.*, ab_zero_limb_count * Limb.bit_count);
}
}
@@ -837,25 +953,28 @@ pub const Int = struct {
//
// x = qy + r where 0 <= r < y
fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void {
- debug.assert(y.len >= 2);
- debug.assert(x.len >= y.len);
- debug.assert(q.limbs.len >= x.len + y.len - 1);
+ debug.assert(y.len() >= 2);
+ debug.assert(x.len() >= y.len());
+ debug.assert(q.limbs.len >= x.len() + y.len() - 1);
debug.assert(default_capacity >= 3); // see 3.2
var tmp = try Int.init(allocator);
defer tmp.deinit();
- // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set)
- const norm_shift = @clz(y.limbs[y.len - 1]);
+ // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even
+ var norm_shift = @clz(y.limbs[y.len() - 1]);
+ if (norm_shift == 0 and y.isOdd()) {
+ norm_shift = Limb.bit_count;
+ }
try x.shiftLeft(x.*, norm_shift);
try y.shiftLeft(y.*, norm_shift);
- const n = x.len - 1;
- const t = y.len - 1;
+ const n = x.len() - 1;
+ const t = y.len() - 1;
// 1.
- q.len = n - t + 1;
- mem.set(Limb, q.limbs[0..q.len], 0);
+ q.metadata = n - t + 1;
+ mem.set(Limb, q.limbs[0..q.len()], 0);
// 2.
try tmp.shiftLeft(y.*, Limb.bit_count * (n - t));
@@ -880,7 +999,7 @@ pub const Int = struct {
tmp.limbs[0] = if (i >= 2) x.limbs[i - 2] else 0;
tmp.limbs[1] = if (i >= 1) x.limbs[i - 1] else 0;
tmp.limbs[2] = x.limbs[i];
- tmp.normN(3);
+ tmp.normalize(3);
while (true) {
// 2x1 limb multiplication unrolled against single-limb q[i-t-1]
@@ -888,7 +1007,7 @@ pub const Int = struct {
r.limbs[0] = addMulLimbWithCarry(0, if (t >= 1) y.limbs[t - 1] else 0, q.limbs[i - t - 1], &carry);
r.limbs[1] = addMulLimbWithCarry(0, y.limbs[t], q.limbs[i - t - 1], &carry);
r.limbs[2] = carry;
- r.normN(3);
+ r.normalize(3);
if (r.cmpAbs(tmp) <= 0) {
break;
@@ -903,7 +1022,7 @@ pub const Int = struct {
try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1));
try x.sub(x.*, tmp);
- if (!x.positive) {
+ if (!x.isPositive()) {
try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1));
try x.add(x.*, tmp);
q.limbs[i - t - 1] -= 1;
@@ -911,18 +1030,20 @@ pub const Int = struct {
}
// Denormalize
- q.normN(q.len);
+ q.normalize(q.len());
try r.shiftRight(x.*, norm_shift);
- r.normN(r.len);
+ r.normalize(r.len());
}
- // r = a << shift, in other words, r = a * 2^shift
+ /// r = a << shift, in other words, r = a * 2^shift
pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void {
- try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1);
- llshl(r.limbs[0..], a.limbs[0..a.len], shift);
- r.norm1(a.len + (shift / Limb.bit_count) + 1);
- r.positive = a.positive;
+ r.assertWritable();
+
+ try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1);
+ llshl(r.limbs[0..], a.limbs[0..a.len()], shift);
+ r.normalize(a.len() + (shift / Limb.bit_count) + 1);
+ r.setSign(a.isPositive());
}
fn llshl(r: []Limb, a: []const Limb, shift: usize) void {
@@ -948,19 +1069,20 @@ pub const Int = struct {
mem.set(Limb, r[0 .. limb_shift - 1], 0);
}
- // r = a >> shift
+ /// r = a >> shift
pub fn shiftRight(r: *Int, a: Int, shift: usize) !void {
- if (a.len <= shift / Limb.bit_count) {
- r.len = 1;
+ r.assertWritable();
+
+ if (a.len() <= shift / Limb.bit_count) {
+ r.metadata = 1;
r.limbs[0] = 0;
- r.positive = true;
return;
}
- try r.ensureCapacity(a.len - (shift / Limb.bit_count));
- const r_len = llshr(r.limbs[0..], a.limbs[0..a.len], shift);
- r.len = a.len - (shift / Limb.bit_count);
- r.positive = a.positive;
+ try r.ensureCapacity(a.len() - (shift / Limb.bit_count));
+ const r_len = llshr(r.limbs[0..], a.limbs[0..a.len()], shift);
+ r.metadata = a.len() - (shift / Limb.bit_count);
+ r.setSign(a.isPositive());
}
fn llshr(r: []Limb, a: []const Limb, shift: usize) void {
@@ -983,16 +1105,20 @@ pub const Int = struct {
}
}
- // r = a | b
+ /// r = a | b
+ ///
+ /// a and b are zero-extended to the longer of a or b.
pub fn bitOr(r: *Int, a: Int, b: Int) !void {
- if (a.len > b.len) {
- try r.ensureCapacity(a.len);
- llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
- r.len = a.len;
+ r.assertWritable();
+
+ if (a.len() > b.len()) {
+ try r.ensureCapacity(a.len());
+ llor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
+ r.setLen(a.len());
} else {
- try r.ensureCapacity(b.len);
- llor(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
- r.len = b.len;
+ try r.ensureCapacity(b.len());
+ llor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
+ r.setLen(b.len());
}
}
@@ -1010,16 +1136,18 @@ pub const Int = struct {
}
}
- // r = a & b
+ /// r = a & b
pub fn bitAnd(r: *Int, a: Int, b: Int) !void {
- if (a.len > b.len) {
- try r.ensureCapacity(b.len);
- lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
- r.normN(b.len);
+ r.assertWritable();
+
+ if (a.len() > b.len()) {
+ try r.ensureCapacity(b.len());
+ lland(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
+ r.normalize(b.len());
} else {
- try r.ensureCapacity(a.len);
- lland(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
- r.normN(a.len);
+ try r.ensureCapacity(a.len());
+ lland(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
+ r.normalize(a.len());
}
}
@@ -1034,16 +1162,18 @@ pub const Int = struct {
}
}
- // r = a ^ b
+ /// r = a ^ b
pub fn bitXor(r: *Int, a: Int, b: Int) !void {
- if (a.len > b.len) {
- try r.ensureCapacity(a.len);
- llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]);
- r.normN(a.len);
+ r.assertWritable();
+
+ if (a.len() > b.len()) {
+ try r.ensureCapacity(a.len());
+ llxor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]);
+ r.normalize(a.len());
} else {
- try r.ensureCapacity(b.len);
- llxor(r.limbs[0..], b.limbs[0..b.len], a.limbs[0..a.len]);
- r.normN(b.len);
+ try r.ensureCapacity(b.len());
+ llxor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]);
+ r.normalize(b.len());
}
}
@@ -1067,7 +1197,9 @@ pub const Int = struct {
// They will still run on larger than this and should pass, but the multi-limb code-paths
// may be untested in some cases.
-const al = debug.global_allocator;
+var buffer: [64 * 8192]u8 = undefined;
+var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
+const al = &fixed.allocator;
test "big.int comptime_int set" {
comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab;
@@ -1088,14 +1220,14 @@ test "big.int comptime_int set negative" {
var a = try Int.initSet(al, -10);
testing.expect(a.limbs[0] == 10);
- testing.expect(a.positive == false);
+ testing.expect(a.isPositive() == false);
}
test "big.int int set unaligned small" {
var a = try Int.initSet(al, u7(45));
testing.expect(a.limbs[0] == 45);
- testing.expect(a.positive == true);
+ testing.expect(a.isPositive() == true);
}
test "big.int comptime_int to" {
@@ -1116,7 +1248,7 @@ test "big.int to target too small error" {
testing.expectError(error.TargetTooSmall, a.to(u8));
}
-test "big.int norm1" {
+test "big.int normalize" {
var a = try Int.init(al);
try a.ensureCapacity(8);
@@ -1124,26 +1256,26 @@ test "big.int norm1" {
a.limbs[1] = 2;
a.limbs[2] = 3;
a.limbs[3] = 0;
- a.norm1(4);
- testing.expect(a.len == 3);
+ a.normalize(4);
+ testing.expect(a.len() == 3);
a.limbs[0] = 1;
a.limbs[1] = 2;
a.limbs[2] = 3;
- a.norm1(3);
- testing.expect(a.len == 3);
+ a.normalize(3);
+ testing.expect(a.len() == 3);
a.limbs[0] = 0;
a.limbs[1] = 0;
- a.norm1(2);
- testing.expect(a.len == 1);
+ a.normalize(2);
+ testing.expect(a.len() == 1);
a.limbs[0] = 0;
- a.norm1(1);
- testing.expect(a.len == 1);
+ a.normalize(1);
+ testing.expect(a.len() == 1);
}
-test "big.int normN" {
+test "big.int normalize multi" {
var a = try Int.init(al);
try a.ensureCapacity(8);
@@ -1151,25 +1283,25 @@ test "big.int normN" {
a.limbs[1] = 2;
a.limbs[2] = 0;
a.limbs[3] = 0;
- a.normN(4);
- testing.expect(a.len == 2);
+ a.normalize(4);
+ testing.expect(a.len() == 2);
a.limbs[0] = 1;
a.limbs[1] = 2;
a.limbs[2] = 3;
- a.normN(3);
- testing.expect(a.len == 3);
+ a.normalize(3);
+ testing.expect(a.len() == 3);
a.limbs[0] = 0;
a.limbs[1] = 0;
a.limbs[2] = 0;
a.limbs[3] = 0;
- a.normN(4);
- testing.expect(a.len == 1);
+ a.normalize(4);
+ testing.expect(a.len() == 1);
a.limbs[0] = 0;
- a.normN(1);
- testing.expect(a.len == 1);
+ a.normalize(1);
+ testing.expect(a.len() == 1);
}
test "big.int parity" {
@@ -1204,7 +1336,7 @@ test "big.int bitcount + sizeInBase" {
try a.shiftLeft(a, 5000);
testing.expect(a.bitCountAbs() == 5032);
testing.expect(a.sizeInBase(2) >= 5032);
- a.positive = false;
+ a.setSign(false);
testing.expect(a.bitCountAbs() == 5032);
testing.expect(a.sizeInBase(2) >= 5033);
@@ -1216,10 +1348,8 @@ test "big.int bitcount/to" {
try a.set(0);
testing.expect(a.bitCountTwosComp() == 0);
- // TODO: stack smashing
- // testing.expect((try a.to(u0)) == 0);
- // TODO: sigsegv
- // testing.expect((try a.to(i0)) == 0);
+ testing.expect((try a.to(u0)) == 0);
+ testing.expect((try a.to(i0)) == 0);
try a.set(-1);
testing.expect(a.bitCountTwosComp() == 1);
@@ -1980,6 +2110,98 @@ test "big.int div multi-multi (3.1/3.3 branch)" {
testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
}
+test "big.int div multi-single zero-limb trailing" {
+ var a = try Int.initSet(al, 0x60000000000000000000000000000000000000000000000000000000000000000);
+ var b = try Int.initSet(al, 0x10000000000000000);
+
+ var q = try Int.init(al);
+ var r = try Int.init(al);
+ try Int.divTrunc(&q, &r, a, b);
+
+ var expected = try Int.initSet(al, 0x6000000000000000000000000000000000000000000000000);
+ testing.expect(q.eq(expected));
+ testing.expect(r.eqZero());
+}
+
+test "big.int div multi-multi zero-limb trailing (with rem)" {
+ var a = try Int.initSet(al, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
+ var b = try Int.initSet(al, 0x8666666655555555444444443333333300000000000000000000000000000000);
+
+ var q = try Int.init(al);
+ var r = try Int.init(al);
+ try Int.divTrunc(&q, &r, a, b);
+
+ testing.expect((try q.to(u128)) == 0x10000000000000000);
+
+ const rs = try r.toString(al, 16);
+ testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000"));
+}
+
+test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" {
+ var a = try Int.initSet(al, 0x8666666655555555888888877777777611111111111111110000000000000000);
+ var b = try Int.initSet(al, 0x8666666655555555444444443333333300000000000000000000000000000000);
+
+ var q = try Int.init(al);
+ var r = try Int.init(al);
+ try Int.divTrunc(&q, &r, a, b);
+
+ testing.expect((try q.to(u128)) == 0x1);
+
+ const rs = try r.toString(al, 16);
+ testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000"));
+}
+
+test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" {
+ var a = try Int.initSet(al, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000);
+ var b = try Int.initSet(al, 0x866666665555555544444444333333330000000000000000);
+
+ var q = try Int.init(al);
+ var r = try Int.init(al);
+ try Int.divTrunc(&q, &r, a, b);
+
+ const qs = try q.toString(al, 16);
+ testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f"));
+
+ const rs = try r.toString(al, 16);
+ testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000"));
+}
+
+test "big.int div multi-multi fuzz case #1" {
+ var a = try Int.init(al);
+ var b = try Int.init(al);
+
+ try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
+ try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff");
+
+ var q = try Int.init(al);
+ var r = try Int.init(al);
+ try Int.divTrunc(&q, &r, a, b);
+
+ const qs = try q.toString(al, 16);
+ testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1"));
+
+ const rs = try r.toString(al, 16);
+ testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1"));
+}
+
+test "big.int div multi-multi fuzz case #2" {
+ var a = try Int.init(al);
+ var b = try Int.init(al);
+
+ try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000");
+ try b.setString(16, "ffc0000000000000000000000000000000000000000000000000");
+
+ var q = try Int.init(al);
+ var r = try Int.init(al);
+ try Int.divTrunc(&q, &r, a, b);
+
+ const qs = try q.toString(al, 16);
+ testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4"));
+
+ const rs = try r.toString(al, 16);
+ testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000"));
+}
+
test "big.int shift-right single" {
var a = try Int.initSet(al, 0xffff0000);
try a.shiftRight(a, 16);
diff --git a/std/math/big/rational.zig b/std/math/big/rational.zig
new file mode 100644
index 0000000000..58a5e3ac76
--- /dev/null
+++ b/std/math/big/rational.zig
@@ -0,0 +1,938 @@
+const std = @import("../../std.zig");
+const builtin = @import("builtin");
+const debug = std.debug;
+const math = std.math;
+const mem = std.mem;
+const testing = std.testing;
+const Allocator = mem.Allocator;
+const ArrayList = std.ArrayList;
+
+const TypeId = builtin.TypeId;
+
+const bn = @import("int.zig");
+const Limb = bn.Limb;
+const DoubleLimb = bn.DoubleLimb;
+const Int = bn.Int;
+
+/// An arbitrary-precision rational number.
+///
+/// Memory is allocated as needed for operations to ensure full precision is kept. The precision
+/// of a Rational is only bounded by memory.
+///
+/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
+/// gcd(p, q) = 1 always.
+pub const Rational = struct {
+ /// Numerator. Determines the sign of the Rational.
+ p: Int,
+
+ /// Denominator. Sign is ignored.
+ q: Int,
+
+ /// Create a new Rational. A small amount of memory will be allocated on initialization.
+ /// This will be 2 * Int.default_capacity.
+ pub fn init(a: *Allocator) !Rational {
+ return Rational{
+ .p = try Int.init(a),
+ .q = try Int.initSet(a, 1),
+ };
+ }
+
+ /// Frees all memory associated with a Rational.
+ pub fn deinit(self: *Rational) void {
+ self.p.deinit();
+ self.q.deinit();
+ }
+
+ /// Set a Rational from a primitive integer type.
+ pub fn setInt(self: *Rational, a: var) !void {
+ try self.p.set(a);
+ try self.q.set(1);
+ }
+
+ /// Set a Rational from a string of the form `A/B` where A and B are base-10 integers.
+ pub fn setFloatString(self: *Rational, str: []const u8) !void {
+ // TODO: Accept a/b fractions and exponent form
+ if (str.len == 0) {
+ return error.InvalidFloatString;
+ }
+
+ const State = enum {
+ Integer,
+ Fractional,
+ };
+
+ var state = State.Integer;
+ var point: ?usize = null;
+
+ var start: usize = 0;
+ if (str[0] == '-') {
+ start += 1;
+ }
+
+ for (str) |c, i| {
+ switch (state) {
+ State.Integer => {
+ switch (c) {
+ '.' => {
+ state = State.Fractional;
+ point = i;
+ },
+ '0'...'9' => {
+ // okay
+ },
+ else => {
+ return error.InvalidFloatString;
+ },
+ }
+ },
+ State.Fractional => {
+ switch (c) {
+ '0'...'9' => {
+ // okay
+ },
+ else => {
+ return error.InvalidFloatString;
+ },
+ }
+ },
+ }
+ }
+
+ // TODO: batch the multiplies by 10
+ if (point) |i| {
+ try self.p.setString(10, str[0..i]);
+
+ const base = Int.initFixed(([]Limb{10})[0..]);
+
+ var j: usize = start;
+ while (j < str.len - i - 1) : (j += 1) {
+ try self.p.mul(self.p, base);
+ }
+
+ try self.q.setString(10, str[i + 1 ..]);
+ try self.p.add(self.p, self.q);
+
+ try self.q.set(1);
+ var k: usize = i + 1;
+ while (k < str.len) : (k += 1) {
+ try self.q.mul(self.q, base);
+ }
+
+ try self.reduce();
+ } else {
+ try self.p.setString(10, str[0..]);
+ try self.q.set(1);
+ }
+ }
+
+ /// Set a Rational from a floating-point value. The rational will have enough precision to
+ /// completely represent the provided float.
+ pub fn setFloat(self: *Rational, comptime T: type, f: T) !void {
+ // Translated from golang.go/src/math/big/rat.go.
+ debug.assert(@typeId(T) == builtin.TypeId.Float);
+
+ const UnsignedIntType = @IntType(false, T.bit_count);
+ const f_bits = @bitCast(UnsignedIntType, f);
+
+ const exponent_bits = math.floatExponentBits(T);
+ const exponent_bias = (1 << (exponent_bits - 1)) - 1;
+ const mantissa_bits = math.floatMantissaBits(T);
+
+ const exponent_mask = (1 << exponent_bits) - 1;
+ const mantissa_mask = (1 << mantissa_bits) - 1;
+
+ var exponent = @intCast(i16, (f_bits >> mantissa_bits) & exponent_mask);
+ var mantissa = f_bits & mantissa_mask;
+
+ switch (exponent) {
+ exponent_mask => {
+ return error.NonFiniteFloat;
+ },
+ 0 => {
+ // denormal
+ exponent -= exponent_bias - 1;
+ },
+ else => {
+ // normal
+ mantissa |= 1 << mantissa_bits;
+ exponent -= exponent_bias;
+ },
+ }
+
+ var shift: i16 = mantissa_bits - exponent;
+
+ // factor out powers of two early from rational
+ while (mantissa & 1 == 0 and shift > 0) {
+ mantissa >>= 1;
+ shift -= 1;
+ }
+
+ try self.p.set(mantissa);
+ self.p.setSign(f >= 0);
+
+ try self.q.set(1);
+ if (shift >= 0) {
+ try self.q.shiftLeft(self.q, @intCast(usize, shift));
+ } else {
+ try self.p.shiftLeft(self.p, @intCast(usize, -shift));
+ }
+
+ try self.reduce();
+ }
+
+ /// Return a floating-point value that is the closest value to a Rational.
+ ///
+ /// The result may not be exact if the Rational is too precise or too large for the
+ /// target type.
+ pub fn toFloat(self: Rational, comptime T: type) !T {
+ // Translated from golang.go/src/math/big/rat.go.
+ // TODO: Indicate whether the result is not exact.
+ debug.assert(@typeId(T) == builtin.TypeId.Float);
+
+ const fsize = T.bit_count;
+ const BitReprType = @IntType(false, T.bit_count);
+
+ const msize = math.floatMantissaBits(T);
+ const msize1 = msize + 1;
+ const msize2 = msize1 + 1;
+
+ const esize = math.floatExponentBits(T);
+ const ebias = (1 << (esize - 1)) - 1;
+ const emin = 1 - ebias;
+ const emax = ebias;
+
+ if (self.p.eqZero()) {
+ return 0;
+ }
+
+ // 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)]
+ var exp = @intCast(isize, self.p.bitCountTwosComp()) - @intCast(isize, self.q.bitCountTwosComp());
+
+ var a2 = try self.p.clone();
+ defer a2.deinit();
+
+ var b2 = try self.q.clone();
+ defer b2.deinit();
+
+ const shift = msize2 - exp;
+ if (shift >= 0) {
+ try a2.shiftLeft(a2, @intCast(usize, shift));
+ } else {
+ try b2.shiftLeft(b2, @intCast(usize, -shift));
+ }
+
+ // 2. compute quotient and remainder
+ var q = try Int.init(self.p.allocator.?);
+ defer q.deinit();
+
+ // unused
+ var r = try Int.init(self.p.allocator.?);
+ defer r.deinit();
+
+ try Int.divTrunc(&q, &r, a2, b2);
+
+ var mantissa = extractLowBits(q, BitReprType);
+ var have_rem = r.len() > 0;
+
+ // 3. q didn't fit in msize2 bits, redo division b2 << 1
+ if (mantissa >> msize2 == 1) {
+ if (mantissa & 1 == 1) {
+ have_rem = true;
+ }
+ mantissa >>= 1;
+ exp += 1;
+ }
+ if (mantissa >> msize1 != 1) {
+ // NOTE: This can be hit if the limb size is small (u8/16).
+ @panic("unexpected bits in result");
+ }
+
+ // 4. Rounding
+ if (emin - msize <= exp and exp <= emin) {
+ // denormal
+ const shift1 = @intCast(math.Log2Int(BitReprType), emin - (exp - 1));
+ const lost_bits = mantissa & ((@intCast(BitReprType, 1) << shift1) - 1);
+ have_rem = have_rem or lost_bits != 0;
+ mantissa >>= shift1;
+ exp = 2 - ebias;
+ }
+
+ // round q using round-half-to-even
+ var exact = !have_rem;
+ if (mantissa & 1 != 0) {
+ exact = false;
+ if (have_rem or (mantissa & 2 != 0)) {
+ mantissa += 1;
+ if (mantissa >= 1 << msize2) {
+ // 11...1 => 100...0
+ mantissa >>= 1;
+ exp += 1;
+ }
+ }
+ }
+ mantissa >>= 1;
+
+ const f = math.scalbn(@intToFloat(T, mantissa), @intCast(i32, exp - msize1));
+ if (math.isInf(f)) {
+ exact = false;
+ }
+
+ return if (self.p.isPositive()) f else -f;
+ }
+
+ /// Set a rational from an integer ratio.
+ pub fn setRatio(self: *Rational, p: var, q: var) !void {
+ try self.p.set(p);
+ try self.q.set(q);
+
+ self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
+ self.q.setSign(true);
+
+ try self.reduce();
+
+ if (self.q.eqZero()) {
+ @panic("cannot set rational with denominator = 0");
+ }
+ }
+
+ /// Set a Rational directly from an Int.
+ pub fn copyInt(self: *Rational, a: Int) !void {
+ try self.p.copy(a);
+ try self.q.set(1);
+ }
+
+ /// Set a Rational directly from a ratio of two Int's.
+ pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
+ try self.p.copy(a);
+ try self.q.copy(b);
+
+ self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
+ self.q.setSign(true);
+
+ try self.reduce();
+ }
+
+ /// Make a Rational positive.
+ pub fn abs(r: *Rational) void {
+ r.p.abs();
+ }
+
+ /// Negate the sign of a Rational.
+ pub fn negate(r: *Rational) void {
+ r.p.negate();
+ }
+
+ /// Efficiently swap a Rational with another. This swaps the limb pointers and a full copy is not
+ /// performed. The address of the limbs field will not be the same after this function.
+ pub fn swap(r: *Rational, other: *Rational) void {
+ r.p.swap(&other.p);
+ r.q.swap(&other.q);
+ }
+
+ /// Returns -1, 0, 1 if a < b, a == b or a > b respectively.
+ pub fn cmp(a: Rational, b: Rational) !i8 {
+ return cmpInternal(a, b, true);
+ }
+
+ /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
+ pub fn cmpAbs(a: Rational, b: Rational) !i8 {
+ return cmpInternal(a, b, false);
+ }
+
+ // p/q > x/y iff p*y > x*q
+ fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !i8 {
+ // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
+ // the memory allocations here?
+ var q = try Int.init(a.p.allocator.?);
+ defer q.deinit();
+
+ var p = try Int.init(b.p.allocator.?);
+ defer p.deinit();
+
+ try q.mul(a.p, b.q);
+ try p.mul(b.p, a.q);
+
+ return if (is_abs) q.cmpAbs(p) else q.cmp(p);
+ }
+
+ /// rma = a + b.
+ ///
+ /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
+ ///
+ /// Returns an error if memory could not be allocated.
+ pub fn add(rma: *Rational, a: Rational, b: Rational) !void {
+ var r = rma;
+ var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
+
+ var sr: Rational = undefined;
+ if (aliased) {
+ sr = try Rational.init(rma.p.allocator.?);
+ r = &sr;
+ aliased = true;
+ }
+ defer if (aliased) {
+ rma.swap(r);
+ r.deinit();
+ };
+
+ try r.p.mul(a.p, b.q);
+ try r.q.mul(b.p, a.q);
+ try r.p.add(r.p, r.q);
+
+ try r.q.mul(a.q, b.q);
+ try r.reduce();
+ }
+
+ /// rma = a - b.
+ ///
+ /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
+ ///
+ /// Returns an error if memory could not be allocated.
+ pub fn sub(rma: *Rational, a: Rational, b: Rational) !void {
+ var r = rma;
+ var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
+
+ var sr: Rational = undefined;
+ if (aliased) {
+ sr = try Rational.init(rma.p.allocator.?);
+ r = &sr;
+ aliased = true;
+ }
+ defer if (aliased) {
+ rma.swap(r);
+ r.deinit();
+ };
+
+ try r.p.mul(a.p, b.q);
+ try r.q.mul(b.p, a.q);
+ try r.p.sub(r.p, r.q);
+
+ try r.q.mul(a.q, b.q);
+ try r.reduce();
+ }
+
+ /// rma = a * b.
+ ///
+ /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
+ ///
+ /// Returns an error if memory could not be allocated.
+ pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
+ try r.p.mul(a.p, b.p);
+ try r.q.mul(a.q, b.q);
+ try r.reduce();
+ }
+
+ /// rma = a / b.
+ ///
+ /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
+ ///
+ /// Returns an error if memory could not be allocated.
+ pub fn div(r: *Rational, a: Rational, b: Rational) !void {
+ if (b.p.eqZero()) {
+ @panic("division by zero");
+ }
+
+ try r.p.mul(a.p, b.q);
+ try r.q.mul(b.p, a.q);
+ try r.reduce();
+ }
+
+ /// Invert the numerator and denominator fields of a Rational. p/q => q/p.
+ pub fn invert(r: *Rational) void {
+ Int.swap(&r.p, &r.q);
+ }
+
+ // reduce r/q such that gcd(r, q) = 1
+ fn reduce(r: *Rational) !void {
+ var a = try Int.init(r.p.allocator.?);
+ defer a.deinit();
+
+ const sign = r.p.isPositive();
+ r.p.abs();
+ try gcd(&a, r.p, r.q);
+ r.p.setSign(sign);
+
+ const one = Int.initFixed(([]Limb{1})[0..]);
+ if (a.cmp(one) != 0) {
+ var unused = try Int.init(r.p.allocator.?);
+ defer unused.deinit();
+
+ // TODO: divexact would be useful here
+ // TODO: don't copy r.q for div
+ try Int.divTrunc(&r.p, &unused, r.p, a);
+ try Int.divTrunc(&r.q, &unused, r.q, a);
+ }
+ }
+};
+
+const SignedDoubleLimb = @IntType(true, DoubleLimb.bit_count);
+
+fn gcd(rma: *Int, x: Int, y: Int) !void {
+ rma.assertWritable();
+ var r = rma;
+ var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr;
+
+ var sr: Int = undefined;
+ if (aliased) {
+ sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len()));
+ r = &sr;
+ aliased = true;
+ }
+ defer if (aliased) {
+ rma.swap(r);
+ r.deinit();
+ };
+
+ try gcdLehmer(r, x, y);
+}
+
+// Storage must live for the lifetime of the returned value
+fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int {
+ std.debug.assert(storage.len >= 2);
+
+ var A_is_positive = A >= 0;
+ const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
+ storage[0] = @truncate(Limb, Au);
+ storage[1] = @truncate(Limb, Au >> Limb.bit_count);
+ var Ap = Int.initFixed(storage[0..2]);
+ Ap.setSign(A_is_positive);
+ return Ap;
+}
+
+fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void {
+ var x = try xa.clone();
+ x.abs();
+ defer x.deinit();
+
+ var y = try ya.clone();
+ y.abs();
+ defer y.deinit();
+
+ if (x.cmp(y) < 0) {
+ x.swap(&y);
+ }
+
+ var T = try Int.init(r.allocator.?);
+ defer T.deinit();
+
+ while (y.len() > 1) {
+ debug.assert(x.isPositive() and y.isPositive());
+ debug.assert(x.len() >= y.len());
+
+ var xh: SignedDoubleLimb = x.limbs[x.len() - 1];
+ var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1];
+
+ var A: SignedDoubleLimb = 1;
+ var B: SignedDoubleLimb = 0;
+ var C: SignedDoubleLimb = 0;
+ var D: SignedDoubleLimb = 1;
+
+ while (yh + C != 0 and yh + D != 0) {
+ const q = @divFloor(xh + A, yh + C);
+ const qp = @divFloor(xh + B, yh + D);
+ if (q != qp) {
+ break;
+ }
+
+ var t = A - q * C;
+ A = C;
+ C = t;
+ t = B - q * D;
+ B = D;
+ D = t;
+
+ t = xh - q * yh;
+ xh = yh;
+ yh = t;
+ }
+
+ if (B == 0) {
+ // T = x % y, r is unused
+ try Int.divTrunc(r, &T, x, y);
+ debug.assert(T.isPositive());
+
+ x.swap(&y);
+ y.swap(&T);
+ } else {
+ var storage: [8]Limb = undefined;
+ const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]);
+ const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]);
+ const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]);
+ const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]);
+
+ // T = Ax + By
+ try r.mul(x, Ap);
+ try T.mul(y, Bp);
+ try T.add(r.*, T);
+
+ // u = Cx + Dy, r as u
+ try x.mul(x, Cp);
+ try r.mul(y, Dp);
+ try r.add(x, r.*);
+
+ x.swap(&T);
+ y.swap(r);
+ }
+ }
+
+ // euclidean algorithm
+ debug.assert(x.cmp(y) >= 0);
+
+ while (!y.eqZero()) {
+ try Int.divTrunc(&T, r, x, y);
+ x.swap(&y);
+ y.swap(r);
+ }
+
+ r.swap(&x);
+}
+
+var buffer: [64 * 8192]u8 = undefined;
+var fixed = std.heap.FixedBufferAllocator.init(buffer[0..]);
+var al = &fixed.allocator;
+
+test "big.rational gcd non-one small" {
+ var a = try Int.initSet(al, 17);
+ var b = try Int.initSet(al, 97);
+ var r = try Int.init(al);
+
+ try gcd(&r, a, b);
+
+ testing.expect((try r.to(u32)) == 1);
+}
+
+test "big.rational gcd non-one small" {
+ var a = try Int.initSet(al, 4864);
+ var b = try Int.initSet(al, 3458);
+ var r = try Int.init(al);
+
+ try gcd(&r, a, b);
+
+ testing.expect((try r.to(u32)) == 38);
+}
+
+test "big.rational gcd non-one large" {
+ var a = try Int.initSet(al, 0xffffffffffffffff);
+ var b = try Int.initSet(al, 0xffffffffffffffff7777);
+ var r = try Int.init(al);
+
+ try gcd(&r, a, b);
+
+ testing.expect((try r.to(u32)) == 4369);
+}
+
+test "big.rational gcd large multi-limb result" {
+ var a = try Int.initSet(al, 0x12345678123456781234567812345678123456781234567812345678);
+ var b = try Int.initSet(al, 0x12345671234567123456712345671234567123456712345671234567);
+ var r = try Int.init(al);
+
+ try gcd(&r, a, b);
+
+ testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
+}
+
+test "big.rational gcd one large" {
+ var a = try Int.initSet(al, 1897056385327307);
+ var b = try Int.initSet(al, 2251799813685248);
+ var r = try Int.init(al);
+
+ try gcd(&r, a, b);
+
+ testing.expect((try r.to(u64)) == 1);
+}
+
+fn extractLowBits(a: Int, comptime T: type) T {
+ testing.expect(@typeId(T) == builtin.TypeId.Int);
+
+ if (T.bit_count <= Limb.bit_count) {
+ return @truncate(T, a.limbs[0]);
+ } else {
+ var r: T = 0;
+ comptime var i: usize = 0;
+
+ // Remainder is always 0 since if T.bit_count >= Limb.bit_count -> Limb | T and both
+ // are powers of two.
+ inline while (i < T.bit_count / Limb.bit_count) : (i += 1) {
+ r |= math.shl(T, a.limbs[i], i * Limb.bit_count);
+ }
+
+ return r;
+ }
+}
+
+test "big.rational extractLowBits" {
+ var a = try Int.initSet(al, 0x11112222333344441234567887654321);
+
+ const a1 = extractLowBits(a, u8);
+ testing.expect(a1 == 0x21);
+
+ const a2 = extractLowBits(a, u16);
+ testing.expect(a2 == 0x4321);
+
+ const a3 = extractLowBits(a, u32);
+ testing.expect(a3 == 0x87654321);
+
+ const a4 = extractLowBits(a, u64);
+ testing.expect(a4 == 0x1234567887654321);
+
+ const a5 = extractLowBits(a, u128);
+ testing.expect(a5 == 0x11112222333344441234567887654321);
+}
+
+test "big.rational set" {
+ var a = try Rational.init(al);
+
+ try a.setInt(5);
+ testing.expect((try a.p.to(u32)) == 5);
+ testing.expect((try a.q.to(u32)) == 1);
+
+ try a.setRatio(7, 3);
+ testing.expect((try a.p.to(u32)) == 7);
+ testing.expect((try a.q.to(u32)) == 3);
+
+ try a.setRatio(9, 3);
+ testing.expect((try a.p.to(i32)) == 3);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ try a.setRatio(-9, 3);
+ testing.expect((try a.p.to(i32)) == -3);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ try a.setRatio(9, -3);
+ testing.expect((try a.p.to(i32)) == -3);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ try a.setRatio(-9, -3);
+ testing.expect((try a.p.to(i32)) == 3);
+ testing.expect((try a.q.to(i32)) == 1);
+}
+
+test "big.rational setFloat" {
+ var a = try Rational.init(al);
+
+ try a.setFloat(f64, 2.5);
+ testing.expect((try a.p.to(i32)) == 5);
+ testing.expect((try a.q.to(i32)) == 2);
+
+ try a.setFloat(f32, -2.5);
+ testing.expect((try a.p.to(i32)) == -5);
+ testing.expect((try a.q.to(i32)) == 2);
+
+ try a.setFloat(f32, 3.141593);
+
+ // = 3.14159297943115234375
+ testing.expect((try a.p.to(u32)) == 3294199);
+ testing.expect((try a.q.to(u32)) == 1048576);
+
+ try a.setFloat(f64, 72.141593120712409172417410926841290461290467124);
+
+ // = 72.1415931207124145885245525278151035308837890625
+ testing.expect((try a.p.to(u128)) == 5076513310880537);
+ testing.expect((try a.q.to(u128)) == 70368744177664);
+}
+
+test "big.rational setFloatString" {
+ var a = try Rational.init(al);
+
+ try a.setFloatString("72.14159312071241458852455252781510353");
+
+ // = 72.1415931207124145885245525278151035308837890625
+ testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353);
+ testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000);
+}
+
+test "big.rational toFloat" {
+ var a = try Rational.init(al);
+
+ // = 3.14159297943115234375
+ try a.setRatio(3294199, 1048576);
+ testing.expect((try a.toFloat(f64)) == 3.14159297943115234375);
+
+ // = 72.1415931207124145885245525278151035308837890625
+ try a.setRatio(5076513310880537, 70368744177664);
+ testing.expect((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124);
+}
+
+test "big.rational set/to Float round-trip" {
+ var a = try Rational.init(al);
+ var prng = std.rand.DefaultPrng.init(0x5EED);
+ var i: usize = 0;
+ while (i < 512) : (i += 1) {
+ const r = prng.random.float(f64);
+ try a.setFloat(f64, r);
+ testing.expect((try a.toFloat(f64)) == r);
+ }
+}
+
+test "big.rational copy" {
+ var a = try Rational.init(al);
+
+ const b = try Int.initSet(al, 5);
+
+ try a.copyInt(b);
+ testing.expect((try a.p.to(u32)) == 5);
+ testing.expect((try a.q.to(u32)) == 1);
+
+ const c = try Int.initSet(al, 7);
+ const d = try Int.initSet(al, 3);
+
+ try a.copyRatio(c, d);
+ testing.expect((try a.p.to(u32)) == 7);
+ testing.expect((try a.q.to(u32)) == 3);
+
+ const e = try Int.initSet(al, 9);
+ const f = try Int.initSet(al, 3);
+
+ try a.copyRatio(e, f);
+ testing.expect((try a.p.to(u32)) == 3);
+ testing.expect((try a.q.to(u32)) == 1);
+}
+
+test "big.rational negate" {
+ var a = try Rational.init(al);
+
+ try a.setInt(-50);
+ testing.expect((try a.p.to(i32)) == -50);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ a.negate();
+ testing.expect((try a.p.to(i32)) == 50);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ a.negate();
+ testing.expect((try a.p.to(i32)) == -50);
+ testing.expect((try a.q.to(i32)) == 1);
+}
+
+test "big.rational abs" {
+ var a = try Rational.init(al);
+
+ try a.setInt(-50);
+ testing.expect((try a.p.to(i32)) == -50);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ a.abs();
+ testing.expect((try a.p.to(i32)) == 50);
+ testing.expect((try a.q.to(i32)) == 1);
+
+ a.abs();
+ testing.expect((try a.p.to(i32)) == 50);
+ testing.expect((try a.q.to(i32)) == 1);
+}
+
+test "big.rational swap" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+
+ try a.setRatio(50, 23);
+ try b.setRatio(17, 3);
+
+ testing.expect((try a.p.to(u32)) == 50);
+ testing.expect((try a.q.to(u32)) == 23);
+
+ testing.expect((try b.p.to(u32)) == 17);
+ testing.expect((try b.q.to(u32)) == 3);
+
+ a.swap(&b);
+
+ testing.expect((try a.p.to(u32)) == 17);
+ testing.expect((try a.q.to(u32)) == 3);
+
+ testing.expect((try b.p.to(u32)) == 50);
+ testing.expect((try b.q.to(u32)) == 23);
+}
+
+test "big.rational cmp" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+
+ try a.setRatio(500, 231);
+ try b.setRatio(18903, 8584);
+ testing.expect((try a.cmp(b)) < 0);
+
+ try a.setRatio(890, 10);
+ try b.setRatio(89, 1);
+ testing.expect((try a.cmp(b)) == 0);
+}
+
+test "big.rational add single-limb" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+
+ try a.setRatio(500, 231);
+ try b.setRatio(18903, 8584);
+ testing.expect((try a.cmp(b)) < 0);
+
+ try a.setRatio(890, 10);
+ try b.setRatio(89, 1);
+ testing.expect((try a.cmp(b)) == 0);
+}
+
+test "big.rational add" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+ var r = try Rational.init(al);
+
+ try a.setRatio(78923, 23341);
+ try b.setRatio(123097, 12441414);
+ try a.add(a, b);
+
+ try r.setRatio(984786924199, 290395044174);
+ testing.expect((try a.cmp(r)) == 0);
+}
+
+test "big.rational sub" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+ var r = try Rational.init(al);
+
+ try a.setRatio(78923, 23341);
+ try b.setRatio(123097, 12441414);
+ try a.sub(a, b);
+
+ try r.setRatio(979040510045, 290395044174);
+ testing.expect((try a.cmp(r)) == 0);
+}
+
+test "big.rational mul" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+ var r = try Rational.init(al);
+
+ try a.setRatio(78923, 23341);
+ try b.setRatio(123097, 12441414);
+ try a.mul(a, b);
+
+ try r.setRatio(571481443, 17082061422);
+ testing.expect((try a.cmp(r)) == 0);
+}
+
+test "big.rational div" {
+ var a = try Rational.init(al);
+ var b = try Rational.init(al);
+ var r = try Rational.init(al);
+
+ try a.setRatio(78923, 23341);
+ try b.setRatio(123097, 12441414);
+ try a.div(a, b);
+
+ try r.setRatio(75531824394, 221015929);
+ testing.expect((try a.cmp(r)) == 0);
+}
+
+test "big.rational div" {
+ var a = try Rational.init(al);
+ var r = try Rational.init(al);
+
+ try a.setRatio(78923, 23341);
+ a.invert();
+
+ try r.setRatio(23341, 78923);
+ testing.expect((try a.cmp(r)) == 0);
+
+ try a.setRatio(-78923, 23341);
+ a.invert();
+
+ try r.setRatio(-23341, 78923);
+ testing.expect((try a.cmp(r)) == 0);
+}
diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig
index 9497255269..5241e31323 100644
--- a/std/math/cbrt.zig
+++ b/std/math/cbrt.zig
@@ -1,13 +1,19 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - cbrt(+-0) = +-0
-// - cbrt(+-inf) = +-inf
-// - cbrt(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/cbrtf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/cbrt.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the cube root of x.
+///
+/// Special Cases:
+/// - cbrt(+-0) = +-0
+/// - cbrt(+-inf) = +-inf
+/// - cbrt(nan) = nan
pub fn cbrt(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/ceil.zig b/std/math/ceil.zig
index da83e0ec59..5f86093a6d 100644
--- a/std/math/ceil.zig
+++ b/std/math/ceil.zig
@@ -1,14 +1,20 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - ceil(+-0) = +-0
-// - ceil(+-inf) = +-inf
-// - ceil(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/ceilf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/ceil.c
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the least integer value greater than of equal to x.
+///
+/// Special Cases:
+/// - ceil(+-0) = +-0
+/// - ceil(+-inf) = +-inf
+/// - ceil(nan) = nan
pub fn ceil(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/complex.zig b/std/math/complex.zig
index cc0573b227..e5574f9cee 100644
--- a/std/math/complex.zig
+++ b/std/math/complex.zig
@@ -23,13 +23,18 @@ pub const sqrt = @import("complex/sqrt.zig").sqrt;
pub const tanh = @import("complex/tanh.zig").tanh;
pub const tan = @import("complex/tan.zig").tan;
+/// A complex number consisting of a real an imaginary part. T must be a floating-point value.
pub fn Complex(comptime T: type) type {
return struct {
const Self = @This();
+ /// Real part.
re: T,
+
+ /// Imaginary part.
im: T,
+ /// Create a new Complex number from the given real and imaginary parts.
pub fn new(re: T, im: T) Self {
return Self{
.re = re,
@@ -37,6 +42,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the sum of two complex numbers.
pub fn add(self: Self, other: Self) Self {
return Self{
.re = self.re + other.re,
@@ -44,6 +50,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the subtraction of two complex numbers.
pub fn sub(self: Self, other: Self) Self {
return Self{
.re = self.re - other.re,
@@ -51,6 +58,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the product of two complex numbers.
pub fn mul(self: Self, other: Self) Self {
return Self{
.re = self.re * other.re - self.im * other.im,
@@ -58,6 +66,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the quotient of two complex numbers.
pub fn div(self: Self, other: Self) Self {
const re_num = self.re * other.re + self.im * other.im;
const im_num = self.im * other.re - self.re * other.im;
@@ -69,6 +78,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the complex conjugate of a number.
pub fn conjugate(self: Self) Self {
return Self{
.re = self.re,
@@ -76,6 +86,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the reciprocal of a complex number.
pub fn reciprocal(self: Self) Self {
const m = self.re * self.re + self.im * self.im;
return Self{
@@ -84,6 +95,7 @@ pub fn Complex(comptime T: type) type {
};
}
+ /// Returns the magnitude of a complex number.
pub fn magnitude(self: Self) T {
return math.sqrt(self.re * self.re + self.im * self.im);
}
diff --git a/std/math/complex/abs.zig b/std/math/complex/abs.zig
index e1368d6ef6..8105f57218 100644
--- a/std/math/complex/abs.zig
+++ b/std/math/complex/abs.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the absolute value (modulus) of z.
pub fn abs(z: var) @typeOf(z.re) {
const T = @typeOf(z.re);
return math.hypot(T, z.re, z.im);
diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig
index 8aed26a71b..f3526cc9ff 100644
--- a/std/math/complex/acos.zig
+++ b/std/math/complex/acos.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the arc-cosine of z.
pub fn acos(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = cmath.asin(z);
diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig
index e72bf431fe..6f0fd2e36c 100644
--- a/std/math/complex/acosh.zig
+++ b/std/math/complex/acosh.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the hyperbolic arc-cosine of z.
pub fn acosh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = cmath.acos(z);
diff --git a/std/math/complex/arg.zig b/std/math/complex/arg.zig
index 0a2441d1fd..d0c9588b8d 100644
--- a/std/math/complex/arg.zig
+++ b/std/math/complex/arg.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the angular component (in radians) of z.
pub fn arg(z: var) @typeOf(z.re) {
const T = @typeOf(z.re);
return math.atan2(T, z.im, z.re);
diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig
index 6be775d748..76f94a286c 100644
--- a/std/math/complex/asin.zig
+++ b/std/math/complex/asin.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+// Returns the arc-sine of z.
pub fn asin(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const x = z.re;
diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig
index 8e09036750..da065aad01 100644
--- a/std/math/complex/asinh.zig
+++ b/std/math/complex/asinh.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the hyperbolic arc-sine of z.
pub fn asinh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re);
diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig
index 6b83adbd97..89bc8dfaf0 100644
--- a/std/math/complex/atan.zig
+++ b/std/math/complex/atan.zig
@@ -1,9 +1,16 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/catanf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/catan.c
+
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the arc-tangent of z.
pub fn atan(z: var) @typeOf(z) {
const T = @typeOf(z.re);
return switch (T) {
diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig
index 8edfb6e78e..225e7c61de 100644
--- a/std/math/complex/atanh.zig
+++ b/std/math/complex/atanh.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the hyperbolic arc-tangent of z.
pub fn atanh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re);
diff --git a/std/math/complex/conj.zig b/std/math/complex/conj.zig
index 7a42d365ea..bd71ca3c06 100644
--- a/std/math/complex/conj.zig
+++ b/std/math/complex/conj.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the complex conjugate of z.
pub fn conj(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
return Complex(T).new(z.re, -z.im);
diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig
index 71f9603c75..332009ffe5 100644
--- a/std/math/complex/cos.zig
+++ b/std/math/complex/cos.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the cosine of z.
pub fn cos(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const p = Complex(T).new(-z.im, z.re);
diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig
index 9806e41170..be7bfde963 100644
--- a/std/math/complex/cosh.zig
+++ b/std/math/complex/cosh.zig
@@ -1,3 +1,9 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/ccoshf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/ccosh.c
+
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
@@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
+/// Returns the hyperbolic arc-cosine of z.
pub fn cosh(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
return switch (T) {
diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig
index c74ac2fc08..9b686bebc3 100644
--- a/std/math/complex/exp.zig
+++ b/std/math/complex/exp.zig
@@ -1,3 +1,9 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/cexpf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/cexp.c
+
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
@@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
+/// Returns e raised to the power of z (e^z).
pub fn exp(z: var) @typeOf(z) {
const T = @typeOf(z.re);
diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig
index 6b4306bf77..d6f810793f 100644
--- a/std/math/complex/ldexp.zig
+++ b/std/math/complex/ldexp.zig
@@ -1,9 +1,16 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/__cexpf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/__cexp.c
+
const std = @import("../../std.zig");
const debug = std.debug;
const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns exp(z) scaled to avoid overflow.
pub fn ldexp_cexp(z: var, expt: i32) @typeOf(z) {
const T = @typeOf(z.re);
diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig
index 2b43a6970f..762b4fde9a 100644
--- a/std/math/complex/log.zig
+++ b/std/math/complex/log.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the natural logarithm of z.
pub fn log(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const r = cmath.abs(z);
diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig
index 9174bb3626..a2480453fc 100644
--- a/std/math/complex/pow.zig
+++ b/std/math/complex/pow.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns z raised to the complex power of c.
pub fn pow(comptime T: type, z: T, c: T) T {
const p = cmath.log(z);
const q = c.mul(p);
diff --git a/std/math/complex/proj.zig b/std/math/complex/proj.zig
index aadcff6ff6..c8f2d9fc6d 100644
--- a/std/math/complex/proj.zig
+++ b/std/math/complex/proj.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the projection of z onto the riemann sphere.
pub fn proj(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig
index 049631f31e..9ddc3a7a80 100644
--- a/std/math/complex/sin.zig
+++ b/std/math/complex/sin.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the sine of z.
pub fn sin(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const p = Complex(T).new(-z.im, z.re);
diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig
index 0b656e5354..6286d8447f 100644
--- a/std/math/complex/sinh.zig
+++ b/std/math/complex/sinh.zig
@@ -1,3 +1,9 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/csinhf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/csinh.c
+
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
@@ -6,6 +12,7 @@ const Complex = cmath.Complex;
const ldexp_cexp = @import("ldexp.zig").ldexp_cexp;
+/// Returns the hyperbolic sine of z.
pub fn sinh(z: var) @typeOf(z) {
const T = @typeOf(z.re);
return switch (T) {
diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig
index e935d0b238..36f4c28e29 100644
--- a/std/math/complex/sqrt.zig
+++ b/std/math/complex/sqrt.zig
@@ -1,9 +1,17 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/csqrtf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/csqrt.c
+
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the square root of z. The real and imaginary parts of the result have the same sign
+/// as the imaginary part of z.
pub fn sqrt(z: var) @typeOf(z) {
const T = @typeOf(z.re);
diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig
index 45e2873eb6..398b8295ca 100644
--- a/std/math/complex/tan.zig
+++ b/std/math/complex/tan.zig
@@ -4,6 +4,7 @@ const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the tanget of z.
pub fn tan(z: var) Complex(@typeOf(z.re)) {
const T = @typeOf(z.re);
const q = Complex(T).new(-z.im, z.re);
diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig
index de905ee3f6..5c14ec66f2 100644
--- a/std/math/complex/tanh.zig
+++ b/std/math/complex/tanh.zig
@@ -1,9 +1,16 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/ctanhf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/complex/ctanh.c
+
const std = @import("../../std.zig");
const testing = std.testing;
const math = std.math;
const cmath = math.complex;
const Complex = cmath.Complex;
+/// Returns the hyperbolic tangent of z.
pub fn tanh(z: var) @typeOf(z) {
const T = @typeOf(z.re);
return switch (T) {
diff --git a/std/math/copysign.zig b/std/math/copysign.zig
index ff8140b3ab..e4d90c395e 100644
--- a/std/math/copysign.zig
+++ b/std/math/copysign.zig
@@ -1,8 +1,15 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/math/copysignf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/copysign.c
+
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns a value with the magnitude of x and the sign of y.
pub fn copysign(comptime T: type, x: T, y: T) T {
return switch (T) {
f16 => copysign16(x, y),
diff --git a/std/math/cos.zig b/std/math/cos.zig
index 9479482894..dc4aff59d6 100644
--- a/std/math/cos.zig
+++ b/std/math/cos.zig
@@ -1,18 +1,23 @@
-// Special Cases:
+// Ported from go, which is licensed under a BSD-3 license.
+// https://golang.org/LICENSE
//
-// - cos(+-inf) = nan
-// - cos(nan) = nan
+// https://golang.org/src/math/sin.go
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the cosine of the radian value x.
+///
+/// Special Cases:
+/// - cos(+-inf) = nan
+/// - cos(nan) = nan
pub fn cos(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
- f32 => cos32(x),
- f64 => cos64(x),
+ f32 => cos_(f32, x),
+ f64 => cos_(f64, x),
else => @compileError("cos not implemented for " ++ @typeName(T)),
};
}
@@ -33,78 +38,24 @@ const C3 = 2.48015872888517045348E-5;
const C4 = -1.38888888888730564116E-3;
const C5 = 4.16666666666665929218E-2;
-// NOTE: This is taken from the go stdlib. The musl implementation is much more complex.
-//
-// This may have slight differences on some edge cases and may need to replaced if so.
-fn cos32(x_: f32) f32 {
- const pi4a = 7.85398125648498535156e-1;
- const pi4b = 3.77489470793079817668E-8;
- const pi4c = 2.69515142907905952645E-15;
- const m4pi = 1.273239544735162542821171882678754627704620361328125;
-
- var x = x_;
- if (math.isNan(x) or math.isInf(x)) {
- return math.nan(f32);
- }
-
- var sign = false;
- if (x < 0) {
- x = -x;
- }
-
- var y = math.floor(x * m4pi);
- var j = @floatToInt(i64, y);
+const pi4a = 7.85398125648498535156e-1;
+const pi4b = 3.77489470793079817668E-8;
+const pi4c = 2.69515142907905952645E-15;
+const m4pi = 1.273239544735162542821171882678754627704620361328125;
- if (j & 1 == 1) {
- j += 1;
- y += 1;
- }
-
- j &= 7;
- if (j > 3) {
- j -= 4;
- sign = !sign;
- }
- if (j > 1) {
- sign = !sign;
- }
-
- const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
- const w = z * z;
-
- const r = r: {
- if (j == 1 or j == 2) {
- break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
- } else {
- break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
- }
- };
-
- if (sign) {
- return -r;
- } else {
- return r;
- }
-}
-
-fn cos64(x_: f64) f64 {
- const pi4a = 7.85398125648498535156e-1;
- const pi4b = 3.77489470793079817668E-8;
- const pi4c = 2.69515142907905952645E-15;
- const m4pi = 1.273239544735162542821171882678754627704620361328125;
+fn cos_(comptime T: type, x_: T) T {
+ const I = @IntType(true, T.bit_count);
var x = x_;
if (math.isNan(x) or math.isInf(x)) {
- return math.nan(f64);
+ return math.nan(T);
}
var sign = false;
- if (x < 0) {
- x = -x;
- }
+ x = math.fabs(x);
var y = math.floor(x * m4pi);
- var j = @floatToInt(i64, y);
+ var j = @floatToInt(I, y);
if (j & 1 == 1) {
j += 1;
@@ -123,56 +74,51 @@ fn cos64(x_: f64) f64 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- const r = r: {
- if (j == 1 or j == 2) {
- break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
- } else {
- break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
- }
- };
+ const r = if (j == 1 or j == 2)
+ z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
+ else
+ 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
- if (sign) {
- return -r;
- } else {
- return r;
- }
+ return if (sign) -r else r;
}
test "math.cos" {
- expect(cos(f32(0.0)) == cos32(0.0));
- expect(cos(f64(0.0)) == cos64(0.0));
+ expect(cos(f32(0.0)) == cos_(f32, 0.0));
+ expect(cos(f64(0.0)) == cos_(f64, 0.0));
}
test "math.cos32" {
const epsilon = 0.000001;
- expect(math.approxEq(f32, cos32(0.0), 1.0, epsilon));
- expect(math.approxEq(f32, cos32(0.2), 0.980067, epsilon));
- expect(math.approxEq(f32, cos32(0.8923), 0.627623, epsilon));
- expect(math.approxEq(f32, cos32(1.5), 0.070737, epsilon));
- expect(math.approxEq(f32, cos32(37.45), 0.969132, epsilon));
- expect(math.approxEq(f32, cos32(89.123), 0.400798, epsilon));
+ expect(math.approxEq(f32, cos_(f32, 0.0), 1.0, epsilon));
+ expect(math.approxEq(f32, cos_(f32, 0.2), 0.980067, epsilon));
+ expect(math.approxEq(f32, cos_(f32, 0.8923), 0.627623, epsilon));
+ expect(math.approxEq(f32, cos_(f32, 1.5), 0.070737, epsilon));
+ expect(math.approxEq(f32, cos_(f32, -1.5), 0.070737, epsilon));
+ expect(math.approxEq(f32, cos_(f32, 37.45), 0.969132, epsilon));
+ expect(math.approxEq(f32, cos_(f32, 89.123), 0.400798, epsilon));
}
test "math.cos64" {
const epsilon = 0.000001;
- expect(math.approxEq(f64, cos64(0.0), 1.0, epsilon));
- expect(math.approxEq(f64, cos64(0.2), 0.980067, epsilon));
- expect(math.approxEq(f64, cos64(0.8923), 0.627623, epsilon));
- expect(math.approxEq(f64, cos64(1.5), 0.070737, epsilon));
- expect(math.approxEq(f64, cos64(37.45), 0.969132, epsilon));
- expect(math.approxEq(f64, cos64(89.123), 0.40080, epsilon));
+ expect(math.approxEq(f64, cos_(f64, 0.0), 1.0, epsilon));
+ expect(math.approxEq(f64, cos_(f64, 0.2), 0.980067, epsilon));
+ expect(math.approxEq(f64, cos_(f64, 0.8923), 0.627623, epsilon));
+ expect(math.approxEq(f64, cos_(f64, 1.5), 0.070737, epsilon));
+ expect(math.approxEq(f64, cos_(f64, -1.5), 0.070737, epsilon));
+ expect(math.approxEq(f64, cos_(f64, 37.45), 0.969132, epsilon));
+ expect(math.approxEq(f64, cos_(f64, 89.123), 0.40080, epsilon));
}
test "math.cos32.special" {
- expect(math.isNan(cos32(math.inf(f32))));
- expect(math.isNan(cos32(-math.inf(f32))));
- expect(math.isNan(cos32(math.nan(f32))));
+ expect(math.isNan(cos_(f32, math.inf(f32))));
+ expect(math.isNan(cos_(f32, -math.inf(f32))));
+ expect(math.isNan(cos_(f32, math.nan(f32))));
}
test "math.cos64.special" {
- expect(math.isNan(cos64(math.inf(f64))));
- expect(math.isNan(cos64(-math.inf(f64))));
- expect(math.isNan(cos64(math.nan(f64))));
+ expect(math.isNan(cos_(f64, math.inf(f64))));
+ expect(math.isNan(cos_(f64, -math.inf(f64))));
+ expect(math.isNan(cos_(f64, math.nan(f64))));
}
diff --git a/std/math/cosh.zig b/std/math/cosh.zig
index eb3082e5fa..75c5c15ec1 100644
--- a/std/math/cosh.zig
+++ b/std/math/cosh.zig
@@ -1,8 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - cosh(+-0) = 1
-// - cosh(+-inf) = +inf
-// - cosh(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/coshf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/cosh.c
const builtin = @import("builtin");
const std = @import("../std.zig");
@@ -11,6 +11,12 @@ const expo2 = @import("expo2.zig").expo2;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns the hyperbolic cosine of x.
+///
+/// Special Cases:
+/// - cosh(+-0) = 1
+/// - cosh(+-inf) = +inf
+/// - cosh(nan) = nan
pub fn cosh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/exp.zig b/std/math/exp.zig
index aabccffd0b..ad058646a4 100644
--- a/std/math/exp.zig
+++ b/std/math/exp.zig
@@ -1,13 +1,19 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - exp(+inf) = +inf
-// - exp(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/expf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/exp.c
const std = @import("../std.zig");
const math = std.math;
const assert = std.debug.assert;
const builtin = @import("builtin");
+/// Returns e raised to the power of x (e^x).
+///
+/// Special Cases:
+/// - exp(+inf) = +inf
+/// - exp(nan) = nan
pub fn exp(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/exp2.zig b/std/math/exp2.zig
index 392d45bf68..07a39576b1 100644
--- a/std/math/exp2.zig
+++ b/std/math/exp2.zig
@@ -1,12 +1,18 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - exp2(+inf) = +inf
-// - exp2(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/exp2f.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/exp2.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns 2 raised to the power of x (2^x).
+///
+/// Special Cases:
+/// - exp2(+inf) = +inf
+/// - exp2(nan) = nan
pub fn exp2(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/expm1.zig b/std/math/expm1.zig
index b83cc3e82e..5e347f86f6 100644
--- a/std/math/expm1.zig
+++ b/std/math/expm1.zig
@@ -1,14 +1,23 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - expm1(+inf) = +inf
-// - expm1(-inf) = -1
-// - expm1(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/expmf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/expm.c
+
+// TODO: Updated recently.
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns e raised to the power of x, minus 1 (e^x - 1). This is more accurate than exp(e, x) - 1
+/// when x is near 0.
+///
+/// Special Cases:
+/// - expm1(+inf) = +inf
+/// - expm1(-inf) = -1
+/// - expm1(nan) = nan
pub fn expm1(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/expo2.zig b/std/math/expo2.zig
index 57e2bf34c9..c00098a5a7 100644
--- a/std/math/expo2.zig
+++ b/std/math/expo2.zig
@@ -1,5 +1,12 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/math/__expo2f.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/__expo2.c
+
const math = @import("../math.zig");
+/// Returns exp(x) / 2 for x >= log(maxFloat(T)).
pub fn expo2(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/fabs.zig b/std/math/fabs.zig
index e30f788ae7..6469f38835 100644
--- a/std/math/fabs.zig
+++ b/std/math/fabs.zig
@@ -1,13 +1,19 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - fabs(+-inf) = +inf
-// - fabs(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/fabsf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/fabs.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns the absolute value of x.
+///
+/// Special Cases:
+/// - fabs(+-inf) = +inf
+/// - fabs(nan) = nan
pub fn fabs(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/floor.zig b/std/math/floor.zig
index ab45a8fee7..e5ff2b1fc1 100644
--- a/std/math/floor.zig
+++ b/std/math/floor.zig
@@ -1,14 +1,20 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - floor(+-0) = +-0
-// - floor(+-inf) = +-inf
-// - floor(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/floorf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/floor.c
const builtin = @import("builtin");
const expect = std.testing.expect;
const std = @import("../std.zig");
const math = std.math;
+/// Returns the greatest integer value less than or equal to x.
+///
+/// Special Cases:
+/// - floor(+-0) = +-0
+/// - floor(+-inf) = +-inf
+/// - floor(nan) = nan
pub fn floor(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/fma.zig b/std/math/fma.zig
index a317bc96de..19c306fa2a 100644
--- a/std/math/fma.zig
+++ b/std/math/fma.zig
@@ -1,7 +1,14 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/math/fmaf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/fma.c
+
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns x * y + z with a single rounding error.
pub fn fma(comptime T: type, x: T, y: T, z: T) T {
return switch (T) {
f32 => fma32(x, y, z),
@@ -16,7 +23,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 {
const u = @bitCast(u64, xy_z);
const e = (u >> 52) & 0x7FF;
- if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) {
+ if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or (xy_z - xy == z and xy_z - z == xy)) {
return @floatCast(f32, xy_z);
} else {
// TODO: Handle inexact case with double-rounding
@@ -24,6 +31,7 @@ fn fma32(x: f32, y: f32, z: f32) f32 {
}
}
+// NOTE: Upstream fma.c has been rewritten completely to raise fp exceptions more accurately.
fn fma64(x: f64, y: f64, z: f64) f64 {
if (!math.isFinite(x) or !math.isFinite(y)) {
return x * y + z;
diff --git a/std/math/frexp.zig b/std/math/frexp.zig
index 35eec315d9..2759cd6492 100644
--- a/std/math/frexp.zig
+++ b/std/math/frexp.zig
@@ -1,8 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - frexp(+-0) = +-0, 0
-// - frexp(+-inf) = +-inf, 0
-// - frexp(nan) = nan, undefined
+// https://git.musl-libc.org/cgit/musl/tree/src/math/frexpf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/frexp.c
const std = @import("../std.zig");
const math = std.math;
@@ -17,6 +17,13 @@ fn frexp_result(comptime T: type) type {
pub const frexp32_result = frexp_result(f32);
pub const frexp64_result = frexp_result(f64);
+/// Breaks x into a normalized fraction and an integral power of two.
+/// f == frac * 2^exp, with |frac| in the interval [0.5, 1).
+///
+/// Special Cases:
+/// - frexp(+-0) = +-0, 0
+/// - frexp(+-inf) = +-inf, 0
+/// - frexp(nan) = nan, undefined
pub fn frexp(x: var) frexp_result(@typeOf(x)) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/hypot.zig b/std/math/hypot.zig
index fddbe7c068..c15da1495e 100644
--- a/std/math/hypot.zig
+++ b/std/math/hypot.zig
@@ -1,15 +1,21 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - hypot(+-inf, y) = +inf
-// - hypot(x, +-inf) = +inf
-// - hypot(nan, y) = nan
-// - hypot(x, nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/hypotf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/hypot.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns sqrt(x * x + y * y), avoiding unncessary overflow and underflow.
+///
+/// Special Cases:
+/// - hypot(+-inf, y) = +inf
+/// - hypot(x, +-inf) = +inf
+/// - hypot(nan, y) = nan
+/// - hypot(x, nan) = nan
pub fn hypot(comptime T: type, x: T, y: T) T {
return switch (T) {
f32 => hypot32(x, y),
diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig
index 2dfd42a62c..fe4158a6dd 100644
--- a/std/math/ilogb.zig
+++ b/std/math/ilogb.zig
@@ -1,8 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - ilogb(+-inf) = maxInt(i32)
-// - ilogb(0) = maxInt(i32)
-// - ilogb(nan) = maxInt(i32)
+// https://git.musl-libc.org/cgit/musl/tree/src/math/ilogbf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/ilogb.c
const std = @import("../std.zig");
const math = std.math;
@@ -10,6 +10,12 @@ const expect = std.testing.expect;
const maxInt = std.math.maxInt;
const minInt = std.math.minInt;
+/// Returns the binary exponent of x as an integer.
+///
+/// Special Cases:
+/// - ilogb(+-inf) = maxInt(i32)
+/// - ilogb(0) = maxInt(i32)
+/// - ilogb(nan) = maxInt(i32)
pub fn ilogb(x: var) i32 {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/inf.zig b/std/math/inf.zig
index e1bfbb197a..86ff245533 100644
--- a/std/math/inf.zig
+++ b/std/math/inf.zig
@@ -1,6 +1,7 @@
const std = @import("../std.zig");
const math = std.math;
+/// Returns value inf for the type T.
pub fn inf(comptime T: type) T {
return switch (T) {
f16 => math.inf_f16,
diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig
index ee8a5ff590..99eba668f9 100644
--- a/std/math/isfinite.zig
+++ b/std/math/isfinite.zig
@@ -3,6 +3,7 @@ const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns whether x is a finite value.
pub fn isFinite(x: var) bool {
const T = @typeOf(x);
switch (T) {
diff --git a/std/math/isinf.zig b/std/math/isinf.zig
index 1b1759bd36..37934f4cf4 100644
--- a/std/math/isinf.zig
+++ b/std/math/isinf.zig
@@ -3,6 +3,7 @@ const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns whether x is an infinity, ignoring sign.
pub fn isInf(x: var) bool {
const T = @typeOf(x);
switch (T) {
@@ -28,6 +29,7 @@ pub fn isInf(x: var) bool {
}
}
+/// Returns whether x is an infinity with a positive sign.
pub fn isPositiveInf(x: var) bool {
const T = @typeOf(x);
switch (T) {
@@ -49,6 +51,7 @@ pub fn isPositiveInf(x: var) bool {
}
}
+/// Returns whether x is an infinity with a negative sign.
pub fn isNegativeInf(x: var) bool {
const T = @typeOf(x);
switch (T) {
diff --git a/std/math/isnan.zig b/std/math/isnan.zig
index 9e541bf0a2..cf8cd2e1c2 100644
--- a/std/math/isnan.zig
+++ b/std/math/isnan.zig
@@ -3,13 +3,15 @@ const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns whether x is a nan.
pub fn isNan(x: var) bool {
return x != x;
}
-/// Note: A signalling nan is identical to a standard nan right now but may have a different bit
-/// representation in the future when required.
+/// Returns whether x is a signalling nan.
pub fn isSignalNan(x: var) bool {
+ // Note: A signalling nan is identical to a standard nan right now but may have a different bit
+ // representation in the future when required.
return isNan(x);
}
diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig
index cddcada1d3..f8611ef805 100644
--- a/std/math/isnormal.zig
+++ b/std/math/isnormal.zig
@@ -3,6 +3,7 @@ const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+// Returns whether x has a normalized representation (i.e. integer part of mantissa is 1).
pub fn isNormal(x: var) bool {
const T = @typeOf(x);
switch (T) {
diff --git a/std/math/ln.zig b/std/math/ln.zig
index 82b212f00f..c5d4c9ff25 100644
--- a/std/math/ln.zig
+++ b/std/math/ln.zig
@@ -1,9 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - ln(+inf) = +inf
-// - ln(0) = -inf
-// - ln(x) = nan if x < 0
-// - ln(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/lnf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/ln.c
const std = @import("../std.zig");
const math = std.math;
@@ -11,6 +10,13 @@ const expect = std.testing.expect;
const builtin = @import("builtin");
const TypeId = builtin.TypeId;
+/// Returns the natural logarithm of x.
+///
+/// Special Cases:
+/// - ln(+inf) = +inf
+/// - ln(0) = -inf
+/// - ln(x) = nan if x < 0
+/// - ln(nan) = nan
pub fn ln(x: var) @typeOf(x) {
const T = @typeOf(x);
switch (@typeId(T)) {
diff --git a/std/math/log.zig b/std/math/log.zig
index 100dc2fb7f..77f3639fd2 100644
--- a/std/math/log.zig
+++ b/std/math/log.zig
@@ -1,9 +1,16 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/math/logf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log.c
+
const std = @import("../std.zig");
const math = std.math;
const builtin = @import("builtin");
const TypeId = builtin.TypeId;
const expect = std.testing.expect;
+/// Returns the logarithm of x for the provided base.
pub fn log(comptime T: type, base: T, x: T) T {
if (base == 2) {
return math.log2(x);
diff --git a/std/math/log10.zig b/std/math/log10.zig
index 7311778ddd..9b0bc3ac52 100644
--- a/std/math/log10.zig
+++ b/std/math/log10.zig
@@ -1,9 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - log10(+inf) = +inf
-// - log10(0) = -inf
-// - log10(x) = nan if x < 0
-// - log10(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log10f.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log10.c
const std = @import("../std.zig");
const math = std.math;
@@ -12,6 +11,13 @@ const builtin = @import("builtin");
const TypeId = builtin.TypeId;
const maxInt = std.math.maxInt;
+/// Returns the base-10 logarithm of x.
+///
+/// Special Cases:
+/// - log10(+inf) = +inf
+/// - log10(0) = -inf
+/// - log10(x) = nan if x < 0
+/// - log10(nan) = nan
pub fn log10(x: var) @typeOf(x) {
const T = @typeOf(x);
switch (@typeId(T)) {
diff --git a/std/math/log1p.zig b/std/math/log1p.zig
index c9be1132be..bae6deb536 100644
--- a/std/math/log1p.zig
+++ b/std/math/log1p.zig
@@ -1,16 +1,22 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - log1p(+inf) = +inf
-// - log1p(+-0) = +-0
-// - log1p(-1) = -inf
-// - log1p(x) = nan if x < -1
-// - log1p(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log1pf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log1p.c
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the natural logarithm of 1 + x with greater accuracy when x is near zero.
+///
+/// Special Cases:
+/// - log1p(+inf) = +inf
+/// - log1p(+-0) = +-0
+/// - log1p(-1) = -inf
+/// - log1p(x) = nan if x < -1
+/// - log1p(nan) = nan
pub fn log1p(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/log2.zig b/std/math/log2.zig
index e2dbf4105a..88450a7ffd 100644
--- a/std/math/log2.zig
+++ b/std/math/log2.zig
@@ -1,9 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - log2(+inf) = +inf
-// - log2(0) = -inf
-// - log2(x) = nan if x < 0
-// - log2(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log2f.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/log2.c
const std = @import("../std.zig");
const math = std.math;
@@ -12,6 +11,13 @@ const builtin = @import("builtin");
const TypeId = builtin.TypeId;
const maxInt = std.math.maxInt;
+/// Returns the base-2 logarithm of x.
+///
+/// Special Cases:
+/// - log2(+inf) = +inf
+/// - log2(0) = -inf
+/// - log2(x) = nan if x < 0
+/// - log2(nan) = nan
pub fn log2(x: var) @typeOf(x) {
const T = @typeOf(x);
switch (@typeId(T)) {
diff --git a/std/math/modf.zig b/std/math/modf.zig
index e5a4964e63..92194d4c75 100644
--- a/std/math/modf.zig
+++ b/std/math/modf.zig
@@ -1,7 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - modf(+-inf) = +-inf, nan
-// - modf(nan) = nan, nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/modff.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/modf.c
const std = @import("../std.zig");
const math = std.math;
@@ -17,6 +18,12 @@ fn modf_result(comptime T: type) type {
pub const modf32_result = modf_result(f32);
pub const modf64_result = modf_result(f64);
+/// Returns the integer and fractional floating-point numbers that sum to x. The sign of each
+/// result is the same as the sign of x.
+///
+/// Special Cases:
+/// - modf(+-inf) = +-inf, nan
+/// - modf(nan) = nan, nan
pub fn modf(x: var) modf_result(@typeOf(x)) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/nan.zig b/std/math/nan.zig
index 1a11a141d5..5a01a5b3bd 100644
--- a/std/math/nan.zig
+++ b/std/math/nan.zig
@@ -1,5 +1,6 @@
const math = @import("../math.zig");
+/// Returns the nan representation for type T.
pub fn nan(comptime T: type) T {
return switch (T) {
f16 => math.nan_f16,
@@ -10,9 +11,10 @@ pub fn nan(comptime T: type) T {
};
}
-// Note: A signalling nan is identical to a standard right now by may have a different bit
-// representation in the future when required.
+/// Returns the signalling nan representation for type T.
pub fn snan(comptime T: type) T {
+ // Note: A signalling nan is identical to a standard right now by may have a different bit
+ // representation in the future when required.
return switch (T) {
f16 => @bitCast(f16, math.nan_u16),
f32 => @bitCast(f32, math.nan_u32),
diff --git a/std/math/pow.zig b/std/math/pow.zig
index 81bb2d95d5..18c9f80634 100644
--- a/std/math/pow.zig
+++ b/std/math/pow.zig
@@ -1,32 +1,36 @@
-// Special Cases:
+// Ported from go, which is licensed under a BSD-3 license.
+// https://golang.org/LICENSE
//
-// pow(x, +-0) = 1 for any x
-// pow(1, y) = 1 for any y
-// pow(x, 1) = x for any x
-// pow(nan, y) = nan
-// pow(x, nan) = nan
-// pow(+-0, y) = +-inf for y an odd integer < 0
-// pow(+-0, -inf) = +inf
-// pow(+-0, +inf) = +0
-// pow(+-0, y) = +inf for finite y < 0 and not an odd integer
-// pow(+-0, y) = +-0 for y an odd integer > 0
-// pow(+-0, y) = +0 for finite y > 0 and not an odd integer
-// pow(-1, +-inf) = 1
-// pow(x, +inf) = +inf for |x| > 1
-// pow(x, -inf) = +0 for |x| > 1
-// pow(x, +inf) = +0 for |x| < 1
-// pow(x, -inf) = +inf for |x| < 1
-// pow(+inf, y) = +inf for y > 0
-// pow(+inf, y) = +0 for y < 0
-// pow(-inf, y) = pow(-0, -y)
-// pow(x, y) = nan for finite x < 0 and finite non-integer y
+// https://golang.org/src/math/pow.go
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
-// This implementation is taken from the go stlib, musl is a bit more complex.
+/// Returns x raised to the power of y (x^y).
+///
+/// Special Cases:
+/// - pow(x, +-0) = 1 for any x
+/// - pow(1, y) = 1 for any y
+/// - pow(x, 1) = x for any x
+/// - pow(nan, y) = nan
+/// - pow(x, nan) = nan
+/// - pow(+-0, y) = +-inf for y an odd integer < 0
+/// - pow(+-0, -inf) = +inf
+/// - pow(+-0, +inf) = +0
+/// - pow(+-0, y) = +inf for finite y < 0 and not an odd integer
+/// - pow(+-0, y) = +-0 for y an odd integer > 0
+/// - pow(+-0, y) = +0 for finite y > 0 and not an odd integer
+/// - pow(-1, +-inf) = 1
+/// - pow(x, +inf) = +inf for |x| > 1
+/// - pow(x, -inf) = +0 for |x| > 1
+/// - pow(x, +inf) = +0 for |x| < 1
+/// - pow(x, -inf) = +inf for |x| < 1
+/// - pow(+inf, y) = +inf for y > 0
+/// - pow(+inf, y) = +0 for y < 0
+/// - pow(-inf, y) = pow(-0, -y)
+/// - pow(x, y) = nan for finite x < 0 and finite non-integer y
pub fn pow(comptime T: type, x: T, y: T) T {
if (@typeInfo(T) == builtin.TypeId.Int) {
return math.powi(T, x, y) catch unreachable;
@@ -53,15 +57,6 @@ pub fn pow(comptime T: type, x: T, y: T) T {
return x;
}
- // special case sqrt
- if (y == 0.5) {
- return math.sqrt(x);
- }
-
- if (y == -0.5) {
- return 1 / math.sqrt(x);
- }
-
if (x == 0) {
if (y < 0) {
// pow(+-0, y) = +- 0 for y an odd integer
@@ -112,14 +107,16 @@ pub fn pow(comptime T: type, x: T, y: T) T {
}
}
- var ay = y;
- var flip = false;
- if (ay < 0) {
- ay = -ay;
- flip = true;
+ // special case sqrt
+ if (y == 0.5) {
+ return math.sqrt(x);
+ }
+
+ if (y == -0.5) {
+ return 1 / math.sqrt(x);
}
- const r1 = math.modf(ay);
+ const r1 = math.modf(math.fabs(y));
var yi = r1.ipart;
var yf = r1.fpart;
@@ -148,8 +145,18 @@ pub fn pow(comptime T: type, x: T, y: T) T {
var xe = r2.exponent;
var x1 = r2.significand;
- var i = @floatToInt(i32, yi);
+ var i = @floatToInt(@IntType(true, T.bit_count), yi);
while (i != 0) : (i >>= 1) {
+ const overflow_shift = math.floatExponentBits(T) + 1;
+ if (xe < -(1 << overflow_shift) or (1 << overflow_shift) < xe) {
+ // catch xe before it overflows the left shift below
+ // Since i != 0 it has at least one bit still set, so ae will accumulate xe
+ // on at least one more iteration, ae += xe is a lower bound on ae
+ // the lower bound on ae exceeds the size of a float exp
+ // so the final call to Ldexp will produce under/overflow (0/Inf)
+ ae += xe;
+ break;
+ }
if (i & 1 == 1) {
a1 *= x1;
ae += xe;
@@ -163,7 +170,7 @@ pub fn pow(comptime T: type, x: T, y: T) T {
}
// a *= a1 * 2^ae
- if (flip) {
+ if (y < 0) {
a1 = 1 / a1;
ae = -ae;
}
@@ -202,6 +209,9 @@ test "math.pow.special" {
expect(pow(f32, 45, 1.0) == 45);
expect(pow(f32, -45, 1.0) == -45);
expect(math.isNan(pow(f32, math.nan(f32), 5.0)));
+ expect(math.isPositiveInf(pow(f32, -math.inf(f32), 0.5)));
+ expect(math.isPositiveInf(pow(f32, -0, -0.5)));
+ expect(pow(f32, -0, 0.5) == 0);
expect(math.isNan(pow(f32, 5.0, math.nan(f32))));
expect(math.isPositiveInf(pow(f32, 0.0, -1.0)));
//expect(math.isNegativeInf(pow(f32, -0.0, -3.0))); TODO is this required?
@@ -232,3 +242,11 @@ test "math.pow.special" {
expect(math.isNan(pow(f32, -1.0, 1.2)));
expect(math.isNan(pow(f32, -12.4, 78.5)));
}
+
+test "math.pow.overflow" {
+ expect(math.isPositiveInf(pow(f64, 2, 1 << 32)));
+ expect(pow(f64, 2, -(1 << 32)) == 0);
+ expect(math.isNegativeInf(pow(f64, -2, (1 << 32) + 1)));
+ expect(pow(f64, 0.5, 1 << 45) == 0);
+ expect(math.isPositiveInf(pow(f64, 0.5, -(1 << 45))));
+}
diff --git a/std/math/powi.zig b/std/math/powi.zig
index b7212efcbf..d80700e5cd 100644
--- a/std/math/powi.zig
+++ b/std/math/powi.zig
@@ -1,12 +1,7 @@
-// Special Cases:
+// Based on Rust, which is licensed under the MIT license.
+// https://github.com/rust-lang/rust/blob/360432f1e8794de58cd94f34c9c17ad65871e5b5/LICENSE-MIT
//
-// powi(x, +-0) = 1 for any x
-// powi(0, y) = 0 for any y
-// powi(1, y) = 1 for any y
-// powi(-1, y) = -1 for for y an odd integer
-// powi(-1, y) = 1 for for y an even integer
-// powi(x, y) = Overflow for for y >= @sizeOf(x) - 1 y > 0
-// powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0
+// https://github.com/rust-lang/rust/blob/360432f1e8794de58cd94f34c9c17ad65871e5b5/src/libcore/num/mod.rs#L3423
const builtin = @import("builtin");
const std = @import("../std.zig");
@@ -14,7 +9,16 @@ const math = std.math;
const assert = std.debug.assert;
const testing = std.testing;
-// This implementation is based on that from the rust stlib
+/// Returns the power of x raised by the integer y (x^y).
+///
+/// Special Cases:
+/// - powi(x, +-0) = 1 for any x
+/// - powi(0, y) = 0 for any y
+/// - powi(1, y) = 1 for any y
+/// - powi(-1, y) = -1 for y an odd integer
+/// - powi(-1, y) = 1 for y an even integer
+/// - powi(x, y) = Overflow for y >= @sizeOf(x) - 1 or y > 0
+/// - powi(x, y) = Underflow for y > @sizeOf(x) - 1 or y < 0
pub fn powi(comptime T: type, x: T, y: T) (error{
Overflow,
Underflow,
diff --git a/std/math/round.zig b/std/math/round.zig
index 39ff56ca79..0b80a46ce5 100644
--- a/std/math/round.zig
+++ b/std/math/round.zig
@@ -1,14 +1,20 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - round(+-0) = +-0
-// - round(+-inf) = +-inf
-// - round(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/roundf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/round.c
const builtin = @import("builtin");
const expect = std.testing.expect;
const std = @import("../std.zig");
const math = std.math;
+/// Returns x rounded to the nearest integer, rounding half away from zero.
+///
+/// Special Cases:
+/// - round(+-0) = +-0
+/// - round(+-inf) = +-inf
+/// - round(nan) = nan
pub fn round(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig
index d1338f5acb..d5716d621c 100644
--- a/std/math/scalbn.zig
+++ b/std/math/scalbn.zig
@@ -1,7 +1,14 @@
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
+//
+// https://git.musl-libc.org/cgit/musl/tree/src/math/scalbnf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/scalbn.c
+
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns x * 2^n.
pub fn scalbn(x: var, n: i32) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/signbit.zig b/std/math/signbit.zig
index 9727152b07..e5c5909292 100644
--- a/std/math/signbit.zig
+++ b/std/math/signbit.zig
@@ -2,6 +2,7 @@ const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns whether x is negative or negative 0.
pub fn signbit(x: var) bool {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/sin.zig b/std/math/sin.zig
index e25b8a292b..f21db4054e 100644
--- a/std/math/sin.zig
+++ b/std/math/sin.zig
@@ -1,19 +1,24 @@
-// Special Cases:
+// Ported from go, which is licensed under a BSD-3 license.
+// https://golang.org/LICENSE
//
-// - sin(+-0) = +-0
-// - sin(+-inf) = nan
-// - sin(nan) = nan
+// https://golang.org/src/math/sin.go
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the sine of the radian value x.
+///
+/// Special Cases:
+/// - sin(+-0) = +-0
+/// - sin(+-inf) = nan
+/// - sin(nan) = nan
pub fn sin(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
- f32 => sin32(x),
- f64 => sin64(x),
+ f32 => sin_(T, x),
+ f64 => sin_(T, x),
else => @compileError("sin not implemented for " ++ @typeName(T)),
};
}
@@ -34,83 +39,27 @@ const C3 = 2.48015872888517045348E-5;
const C4 = -1.38888888888730564116E-3;
const C5 = 4.16666666666665929218E-2;
-// NOTE: This is taken from the go stdlib. The musl implementation is much more complex.
-//
-// This may have slight differences on some edge cases and may need to replaced if so.
-fn sin32(x_: f32) f32 {
- const pi4a = 7.85398125648498535156e-1;
- const pi4b = 3.77489470793079817668E-8;
- const pi4c = 2.69515142907905952645E-15;
- const m4pi = 1.273239544735162542821171882678754627704620361328125;
-
- var x = x_;
- if (x == 0 or math.isNan(x)) {
- return x;
- }
- if (math.isInf(x)) {
- return math.nan(f32);
- }
-
- var sign = false;
- if (x < 0) {
- x = -x;
- sign = true;
- }
-
- var y = math.floor(x * m4pi);
- var j = @floatToInt(i64, y);
-
- if (j & 1 == 1) {
- j += 1;
- y += 1;
- }
+const pi4a = 7.85398125648498535156e-1;
+const pi4b = 3.77489470793079817668E-8;
+const pi4c = 2.69515142907905952645E-15;
+const m4pi = 1.273239544735162542821171882678754627704620361328125;
- j &= 7;
- if (j > 3) {
- j -= 4;
- sign = !sign;
- }
-
- const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
- const w = z * z;
-
- const r = r: {
- if (j == 1 or j == 2) {
- break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
- } else {
- break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
- }
- };
-
- if (sign) {
- return -r;
- } else {
- return r;
- }
-}
-
-fn sin64(x_: f64) f64 {
- const pi4a = 7.85398125648498535156e-1;
- const pi4b = 3.77489470793079817668E-8;
- const pi4c = 2.69515142907905952645E-15;
- const m4pi = 1.273239544735162542821171882678754627704620361328125;
+fn sin_(comptime T: type, x_: T) T {
+ const I = @IntType(true, T.bit_count);
var x = x_;
if (x == 0 or math.isNan(x)) {
return x;
}
if (math.isInf(x)) {
- return math.nan(f64);
+ return math.nan(T);
}
- var sign = false;
- if (x < 0) {
- x = -x;
- sign = true;
- }
+ var sign = x < 0;
+ x = math.fabs(x);
var y = math.floor(x * m4pi);
- var j = @floatToInt(i64, y);
+ var j = @floatToInt(I, y);
if (j & 1 == 1) {
j += 1;
@@ -126,61 +75,56 @@ fn sin64(x_: f64) f64 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- const r = r: {
- if (j == 1 or j == 2) {
- break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
- } else {
- break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
- }
- };
+ const r = if (j == 1 or j == 2)
+ 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
+ else
+ z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
- if (sign) {
- return -r;
- } else {
- return r;
- }
+ return if (sign) -r else r;
}
test "math.sin" {
- expect(sin(f32(0.0)) == sin32(0.0));
- expect(sin(f64(0.0)) == sin64(0.0));
+ expect(sin(f32(0.0)) == sin_(f32, 0.0));
+ expect(sin(f64(0.0)) == sin_(f64, 0.0));
expect(comptime (math.sin(f64(2))) == math.sin(f64(2)));
}
test "math.sin32" {
const epsilon = 0.000001;
- expect(math.approxEq(f32, sin32(0.0), 0.0, epsilon));
- expect(math.approxEq(f32, sin32(0.2), 0.198669, epsilon));
- expect(math.approxEq(f32, sin32(0.8923), 0.778517, epsilon));
- expect(math.approxEq(f32, sin32(1.5), 0.997495, epsilon));
- expect(math.approxEq(f32, sin32(37.45), -0.246544, epsilon));
- expect(math.approxEq(f32, sin32(89.123), 0.916166, epsilon));
+ expect(math.approxEq(f32, sin_(f32, 0.0), 0.0, epsilon));
+ expect(math.approxEq(f32, sin_(f32, 0.2), 0.198669, epsilon));
+ expect(math.approxEq(f32, sin_(f32, 0.8923), 0.778517, epsilon));
+ expect(math.approxEq(f32, sin_(f32, 1.5), 0.997495, epsilon));
+ expect(math.approxEq(f32, sin_(f32, -1.5), -0.997495, epsilon));
+ expect(math.approxEq(f32, sin_(f32, 37.45), -0.246544, epsilon));
+ expect(math.approxEq(f32, sin_(f32, 89.123), 0.916166, epsilon));
}
test "math.sin64" {
const epsilon = 0.000001;
- expect(math.approxEq(f64, sin64(0.0), 0.0, epsilon));
- expect(math.approxEq(f64, sin64(0.2), 0.198669, epsilon));
- expect(math.approxEq(f64, sin64(0.8923), 0.778517, epsilon));
- expect(math.approxEq(f64, sin64(1.5), 0.997495, epsilon));
- expect(math.approxEq(f64, sin64(37.45), -0.246543, epsilon));
- expect(math.approxEq(f64, sin64(89.123), 0.916166, epsilon));
+ expect(math.approxEq(f64, sin_(f64, 0.0), 0.0, epsilon));
+ expect(math.approxEq(f64, sin_(f64, 0.2), 0.198669, epsilon));
+ expect(math.approxEq(f64, sin_(f64, 0.8923), 0.778517, epsilon));
+ expect(math.approxEq(f64, sin_(f64, 1.5), 0.997495, epsilon));
+ expect(math.approxEq(f64, sin_(f64, -1.5), -0.997495, epsilon));
+ expect(math.approxEq(f64, sin_(f64, 37.45), -0.246543, epsilon));
+ expect(math.approxEq(f64, sin_(f64, 89.123), 0.916166, epsilon));
}
test "math.sin32.special" {
- expect(sin32(0.0) == 0.0);
- expect(sin32(-0.0) == -0.0);
- expect(math.isNan(sin32(math.inf(f32))));
- expect(math.isNan(sin32(-math.inf(f32))));
- expect(math.isNan(sin32(math.nan(f32))));
+ expect(sin_(f32, 0.0) == 0.0);
+ expect(sin_(f32, -0.0) == -0.0);
+ expect(math.isNan(sin_(f32, math.inf(f32))));
+ expect(math.isNan(sin_(f32, -math.inf(f32))));
+ expect(math.isNan(sin_(f32, math.nan(f32))));
}
test "math.sin64.special" {
- expect(sin64(0.0) == 0.0);
- expect(sin64(-0.0) == -0.0);
- expect(math.isNan(sin64(math.inf(f64))));
- expect(math.isNan(sin64(-math.inf(f64))));
- expect(math.isNan(sin64(math.nan(f64))));
+ expect(sin_(f64, 0.0) == 0.0);
+ expect(sin_(f64, -0.0) == -0.0);
+ expect(math.isNan(sin_(f64, math.inf(f64))));
+ expect(math.isNan(sin_(f64, -math.inf(f64))));
+ expect(math.isNan(sin_(f64, math.nan(f64))));
}
diff --git a/std/math/sinh.zig b/std/math/sinh.zig
index cf4363c4a9..73ee65ea6f 100644
--- a/std/math/sinh.zig
+++ b/std/math/sinh.zig
@@ -1,8 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - sinh(+-0) = +-0
-// - sinh(+-inf) = +-inf
-// - sinh(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/sinhf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/sinh.c
const builtin = @import("builtin");
const std = @import("../std.zig");
@@ -11,6 +11,12 @@ const expect = std.testing.expect;
const expo2 = @import("expo2.zig").expo2;
const maxInt = std.math.maxInt;
+/// Returns the hyperbolic sine of x.
+///
+/// Special Cases:
+/// - sinh(+-0) = +-0
+/// - sinh(+-inf) = +-inf
+/// - sinh(nan) = nan
pub fn sinh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig
index 9062f598a1..30af5915d4 100644
--- a/std/math/sqrt.zig
+++ b/std/math/sqrt.zig
@@ -1,10 +1,3 @@
-// Special Cases:
-//
-// - sqrt(+inf) = +inf
-// - sqrt(+-0) = +-0
-// - sqrt(x) = nan if x < 0
-// - sqrt(nan) = nan
-
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
@@ -12,6 +5,13 @@ const builtin = @import("builtin");
const TypeId = builtin.TypeId;
const maxInt = std.math.maxInt;
+/// Returns the square root of x.
+///
+/// Special Cases:
+/// - sqrt(+inf) = +inf
+/// - sqrt(+-0) = +-0
+/// - sqrt(x) = nan if x < 0
+/// - sqrt(nan) = nan
pub fn sqrt(x: var) (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
const T = @typeOf(x);
switch (@typeId(T)) {
diff --git a/std/math/tan.zig b/std/math/tan.zig
index fc11ebdef7..e8259ee7ad 100644
--- a/std/math/tan.zig
+++ b/std/math/tan.zig
@@ -1,19 +1,24 @@
-// Special Cases:
+// Ported from go, which is licensed under a BSD-3 license.
+// https://golang.org/LICENSE
//
-// - tan(+-0) = +-0
-// - tan(+-inf) = nan
-// - tan(nan) = nan
+// https://golang.org/src/math/tan.go
const builtin = @import("builtin");
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
+/// Returns the tangent of the radian value x.
+///
+/// Special Cases:
+/// - tan(+-0) = +-0
+/// - tan(+-inf) = nan
+/// - tan(nan) = nan
pub fn tan(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
- f32 => tan32(x),
- f64 => tan64(x),
+ f32 => tan_(f32, x),
+ f64 => tan_(f64, x),
else => @compileError("tan not implemented for " ++ @typeName(T)),
};
}
@@ -27,80 +32,27 @@ const Tq2 = -1.32089234440210967447E6;
const Tq3 = 2.50083801823357915839E7;
const Tq4 = -5.38695755929454629881E7;
-// NOTE: This is taken from the go stdlib. The musl implementation is much more complex.
-//
-// This may have slight differences on some edge cases and may need to replaced if so.
-fn tan32(x_: f32) f32 {
- const pi4a = 7.85398125648498535156e-1;
- const pi4b = 3.77489470793079817668E-8;
- const pi4c = 2.69515142907905952645E-15;
- const m4pi = 1.273239544735162542821171882678754627704620361328125;
-
- var x = x_;
- if (x == 0 or math.isNan(x)) {
- return x;
- }
- if (math.isInf(x)) {
- return math.nan(f32);
- }
-
- var sign = false;
- if (x < 0) {
- x = -x;
- sign = true;
- }
-
- var y = math.floor(x * m4pi);
- var j = @floatToInt(i64, y);
-
- if (j & 1 == 1) {
- j += 1;
- y += 1;
- }
-
- const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
- const w = z * z;
-
- var r = r: {
- if (w > 1e-14) {
- break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
- } else {
- break :r z;
- }
- };
-
- if (j & 2 == 2) {
- r = -1 / r;
- }
- if (sign) {
- r = -r;
- }
-
- return r;
-}
+const pi4a = 7.85398125648498535156e-1;
+const pi4b = 3.77489470793079817668E-8;
+const pi4c = 2.69515142907905952645E-15;
+const m4pi = 1.273239544735162542821171882678754627704620361328125;
-fn tan64(x_: f64) f64 {
- const pi4a = 7.85398125648498535156e-1;
- const pi4b = 3.77489470793079817668E-8;
- const pi4c = 2.69515142907905952645E-15;
- const m4pi = 1.273239544735162542821171882678754627704620361328125;
+fn tan_(comptime T: type, x_: T) T {
+ const I = @IntType(true, T.bit_count);
var x = x_;
if (x == 0 or math.isNan(x)) {
return x;
}
if (math.isInf(x)) {
- return math.nan(f64);
+ return math.nan(T);
}
- var sign = false;
- if (x < 0) {
- x = -x;
- sign = true;
- }
+ var sign = x < 0;
+ x = math.fabs(x);
var y = math.floor(x * m4pi);
- var j = @floatToInt(i64, y);
+ var j = @floatToInt(I, y);
if (j & 1 == 1) {
j += 1;
@@ -110,63 +62,57 @@ fn tan64(x_: f64) f64 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- var r = r: {
- if (w > 1e-14) {
- break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
- } else {
- break :r z;
- }
- };
+ var r = if (w > 1e-14)
+ z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
+ else
+ z;
if (j & 2 == 2) {
r = -1 / r;
}
- if (sign) {
- r = -r;
- }
- return r;
+ return if (sign) -r else r;
}
test "math.tan" {
- expect(tan(f32(0.0)) == tan32(0.0));
- expect(tan(f64(0.0)) == tan64(0.0));
+ expect(tan(f32(0.0)) == tan_(f32, 0.0));
+ expect(tan(f64(0.0)) == tan_(f64, 0.0));
}
test "math.tan32" {
const epsilon = 0.000001;
- expect(math.approxEq(f32, tan32(0.0), 0.0, epsilon));
- expect(math.approxEq(f32, tan32(0.2), 0.202710, epsilon));
- expect(math.approxEq(f32, tan32(0.8923), 1.240422, epsilon));
- expect(math.approxEq(f32, tan32(1.5), 14.101420, epsilon));
- expect(math.approxEq(f32, tan32(37.45), -0.254397, epsilon));
- expect(math.approxEq(f32, tan32(89.123), 2.285852, epsilon));
+ expect(math.approxEq(f32, tan_(f32, 0.0), 0.0, epsilon));
+ expect(math.approxEq(f32, tan_(f32, 0.2), 0.202710, epsilon));
+ expect(math.approxEq(f32, tan_(f32, 0.8923), 1.240422, epsilon));
+ expect(math.approxEq(f32, tan_(f32, 1.5), 14.101420, epsilon));
+ expect(math.approxEq(f32, tan_(f32, 37.45), -0.254397, epsilon));
+ expect(math.approxEq(f32, tan_(f32, 89.123), 2.285852, epsilon));
}
test "math.tan64" {
const epsilon = 0.000001;
- expect(math.approxEq(f64, tan64(0.0), 0.0, epsilon));
- expect(math.approxEq(f64, tan64(0.2), 0.202710, epsilon));
- expect(math.approxEq(f64, tan64(0.8923), 1.240422, epsilon));
- expect(math.approxEq(f64, tan64(1.5), 14.101420, epsilon));
- expect(math.approxEq(f64, tan64(37.45), -0.254397, epsilon));
- expect(math.approxEq(f64, tan64(89.123), 2.2858376, epsilon));
+ expect(math.approxEq(f64, tan_(f64, 0.0), 0.0, epsilon));
+ expect(math.approxEq(f64, tan_(f64, 0.2), 0.202710, epsilon));
+ expect(math.approxEq(f64, tan_(f64, 0.8923), 1.240422, epsilon));
+ expect(math.approxEq(f64, tan_(f64, 1.5), 14.101420, epsilon));
+ expect(math.approxEq(f64, tan_(f64, 37.45), -0.254397, epsilon));
+ expect(math.approxEq(f64, tan_(f64, 89.123), 2.2858376, epsilon));
}
test "math.tan32.special" {
- expect(tan32(0.0) == 0.0);
- expect(tan32(-0.0) == -0.0);
- expect(math.isNan(tan32(math.inf(f32))));
- expect(math.isNan(tan32(-math.inf(f32))));
- expect(math.isNan(tan32(math.nan(f32))));
+ expect(tan_(f32, 0.0) == 0.0);
+ expect(tan_(f32, -0.0) == -0.0);
+ expect(math.isNan(tan_(f32, math.inf(f32))));
+ expect(math.isNan(tan_(f32, -math.inf(f32))));
+ expect(math.isNan(tan_(f32, math.nan(f32))));
}
test "math.tan64.special" {
- expect(tan64(0.0) == 0.0);
- expect(tan64(-0.0) == -0.0);
- expect(math.isNan(tan64(math.inf(f64))));
- expect(math.isNan(tan64(-math.inf(f64))));
- expect(math.isNan(tan64(math.nan(f64))));
+ expect(tan_(f64, 0.0) == 0.0);
+ expect(tan_(f64, -0.0) == -0.0);
+ expect(math.isNan(tan_(f64, math.inf(f64))));
+ expect(math.isNan(tan_(f64, -math.inf(f64))));
+ expect(math.isNan(tan_(f64, math.nan(f64))));
}
diff --git a/std/math/tanh.zig b/std/math/tanh.zig
index f88ecfdc9c..48d26d091e 100644
--- a/std/math/tanh.zig
+++ b/std/math/tanh.zig
@@ -1,8 +1,8 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - sinh(+-0) = +-0
-// - sinh(+-inf) = +-1
-// - sinh(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/tanhf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/tanh.c
const builtin = @import("builtin");
const std = @import("../std.zig");
@@ -11,6 +11,12 @@ const expect = std.testing.expect;
const expo2 = @import("expo2.zig").expo2;
const maxInt = std.math.maxInt;
+/// Returns the hyperbolic tangent of x.
+///
+/// Special Cases:
+/// - sinh(+-0) = +-0
+/// - sinh(+-inf) = +-1
+/// - sinh(nan) = nan
pub fn tanh(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/math/trunc.zig b/std/math/trunc.zig
index 9449d0e7eb..219bcd4914 100644
--- a/std/math/trunc.zig
+++ b/std/math/trunc.zig
@@ -1,14 +1,20 @@
-// Special Cases:
+// Ported from musl, which is licensed under the MIT license:
+// https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT
//
-// - trunc(+-0) = +-0
-// - trunc(+-inf) = +-inf
-// - trunc(nan) = nan
+// https://git.musl-libc.org/cgit/musl/tree/src/math/truncf.c
+// https://git.musl-libc.org/cgit/musl/tree/src/math/trunc.c
const std = @import("../std.zig");
const math = std.math;
const expect = std.testing.expect;
const maxInt = std.math.maxInt;
+/// Returns the integer value of x.
+///
+/// Special Cases:
+/// - trunc(+-0) = +-0
+/// - trunc(+-inf) = +-inf
+/// - trunc(nan) = nan
pub fn trunc(x: var) @typeOf(x) {
const T = @typeOf(x);
return switch (T) {
diff --git a/std/mem.zig b/std/mem.zig
index 9bf68698c9..2407b56f07 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -34,39 +34,39 @@ pub const Allocator = struct {
/// The returned slice must have its pointer aligned at least to `new_alignment` bytes.
reallocFn: fn (
self: *Allocator,
- // Guaranteed to be the same as what was returned from most recent call to
- // `reallocFn` or `shrinkFn`.
- // If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
- // is guaranteed to be >= 1.
+ /// Guaranteed to be the same as what was returned from most recent call to
+ /// `reallocFn` or `shrinkFn`.
+ /// If `old_mem.len == 0` then this is a new allocation and `new_byte_count`
+ /// is guaranteed to be >= 1.
old_mem: []u8,
- // If `old_mem.len == 0` then this is `undefined`, otherwise:
- // Guaranteed to be the same as what was returned from most recent call to
- // `reallocFn` or `shrinkFn`.
- // Guaranteed to be >= 1.
- // Guaranteed to be a power of 2.
+ /// If `old_mem.len == 0` then this is `undefined`, otherwise:
+ /// Guaranteed to be the same as what was returned from most recent call to
+ /// `reallocFn` or `shrinkFn`.
+ /// Guaranteed to be >= 1.
+ /// Guaranteed to be a power of 2.
old_alignment: u29,
- // If `new_byte_count` is 0 then this is a free and it is guaranteed that
- // `old_mem.len != 0`.
+ /// If `new_byte_count` is 0 then this is a free and it is guaranteed that
+ /// `old_mem.len != 0`.
new_byte_count: usize,
- // Guaranteed to be >= 1.
- // Guaranteed to be a power of 2.
- // Returned slice's pointer must have this alignment.
+ /// Guaranteed to be >= 1.
+ /// Guaranteed to be a power of 2.
+ /// Returned slice's pointer must have this alignment.
new_alignment: u29,
) Error![]u8,
/// This function deallocates memory. It must succeed.
shrinkFn: fn (
self: *Allocator,
- // Guaranteed to be the same as what was returned from most recent call to
- // `reallocFn` or `shrinkFn`.
+ /// Guaranteed to be the same as what was returned from most recent call to
+ /// `reallocFn` or `shrinkFn`.
old_mem: []u8,
- // Guaranteed to be the same as what was returned from most recent call to
- // `reallocFn` or `shrinkFn`.
+ /// Guaranteed to be the same as what was returned from most recent call to
+ /// `reallocFn` or `shrinkFn`.
old_alignment: u29,
- // Guaranteed to be less than or equal to `old_mem.len`.
+ /// Guaranteed to be less than or equal to `old_mem.len`.
new_byte_count: usize,
- // If `new_byte_count == 0` then this is `undefined`, otherwise:
- // Guaranteed to be less than or equal to `old_alignment`.
+ /// If `new_byte_count == 0` then this is `undefined`, otherwise:
+ /// Guaranteed to be less than or equal to `old_alignment`.
new_alignment: u29,
) []u8,
@@ -104,10 +104,7 @@ pub const Allocator = struct {
const byte_count = math.mul(usize, @sizeOf(T), n) catch return Error.OutOfMemory;
const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, alignment);
assert(byte_slice.len == byte_count);
- // This loop gets optimized out in ReleaseFast mode
- for (byte_slice) |*byte| {
- byte.* = undefined;
- }
+ @memset(byte_slice.ptr, undefined, byte_slice.len);
return @bytesToSlice(T, @alignCast(alignment, byte_slice));
}
@@ -153,10 +150,7 @@ pub const Allocator = struct {
const byte_slice = try self.reallocFn(self, old_byte_slice, Slice.alignment, byte_count, new_alignment);
assert(byte_slice.len == byte_count);
if (new_n > old_mem.len) {
- // This loop gets optimized out in ReleaseFast mode
- for (byte_slice[old_byte_slice.len..]) |*byte| {
- byte.* = undefined;
- }
+ @memset(byte_slice.ptr + old_byte_slice.len, undefined, byte_slice.len - old_byte_slice.len);
}
return @bytesToSlice(T, @alignCast(new_alignment, byte_slice));
}
diff --git a/std/os.zig b/std/os.zig
index d641cf29c9..fe97c1aa61 100644
--- a/std/os.zig
+++ b/std/os.zig
@@ -23,6 +23,7 @@ test "std.os" {
_ = @import("os/time.zig");
_ = @import("os/windows.zig");
_ = @import("os/uefi.zig");
+ _ = @import("os/wasi.zig");
_ = @import("os/get_app_data_dir.zig");
}
@@ -33,6 +34,7 @@ pub const freebsd = @import("os/freebsd.zig");
pub const netbsd = @import("os/netbsd.zig");
pub const zen = @import("os/zen.zig");
pub const uefi = @import("os/uefi.zig");
+pub const wasi = @import("os/wasi.zig");
pub const posix = switch (builtin.os) {
Os.linux => linux,
@@ -40,6 +42,7 @@ pub const posix = switch (builtin.os) {
Os.freebsd => freebsd,
Os.netbsd => netbsd,
Os.zen => zen,
+ Os.wasi => wasi,
else => @compileError("Unsupported OS"),
};
@@ -50,7 +53,11 @@ pub const path = @import("os/path.zig");
pub const File = @import("os/file.zig").File;
pub const time = @import("os/time.zig");
-pub const page_size = 4 * 1024;
+pub const page_size = switch (builtin.arch) {
+ .wasm32, .wasm64 => 64 * 1024,
+ else => 4 * 1024,
+};
+
pub const MAX_PATH_BYTES = switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posix.PATH_MAX,
// Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
@@ -139,6 +146,12 @@ pub fn getRandomBytes(buf: []u8) !void {
};
}
},
+ Os.wasi => {
+ const random_get_result = os.wasi.random_get(buf.ptr, buf.len);
+ if (random_get_result != os.wasi.ESUCCESS) {
+ return error.Unknown;
+ }
+ },
Os.zen => {
const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 };
var i: usize = 0;
@@ -198,6 +211,12 @@ pub fn abort() noreturn {
}
windows.ExitProcess(3);
},
+ Os.wasi => {
+ _ = wasi.proc_raise(wasi.SIGABRT);
+ // TODO: Is SIGKILL even necessary?
+ _ = wasi.proc_raise(wasi.SIGKILL);
+ while (true) {}
+ },
Os.uefi => {
// TODO there's gotta be a better thing to do here than loop forever
while (true) {}
@@ -226,6 +245,9 @@ pub fn exit(status: u8) noreturn {
Os.windows => {
windows.ExitProcess(status);
},
+ Os.wasi => {
+ wasi.proc_exit(status);
+ },
else => @compileError("Unsupported OS"),
}
}
@@ -749,6 +771,37 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
try result.setMove(key, value);
}
+ } else if (builtin.os == Os.wasi) {
+ var environ_count: usize = undefined;
+ var environ_buf_size: usize = undefined;
+
+ const environ_sizes_get_ret = std.os.wasi.environ_sizes_get(&environ_count, &environ_buf_size);
+ if (environ_sizes_get_ret != os.wasi.ESUCCESS) {
+ return unexpectedErrorPosix(environ_sizes_get_ret);
+ }
+
+ // TODO: Verify that the documentation is incorrect
+ // https://github.com/WebAssembly/WASI/issues/27
+ var environ = try allocator.alloc(?[*]u8, environ_count + 1);
+ defer allocator.free(environ);
+ var environ_buf = try std.heap.wasm_allocator.alloc(u8, environ_buf_size);
+ defer allocator.free(environ_buf);
+
+ const environ_get_ret = std.os.wasi.environ_get(environ.ptr, environ_buf.ptr);
+ if (environ_get_ret != os.wasi.ESUCCESS) {
+ return unexpectedErrorPosix(environ_get_ret);
+ }
+
+ for (environ) |env| {
+ if (env) |ptr| {
+ const pair = mem.toSlice(u8, ptr);
+ var parts = mem.separate(pair, "=");
+ const key = parts.next().?;
+ const value = parts.next().?;
+ try result.set(key, value);
+ }
+ }
+ return result;
} else {
for (posix_environ_raw) |ptr| {
var line_i: usize = 0;
@@ -1083,13 +1136,14 @@ pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
defer in_file.close();
const mode = try in_file.mode();
+ const in_stream = &in_file.inStream().stream;
var atomic_file = try AtomicFile.init(dest_path, mode);
defer atomic_file.deinit();
var buf: [page_size]u8 = undefined;
while (true) {
- const amt = try in_file.readFull(buf[0..]);
+ const amt = try in_stream.readFull(buf[0..]);
try atomic_file.file.write(buf[0..amt]);
if (amt != buf.len) {
return atomic_file.finish();
@@ -2127,6 +2181,11 @@ pub const ArgIterator = struct {
inner: InnerType,
pub fn init() ArgIterator {
+ if (builtin.os == Os.wasi) {
+ // TODO: Figure out a compatible interface accomodating WASI
+ @compileError("ArgIterator is not yet supported in WASI. Use argsAlloc and argsFree instead.");
+ }
+
return ArgIterator{ .inner = InnerType.init() };
}
@@ -2159,6 +2218,34 @@ pub fn args() ArgIterator {
/// Caller must call argsFree on result.
pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 {
+ if (builtin.os == Os.wasi) {
+ var count: usize = undefined;
+ var buf_size: usize = undefined;
+
+ const args_sizes_get_ret = os.wasi.args_sizes_get(&count, &buf_size);
+ if (args_sizes_get_ret != os.wasi.ESUCCESS) {
+ return unexpectedErrorPosix(args_sizes_get_ret);
+ }
+
+ var argv = try allocator.alloc([*]u8, count);
+ defer allocator.free(argv);
+
+ var argv_buf = try allocator.alloc(u8, buf_size);
+ const args_get_ret = os.wasi.args_get(argv.ptr, argv_buf.ptr);
+ if (args_get_ret != os.wasi.ESUCCESS) {
+ return unexpectedErrorPosix(args_get_ret);
+ }
+
+ var result_slice = try allocator.alloc([]u8, count);
+
+ var i: usize = 0;
+ while (i < count) : (i += 1) {
+ result_slice[i] = mem.toSlice(u8, argv[i]);
+ }
+
+ return result_slice;
+ }
+
// TODO refactor to only make 1 allocation.
var it = args();
var contents = try Buffer.initSize(allocator, 0);
@@ -2196,6 +2283,16 @@ pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 {
}
pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void {
+ if (builtin.os == Os.wasi) {
+ const last_item = args_alloc[args_alloc.len - 1];
+ const last_byte_addr = @ptrToInt(last_item.ptr) + last_item.len + 1; // null terminated
+ const first_item_ptr = args_alloc[0].ptr;
+ const len = last_byte_addr - @ptrToInt(first_item_ptr);
+ allocator.free(first_item_ptr[0..len]);
+
+ return allocator.free(args_alloc);
+ }
+
var total_bytes: usize = 0;
for (args_alloc) |arg| {
total_bytes += @sizeOf([]u8) + arg.len;
@@ -3030,9 +3127,6 @@ pub const SpawnThreadError = error{
Unexpected,
};
-pub var linux_tls_phdr: ?*std.elf.Phdr = null;
-pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is
-
/// caller must call wait on the returned thread
/// fn startFn(@typeOf(context)) T
/// where T is u8, noreturn, void, or !void
@@ -3142,12 +3236,10 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
}
// Finally, the Thread Local Storage, if any.
if (!Thread.use_pthreads) {
- if (linux_tls_phdr) |tls_phdr| {
- l = mem.alignForward(l, tls_phdr.p_align);
+ if (linux.tls.tls_image) |tls_img| {
+ l = mem.alignForward(l, @alignOf(usize));
tls_start_offset = l;
- l += tls_phdr.p_memsz;
- // the fs register address
- l += @sizeOf(usize);
+ l += tls_img.alloc_size;
}
}
break :blk l;
@@ -3188,10 +3280,8 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
posix.CLONE_DETACHED;
var newtls: usize = undefined;
- if (linux_tls_phdr) |tls_phdr| {
- @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz);
- newtls = mmap_addr + mmap_len - @sizeOf(usize);
- @intToPtr(*usize, newtls).* = newtls;
+ if (linux.tls.tls_image) |tls_img| {
+ newtls = linux.tls.copyTLS(mmap_addr + tls_start_offset);
flags |= posix.CLONE_SETTLS;
}
const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index 9dc1ce1173..04122100f4 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -812,7 +812,7 @@ pub fn sigaction(sig: u5, noalias act: *const Sigaction, noalias oact: ?*Sigacti
.sa_mask = act.mask,
};
var coact: c.Sigaction = undefined;
- const result = errnoWrap(c.sigaction(sig, *cact, *coact));
+ const result = errnoWrap(c.sigaction(sig, &cact, &coact));
if (result != 0) {
return result;
}
diff --git a/std/os/file.zig b/std/os/file.zig
index c9a0e2b696..814e5e318c 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -235,7 +235,7 @@ pub const File = struct {
Unexpected,
};
- pub fn seekForward(self: File, amount: isize) SeekError!void {
+ pub fn seekForward(self: File, amount: i64) SeekError!void {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
const result = posix.lseek(self.handle, amount, posix.SEEK_CUR);
@@ -266,7 +266,7 @@ pub const File = struct {
}
}
- pub fn seekTo(self: File, pos: usize) SeekError!void {
+ pub fn seekTo(self: File, pos: u64) SeekError!void {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
const ipos = try math.cast(isize, pos);
@@ -301,13 +301,12 @@ pub const File = struct {
}
pub const GetSeekPosError = error{
- Overflow,
SystemResources,
Unseekable,
Unexpected,
};
- pub fn getPos(self: File) GetSeekPosError!usize {
+ pub fn getPos(self: File) GetSeekPosError!u64 {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
const result = posix.lseek(self.handle, 0, posix.SEEK_CUR);
@@ -324,7 +323,7 @@ pub const File = struct {
else => os.unexpectedErrorPosix(err),
};
}
- return result;
+ return u64(result);
},
Os.windows => {
var pos: windows.LARGE_INTEGER = undefined;
@@ -336,17 +335,16 @@ pub const File = struct {
};
}
- assert(pos >= 0);
- return math.cast(usize, pos);
+ return @intCast(u64, pos);
},
else => @compileError("unsupported OS"),
}
}
- pub fn getEndPos(self: File) GetSeekPosError!usize {
+ pub fn getEndPos(self: File) GetSeekPosError!u64 {
if (is_posix) {
const stat = try os.posixFStat(self.handle);
- return @intCast(usize, stat.size);
+ return @intCast(u64, stat.size);
} else if (is_windows) {
var file_size: windows.LARGE_INTEGER = undefined;
if (windows.GetFileSizeEx(self.handle, &file_size) == 0) {
@@ -355,9 +353,7 @@ pub const File = struct {
else => os.unexpectedErrorWindows(err),
};
}
- if (file_size < 0)
- return error.Overflow;
- return math.cast(usize, @intCast(u64, file_size));
+ return @intCast(u64, file_size);
} else {
@compileError("TODO support getEndPos on this OS");
}
@@ -492,22 +488,22 @@ pub const File = struct {
pub const Stream = io.SeekableStream(SeekError, GetSeekPosError);
- pub fn seekToFn(seekable_stream: *Stream, pos: usize) SeekError!void {
+ pub fn seekToFn(seekable_stream: *Stream, pos: u64) SeekError!void {
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
return self.file.seekTo(pos);
}
- pub fn seekForwardFn(seekable_stream: *Stream, amt: isize) SeekError!void {
+ pub fn seekForwardFn(seekable_stream: *Stream, amt: i64) SeekError!void {
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
return self.file.seekForward(amt);
}
- pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!usize {
+ pub fn getEndPosFn(seekable_stream: *Stream) GetSeekPosError!u64 {
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
return self.file.getEndPos();
}
- pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!usize {
+ pub fn getPosFn(seekable_stream: *Stream) GetSeekPosError!u64 {
const self = @fieldParentPtr(SeekableStream, "stream", seekable_stream);
return self.file.getPos();
}
diff --git a/std/os/linux.zig b/std/os/linux.zig
index e7f822185b..1e121b81c8 100644
--- a/std/os/linux.zig
+++ b/std/os/linux.zig
@@ -2,7 +2,10 @@ const std = @import("../std.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
const maxInt = std.math.maxInt;
+const elf = std.elf;
+pub const tls = @import("linux/tls.zig");
const vdso = @import("linux/vdso.zig");
+const dl = @import("../dynamic_library.zig");
pub use switch (builtin.arch) {
builtin.Arch.x86_64 => @import("linux/x86_64.zig"),
builtin.Arch.i386 => @import("linux/i386.zig"),
@@ -12,6 +15,7 @@ pub use switch (builtin.arch) {
pub use @import("linux/errno.zig");
pub const PATH_MAX = 4096;
+pub const IOV_MAX = 1024;
pub const STDIN_FILENO = 0;
pub const STDOUT_FILENO = 1;
@@ -955,10 +959,13 @@ pub fn waitpid(pid: i32, status: *i32, options: i32) usize {
return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
}
+var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
+
pub fn clock_gettime(clk_id: i32, tp: *timespec) usize {
if (VDSO_CGT_SYM.len != 0) {
- const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered);
- if (@ptrToInt(f) != 0) {
+ const ptr = @atomicLoad(?*const c_void, &vdso_clock_gettime, .Unordered);
+ if (ptr) |fn_ptr| {
+ const f = @ptrCast(@typeOf(clock_gettime), fn_ptr);
const rc = f(clk_id, tp);
switch (rc) {
0, @bitCast(usize, isize(-EINVAL)) => return rc,
@@ -968,13 +975,18 @@ pub fn clock_gettime(clk_id: i32, tp: *timespec) usize {
}
return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp));
}
-var vdso_clock_gettime = init_vdso_clock_gettime;
+
extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize {
- const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM);
- var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr);
- _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic);
- if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS));
- return f(clk, ts);
+ const ptr = @intToPtr(?*const c_void, vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM));
+ // Note that we may not have a VDSO at all, update the stub address anyway
+ // so that clock_gettime will fall back on the good old (and slow) syscall
+ _ = @cmpxchgStrong(?*const c_void, &vdso_clock_gettime, &init_vdso_clock_gettime, ptr, .Monotonic, .Monotonic);
+ // Call into the VDSO if available
+ if (ptr) |fn_ptr| {
+ const f = @ptrCast(@typeOf(clock_gettime), fn_ptr);
+ return f(clk, ts);
+ }
+ return @bitCast(usize, isize(-ENOSYS));
}
pub fn clock_getres(clk_id: i32, tp: *timespec) usize {
@@ -1100,8 +1112,8 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
const NSIG = 65;
const sigset_t = [128 / @sizeOf(usize)]usize;
-const all_mask = []usize{maxInt(usize)};
-const app_mask = []usize{0xfffffffc7fffffff};
+const all_mask = []u32{ 0xffffffff, 0xffffffff };
+const app_mask = []u32{ 0xfffffffc, 0x7fffffff };
const k_sigaction = extern struct {
handler: extern fn (i32) void,
@@ -1193,6 +1205,16 @@ pub const iovec_const = extern struct {
iov_len: usize,
};
+pub const mmsghdr = extern struct {
+ msg_hdr: msghdr,
+ msg_len: u32,
+};
+
+pub const mmsghdr_const = extern struct {
+ msg_hdr: msghdr_const,
+ msg_len: u32,
+};
+
pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize {
return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len));
}
@@ -1213,10 +1235,50 @@ pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noal
return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen));
}
-pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize {
+pub fn sendmsg(fd: i32, msg: *msghdr_const, flags: u32) usize {
return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags);
}
+pub fn sendmmsg(fd: i32, msgvec: [*]mmsghdr_const, vlen: u32, flags: u32) usize {
+ if (@typeInfo(usize).Int.bits > @typeInfo(@typeOf(mmsghdr(undefined).msg_len)).Int.bits) {
+ // workaround kernel brokenness:
+ // if adding up all iov_len overflows a i32 then split into multiple calls
+ // see https://www.openwall.com/lists/musl/2014/06/07/5
+ const kvlen = if (vlen > IOV_MAX) IOV_MAX else vlen; // matches kernel
+ var next_unsent: usize = 0;
+ for (msgvec[0..kvlen]) |*msg, i| {
+ var size: i32 = 0;
+ const msg_iovlen = @intCast(usize, msg.msg_hdr.msg_iovlen); // kernel side this is treated as unsigned
+ for (msg.msg_hdr.msg_iov[0..msg_iovlen]) |iov, j| {
+ if (iov.iov_len > std.math.maxInt(i32) or @addWithOverflow(i32, size, @intCast(i32, iov.iov_len), &size)) {
+ // batch-send all messages up to the current message
+ if (next_unsent < i) {
+ const batch_size = i - next_unsent;
+ const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
+ if (getErrno(r) != 0) return next_unsent;
+ if (r < batch_size) return next_unsent + r;
+ }
+ // send current message as own packet
+ const r = sendmsg(fd, &msg.msg_hdr, flags);
+ if (getErrno(r) != 0) return r;
+ // Linux limits the total bytes sent by sendmsg to INT_MAX, so this cast is safe.
+ msg.msg_len = @intCast(u32, r);
+ next_unsent = i + 1;
+ break;
+ }
+ }
+ }
+ if (next_unsent < kvlen or next_unsent == 0) { // want to make sure at least one syscall occurs (e.g. to trigger MSG_EOR)
+ const batch_size = kvlen - next_unsent;
+ const r = syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(&msgvec[next_unsent]), batch_size, flags);
+ if (getErrno(r) != 0) return r;
+ return next_unsent + r;
+ }
+ return kvlen;
+ }
+ return syscall4(SYS_sendmmsg, @bitCast(usize, isize(fd)), @ptrToInt(msgvec), vlen, flags);
+}
+
pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize {
return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len);
}
@@ -1339,17 +1401,25 @@ pub fn sched_getaffinity(pid: i32, set: []usize) usize {
return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr));
}
-pub const epoll_data = packed union {
+pub const epoll_data = extern union {
ptr: usize,
fd: i32,
@"u32": u32,
@"u64": u64,
};
-pub const epoll_event = packed struct {
- events: u32,
- data: epoll_data,
-};
+// On x86_64 the structure is packed so that it matches the definition of its
+// 32bit counterpart
+pub const epoll_event = if (builtin.arch != .x86_64)
+ extern struct {
+ events: u32,
+ data: epoll_data,
+ }
+else
+ packed struct {
+ events: u32,
+ data: epoll_data,
+ };
pub fn epoll_create() usize {
return epoll_create1(0);
@@ -1534,6 +1604,70 @@ pub const dirent64 = extern struct {
d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173
};
+pub const dl_phdr_info = extern struct {
+ dlpi_addr: usize,
+ dlpi_name: ?[*]const u8,
+ dlpi_phdr: [*]elf.Phdr,
+ dlpi_phnum: u16,
+};
+
+// XXX: This should be weak
+extern const __ehdr_start: elf.Ehdr = undefined;
+
+pub fn dl_iterate_phdr(comptime T: type, callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, data: ?*T) isize {
+ if (builtin.link_libc) {
+ return std.c.dl_iterate_phdr(@ptrCast(std.c.dl_iterate_phdr_callback, callback), @ptrCast(?*c_void, data));
+ }
+
+ const elf_base = @ptrToInt(&__ehdr_start);
+ const n_phdr = __ehdr_start.e_phnum;
+ const phdrs = (@intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff))[0..n_phdr];
+
+ var it = dl.linkmap_iterator(phdrs) catch return 0;
+
+ // The executable has no dynamic link segment, create a single entry for
+ // the whole ELF image
+ if (it.end()) {
+ var info = dl_phdr_info{
+ .dlpi_addr = elf_base,
+ .dlpi_name = c"/proc/self/exe",
+ .dlpi_phdr = @intToPtr([*]elf.Phdr, elf_base + __ehdr_start.e_phoff),
+ .dlpi_phnum = __ehdr_start.e_phnum,
+ };
+
+ return callback(&info, @sizeOf(dl_phdr_info), data);
+ }
+
+ // Last return value from the callback function
+ var last_r: isize = 0;
+ while (it.next()) |entry| {
+ var dlpi_phdr: usize = undefined;
+ var dlpi_phnum: u16 = undefined;
+
+ if (entry.l_addr != 0) {
+ const elf_header = @intToPtr(*elf.Ehdr, entry.l_addr);
+ dlpi_phdr = entry.l_addr + elf_header.e_phoff;
+ dlpi_phnum = elf_header.e_phnum;
+ } else {
+ // This is the running ELF image
+ dlpi_phdr = elf_base + __ehdr_start.e_phoff;
+ dlpi_phnum = __ehdr_start.e_phnum;
+ }
+
+ var info = dl_phdr_info{
+ .dlpi_addr = entry.l_addr,
+ .dlpi_name = entry.l_name,
+ .dlpi_phdr = @intToPtr([*]elf.Phdr, dlpi_phdr),
+ .dlpi_phnum = dlpi_phnum,
+ };
+
+ last_r = callback(&info, @sizeOf(dl_phdr_info), data);
+ if (last_r != 0) break;
+ }
+
+ return last_r;
+}
+
test "import" {
if (builtin.os == builtin.Os.linux) {
_ = @import("linux/test.zig");
diff --git a/std/os/linux/arm64.zig b/std/os/linux/arm64.zig
index ac2c18ee5f..4ae55765fb 100644
--- a/std/os/linux/arm64.zig
+++ b/std/os/linux/arm64.zig
@@ -2,6 +2,7 @@ const std = @import("../../std.zig");
const linux = std.os.linux;
const socklen_t = linux.socklen_t;
const iovec = linux.iovec;
+const iovec_const = linux.iovec_const;
pub const SYS_io_setup = 0;
pub const SYS_io_destroy = 1;
@@ -415,12 +416,24 @@ pub fn syscall6(
pub extern fn clone(func: extern fn (arg: usize) u8, stack: usize, flags: u32, arg: usize, ptid: *i32, tls: usize, ctid: *i32) usize;
pub const msghdr = extern struct {
- msg_name: *u8,
+ msg_name: ?*sockaddr,
msg_namelen: socklen_t,
- msg_iov: *iovec,
+ msg_iov: [*]iovec,
msg_iovlen: i32,
__pad1: i32,
- msg_control: *u8,
+ msg_control: ?*c_void,
+ msg_controllen: socklen_t,
+ __pad2: socklen_t,
+ msg_flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+ msg_name: ?*const sockaddr,
+ msg_namelen: socklen_t,
+ msg_iov: [*]iovec_const,
+ msg_iovlen: i32,
+ __pad1: i32,
+ msg_control: ?*c_void,
msg_controllen: socklen_t,
__pad2: socklen_t,
msg_flags: i32,
diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig
index 286748b70c..1949d00298 100644
--- a/std/os/linux/test.zig
+++ b/std/os/linux/test.zig
@@ -1,6 +1,8 @@
const std = @import("../../std.zig");
const builtin = @import("builtin");
const linux = std.os.linux;
+const mem = std.mem;
+const elf = std.elf;
const expect = std.testing.expect;
test "getpid" {
@@ -42,3 +44,42 @@ test "timer" {
// TODO implicit cast from *[N]T to [*]T
err = linux.epoll_wait(@intCast(i32, epoll_fd), @ptrCast([*]linux.epoll_event, &events), 8, -1);
}
+
+export fn iter_fn(info: *linux.dl_phdr_info, size: usize, data: ?*usize) i32 {
+ var counter = data.?;
+ // Count how many libraries are loaded
+ counter.* += usize(1);
+
+ // The image should contain at least a PT_LOAD segment
+ if (info.dlpi_phnum < 1) return -1;
+
+ // Quick & dirty validation of the phdr pointers, make sure we're not
+ // pointing to some random gibberish
+ var i: usize = 0;
+ var found_load = false;
+ while (i < info.dlpi_phnum) : (i += 1) {
+ const phdr = info.dlpi_phdr[i];
+
+ if (phdr.p_type != elf.PT_LOAD) continue;
+
+ // Find the ELF header
+ const elf_header = @intToPtr(*elf.Ehdr, phdr.p_vaddr - phdr.p_offset);
+ // Validate the magic
+ if (!mem.eql(u8, elf_header.e_ident[0..], "\x7fELF")) return -1;
+ // Consistency check
+ if (elf_header.e_phnum != info.dlpi_phnum) return -1;
+
+ found_load = true;
+ break;
+ }
+
+ if (!found_load) return -1;
+
+ return 42;
+}
+
+test "dl_iterate_phdr" {
+ var counter: usize = 0;
+ expect(linux.dl_iterate_phdr(usize, iter_fn, &counter) != 0);
+ expect(counter != 0);
+}
diff --git a/std/os/linux/tls.zig b/std/os/linux/tls.zig
new file mode 100644
index 0000000000..9c8d816f05
--- /dev/null
+++ b/std/os/linux/tls.zig
@@ -0,0 +1,247 @@
+const std = @import("std");
+const mem = std.mem;
+const posix = std.os.posix;
+const elf = std.elf;
+const builtin = @import("builtin");
+const assert = std.debug.assert;
+
+// This file implements the two TLS variants [1] used by ELF-based systems.
+//
+// The variant I has the following layout in memory:
+// -------------------------------------------------------
+// | DTV | Zig | DTV | Alignment | TLS |
+// | storage | thread data | pointer | | block |
+// ------------------------^------------------------------
+// `-- The thread pointer register points here
+//
+// In this case we allocate additional space for our control structure that's
+// placed _before_ the DTV pointer together with the DTV.
+//
+// NOTE: Some systems such as power64 or mips use this variant with a twist: the
+// alignment is not present and the tp and DTV addresses are offset by a
+// constant.
+//
+// On the other hand the variant II has the following layout in memory:
+// ---------------------------------------
+// | TLS | TCB | Zig | DTV |
+// | block | | thread data | storage |
+// --------^------------------------------
+// `-- The thread pointer register points here
+//
+// The structure of the TCB is not defined by the ABI so we reserve enough space
+// for a single pointer as some architectures such as i386 and x86_64 need a
+// pointer to the TCB block itself at the address pointed by the tp.
+//
+// In this case the control structure and DTV are placed one after another right
+// after the TLS block data.
+//
+// At the moment the DTV is very simple since we only support static TLS, all we
+// need is a two word vector to hold the number of entries (1) and the address
+// of the first TLS block.
+//
+// [1] https://www.akkadia.org/drepper/tls.pdf
+
+const TLSVariant = enum {
+ VariantI,
+ VariantII,
+};
+
+const tls_variant = switch (builtin.arch) {
+ .arm, .armeb, .aarch64, .aarch64_be => TLSVariant.VariantI,
+ .x86_64, .i386 => TLSVariant.VariantII,
+ else => @compileError("undefined tls_variant for this architecture"),
+};
+
+// Controls how many bytes are reserved for the Thread Control Block
+const tls_tcb_size = switch (builtin.arch) {
+ // ARM EABI mandates enough space for two pointers: the first one points to
+ // the DTV while the second one is unspecified but reserved
+ .arm, .armeb, .aarch64, .aarch64_be => 2 * @sizeOf(usize),
+ .i386, .x86_64 => @sizeOf(usize),
+ else => 0,
+};
+
+// Controls if the TCB should be aligned according to the TLS segment p_align
+const tls_tcb_align_size = switch (builtin.arch) {
+ .arm, .armeb, .aarch64, .aarch64_be => true,
+ else => false,
+};
+
+// Check if the architecture-specific parameters look correct
+comptime {
+ if (tls_tcb_align_size and tls_variant != TLSVariant.VariantI) {
+ @compileError("tls_tcb_align_size is only meaningful for variant I TLS");
+ }
+}
+
+// Some architectures add some offset to the tp and dtv addresses in order to
+// make the generated code more efficient
+
+const tls_tp_offset = switch (builtin.arch) {
+ else => 0,
+};
+
+const tls_dtv_offset = switch (builtin.arch) {
+ else => 0,
+};
+
+// Per-thread storage for Zig's use
+const CustomData = packed struct {
+};
+
+// Dynamic Thread Vector
+const DTV = packed struct {
+ entries: usize,
+ tls_block: [1]usize,
+};
+
+// Holds all the information about the process TLS image
+const TLSImage = struct {
+ data_src: []u8,
+ alloc_size: usize,
+ tcb_offset: usize,
+ dtv_offset: usize,
+ data_offset: usize,
+};
+
+pub var tls_image: ?TLSImage = null;
+
+pub fn setThreadPointer(addr: usize) void {
+ switch (builtin.arch) {
+ .x86_64 => {
+ const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl,
+ std.os.linux.ARCH_SET_FS, addr);
+ assert(rc == 0);
+ },
+ .aarch64 => {
+ asm volatile (
+ \\ msr tpidr_el0, %[addr]
+ : : [addr] "r" (addr)
+ );
+ },
+ else => @compileError("Unsupported architecture"),
+ }
+}
+
+pub fn initTLS() void {
+ var tls_phdr: ?*elf.Phdr = null;
+ var img_base: usize = 0;
+
+ const auxv = std.os.linux_elf_aux_maybe.?;
+ var at_phent: usize = undefined;
+ var at_phnum: usize = undefined;
+ var at_phdr: usize = undefined;
+
+ var i: usize = 0;
+ while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) {
+ switch (auxv[i].a_type) {
+ elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
+ elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
+ elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
+ else => continue,
+ }
+ }
+
+ // Sanity check
+ assert(at_phent == @sizeOf(elf.Phdr));
+
+ // Search the TLS section
+ const phdrs = (@intToPtr([*]elf.Phdr, at_phdr))[0..at_phnum];
+
+ for (phdrs) |*phdr| {
+ switch (phdr.p_type) {
+ elf.PT_PHDR => img_base = at_phdr - phdr.p_vaddr,
+ elf.PT_TLS => tls_phdr = phdr,
+ else => continue,
+ }
+ }
+
+ if (tls_phdr) |phdr| {
+ // Offsets into the allocated TLS area
+ var tcb_offset: usize = undefined;
+ var dtv_offset: usize = undefined;
+ var data_offset: usize = undefined;
+ var thread_data_offset: usize = undefined;
+ // Compute the total size of the ABI-specific data plus our own control
+ // structures
+ const alloc_size = switch (tls_variant) {
+ .VariantI => blk: {
+ var l: usize = 0;
+ dtv_offset = l;
+ l += @sizeOf(DTV);
+ thread_data_offset = l;
+ l += @sizeOf(CustomData);
+ l = mem.alignForward(l, phdr.p_align);
+ tcb_offset = l;
+ if (tls_tcb_align_size) {
+ l += mem.alignForward(tls_tcb_size, phdr.p_align);
+ } else {
+ l += tls_tcb_size;
+ }
+ data_offset = l;
+ l += phdr.p_memsz;
+ break :blk l;
+ },
+ .VariantII => blk: {
+ var l: usize = 0;
+ data_offset = l;
+ l += phdr.p_memsz;
+ l = mem.alignForward(l, phdr.p_align);
+ tcb_offset = l;
+ l += tls_tcb_size;
+ thread_data_offset = l;
+ l += @sizeOf(CustomData);
+ dtv_offset = l;
+ l += @sizeOf(DTV);
+ break :blk l;
+ }
+ };
+
+ tls_image = TLSImage{
+ .data_src = @intToPtr([*]u8, phdr.p_vaddr + img_base)[0..phdr.p_filesz],
+ .alloc_size = alloc_size,
+ .tcb_offset = tcb_offset,
+ .dtv_offset = dtv_offset,
+ .data_offset = data_offset,
+ };
+ }
+}
+
+pub fn copyTLS(addr: usize) usize {
+ const tls_img = tls_image.?;
+
+ // Be paranoid, clear the area we're going to use
+ @memset(@intToPtr([*]u8, addr), 0, tls_img.alloc_size);
+ // Prepare the DTV
+ const dtv = @intToPtr(*DTV, addr + tls_img.dtv_offset);
+ dtv.entries = 1;
+ dtv.tls_block[0] = addr + tls_img.data_offset + tls_dtv_offset;
+ // Set-up the TCB
+ const tcb_ptr = @intToPtr(*usize, addr + tls_img.tcb_offset);
+ if (tls_variant == TLSVariant.VariantI) {
+ tcb_ptr.* = addr + tls_img.dtv_offset;
+ } else {
+ tcb_ptr.* = addr + tls_img.tcb_offset;
+ }
+ // Copy the data
+ @memcpy(@intToPtr([*]u8, addr + tls_img.data_offset), tls_img.data_src.ptr, tls_img.data_src.len);
+
+ // Return the corrected (if needed) value for the tp register
+ return addr + tls_img.tcb_offset + tls_tp_offset;
+}
+
+var main_thread_tls_buffer: [256]u8 align(32) = undefined;
+
+pub fn allocateTLS(size: usize) usize {
+ // Small TLS allocation, use our local buffer
+ if (size < main_thread_tls_buffer.len) {
+ return @ptrToInt(&main_thread_tls_buffer);
+ }
+
+ const addr = posix.mmap(null, size, posix.PROT_READ | posix.PROT_WRITE,
+ posix.MAP_PRIVATE | posix.MAP_ANONYMOUS, -1, 0);
+
+ if (posix.getErrno(addr) != 0) @panic("out of memory");
+
+ return addr;
+}
diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig
index d194cd4003..621783d572 100644
--- a/std/os/linux/x86_64.zig
+++ b/std/os/linux/x86_64.zig
@@ -1,7 +1,9 @@
const std = @import("../../std.zig");
const linux = std.os.linux;
+const sockaddr = linux.sockaddr;
const socklen_t = linux.socklen_t;
const iovec = linux.iovec;
+const iovec_const = linux.iovec_const;
pub const SYS_read = 0;
pub const SYS_write = 1;
@@ -386,6 +388,11 @@ pub const VDSO_CGT_VER = "LINUX_2.6";
pub const VDSO_GETCPU_SYM = "__vdso_getcpu";
pub const VDSO_GETCPU_VER = "LINUX_2.6";
+pub const ARCH_SET_GS = 0x1001;
+pub const ARCH_SET_FS = 0x1002;
+pub const ARCH_GET_FS = 0x1003;
+pub const ARCH_GET_GS = 0x1004;
+
pub fn syscall0(number: usize) usize {
return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
@@ -483,12 +490,24 @@ pub nakedcc fn restore_rt() void {
}
pub const msghdr = extern struct {
- msg_name: *u8,
+ msg_name: ?*sockaddr,
+ msg_namelen: socklen_t,
+ msg_iov: [*]iovec,
+ msg_iovlen: i32,
+ __pad1: i32,
+ msg_control: ?*c_void,
+ msg_controllen: socklen_t,
+ __pad2: socklen_t,
+ msg_flags: i32,
+};
+
+pub const msghdr_const = extern struct {
+ msg_name: ?*const sockaddr,
msg_namelen: socklen_t,
- msg_iov: *iovec,
+ msg_iov: [*]iovec_const,
msg_iovlen: i32,
__pad1: i32,
- msg_control: *u8,
+ msg_control: ?*c_void,
msg_controllen: socklen_t,
__pad2: socklen_t,
msg_flags: i32,
diff --git a/std/os/time.zig b/std/os/time.zig
index 66ceedb1b6..abb6412843 100644
--- a/std/os/time.zig
+++ b/std/os/time.zig
@@ -3,41 +3,44 @@ const builtin = @import("builtin");
const Os = builtin.Os;
const debug = std.debug;
const testing = std.testing;
+const math = std.math;
const windows = std.os.windows;
const linux = std.os.linux;
const darwin = std.os.darwin;
+const wasi = std.os.wasi;
const posix = std.os.posix;
pub const epoch = @import("epoch.zig");
-/// Sleep for the specified duration
+/// Spurious wakeups are possible and no precision of timing is guaranteed.
pub fn sleep(nanoseconds: u64) void {
switch (builtin.os) {
Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => {
const s = nanoseconds / ns_per_s;
const ns = nanoseconds % ns_per_s;
- posixSleep(@intCast(u63, s), @intCast(u63, ns));
+ posixSleep(s, ns);
},
Os.windows => {
const ns_per_ms = ns_per_s / ms_per_s;
const milliseconds = nanoseconds / ns_per_ms;
- windows.Sleep(@intCast(windows.DWORD, milliseconds));
+ const ms_that_will_fit = std.math.cast(windows.DWORD, milliseconds) catch std.math.maxInt(windows.DWORD);
+ windows.Sleep(ms_that_will_fit);
},
else => @compileError("Unsupported OS"),
}
}
-pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
+/// Spurious wakeups are possible and no precision of timing is guaranteed.
+pub fn posixSleep(seconds: u64, nanoseconds: u64) void {
var req = posix.timespec{
- .tv_sec = seconds,
- .tv_nsec = nanoseconds,
+ .tv_sec = std.math.cast(isize, seconds) catch std.math.maxInt(isize),
+ .tv_nsec = std.math.cast(isize, nanoseconds) catch std.math.maxInt(isize),
};
var rem: posix.timespec = undefined;
while (true) {
const ret_val = posix.nanosleep(&req, &rem);
const err = posix.getErrno(ret_val);
- if (err == 0) return;
switch (err) {
posix.EFAULT => unreachable,
posix.EINVAL => {
@@ -49,6 +52,7 @@ pub fn posixSleep(seconds: u63, nanoseconds: u63) void {
req = rem;
continue;
},
+ // This prong handles success as well as unexpected errors.
else => return,
}
}
@@ -64,9 +68,21 @@ pub const milliTimestamp = switch (builtin.os) {
Os.windows => milliTimestampWindows,
Os.linux, Os.freebsd, Os.netbsd => milliTimestampPosix,
Os.macosx, Os.ios => milliTimestampDarwin,
+ Os.wasi => milliTimestampWasi,
else => @compileError("Unsupported OS"),
};
+fn milliTimestampWasi() u64 {
+ var ns: wasi.timestamp_t = undefined;
+
+ // TODO: Verify that precision is ignored
+ const err = wasi.clock_time_get(wasi.CLOCK_REALTIME, 1, &ns);
+ debug.assert(err == wasi.ESUCCESS);
+
+ const ns_per_ms = 1000;
+ return @divFloor(ns, ns_per_ms);
+}
+
fn milliTimestampWindows() u64 {
//FileTime has a granularity of 100 nanoseconds
// and uses the NTFS/Windows epoch
diff --git a/std/os/wasi.zig b/std/os/wasi.zig
new file mode 100644
index 0000000000..2118db9a2a
--- /dev/null
+++ b/std/os/wasi.zig
@@ -0,0 +1,42 @@
+pub use @import("wasi/core.zig");
+
+pub const STDIN_FILENO = 0;
+pub const STDOUT_FILENO = 1;
+pub const STDERR_FILENO = 2;
+
+pub fn getErrno(r: usize) usize {
+ const signed_r = @bitCast(isize, r);
+ return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0;
+}
+
+pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
+ var nwritten: usize = undefined;
+
+ const ciovs = ciovec_t{
+ .buf = buf,
+ .buf_len = count,
+ };
+
+ const err = fd_write(@bitCast(fd_t, isize(fd)), &ciovs, 1, &nwritten);
+ if (err == ESUCCESS) {
+ return nwritten;
+ } else {
+ return @bitCast(usize, -isize(err));
+ }
+}
+
+pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
+ var nread: usize = undefined;
+
+ const iovs = iovec_t{
+ .buf = buf,
+ .buf_len = nbyte,
+ };
+
+ const err = fd_read(@bitCast(fd_t, isize(fd)), &iovs, 1, &nread);
+ if (err == ESUCCESS) {
+ return nread;
+ } else {
+ return @bitCast(usize, -isize(err));
+ }
+}
diff --git a/std/os/wasi/core.zig b/std/os/wasi/core.zig
new file mode 100644
index 0000000000..f2bef73be9
--- /dev/null
+++ b/std/os/wasi/core.zig
@@ -0,0 +1,374 @@
+// Based on https://github.com/CraneStation/wasi-sysroot/blob/wasi/libc-bottom-half/headers/public/wasi/core.h
+// and https://github.com/WebAssembly/WASI/blob/master/design/WASI-core.md
+
+pub const advice_t = u8;
+pub const ADVICE_NORMAL: advice_t = 0;
+pub const ADVICE_SEQUENTIAL: advice_t = 1;
+pub const ADVICE_RANDOM: advice_t = 2;
+pub const ADVICE_WILLNEED: advice_t = 3;
+pub const ADVICE_DONTNEED: advice_t = 4;
+pub const ADVICE_NOREUSE: advice_t = 5;
+
+pub const ciovec_t = extern struct {
+ buf: [*]const u8,
+ buf_len: usize,
+};
+
+pub const clockid_t = u32;
+pub const CLOCK_REALTIME: clockid_t = 0;
+pub const CLOCK_MONOTONIC: clockid_t = 1;
+pub const CLOCK_PROCESS_CPUTIME_ID: clockid_t = 2;
+pub const CLOCK_THREAD_CPUTIME_ID: clockid_t = 3;
+
+pub const device_t = u64;
+
+pub const dircookie_t = u64;
+pub const DIRCOOKIE_START: dircookie_t = 0;
+
+pub const dirent_t = extern struct {
+ d_next: dircookie_t,
+ d_ino: inode_t,
+ d_namlen: u32,
+ d_type: filetype_t,
+};
+
+pub const errno_t = u16;
+pub const ESUCCESS: errno_t = 0;
+pub const E2BIG: errno_t = 1;
+pub const EACCES: errno_t = 2;
+pub const EADDRINUSE: errno_t = 3;
+pub const EADDRNOTAVAIL: errno_t = 4;
+pub const EAFNOSUPPORT: errno_t = 5;
+pub const EAGAIN: errno_t = 6;
+pub const EALREADY: errno_t = 7;
+pub const EBADF: errno_t = 8;
+pub const EBADMSG: errno_t = 9;
+pub const EBUSY: errno_t = 10;
+pub const ECANCELED: errno_t = 11;
+pub const ECHILD: errno_t = 12;
+pub const ECONNABORTED: errno_t = 13;
+pub const ECONNREFUSED: errno_t = 14;
+pub const ECONNRESET: errno_t = 15;
+pub const EDEADLK: errno_t = 16;
+pub const EDESTADDRREQ: errno_t = 17;
+pub const EDOM: errno_t = 18;
+pub const EDQUOT: errno_t = 19;
+pub const EEXIST: errno_t = 20;
+pub const EFAULT: errno_t = 21;
+pub const EFBIG: errno_t = 22;
+pub const EHOSTUNREACH: errno_t = 23;
+pub const EIDRM: errno_t = 24;
+pub const EILSEQ: errno_t = 25;
+pub const EINPROGRESS: errno_t = 26;
+pub const EINTR: errno_t = 27;
+pub const EINVAL: errno_t = 28;
+pub const EIO: errno_t = 29;
+pub const EISCONN: errno_t = 30;
+pub const EISDIR: errno_t = 31;
+pub const ELOOP: errno_t = 32;
+pub const EMFILE: errno_t = 33;
+pub const EMLINK: errno_t = 34;
+pub const EMSGSIZE: errno_t = 35;
+pub const EMULTIHOP: errno_t = 36;
+pub const ENAMETOOLONG: errno_t = 37;
+pub const ENETDOWN: errno_t = 38;
+pub const ENETRESET: errno_t = 39;
+pub const ENETUNREACH: errno_t = 40;
+pub const ENFILE: errno_t = 41;
+pub const ENOBUFS: errno_t = 42;
+pub const ENODEV: errno_t = 43;
+pub const ENOENT: errno_t = 44;
+pub const ENOEXEC: errno_t = 45;
+pub const ENOLCK: errno_t = 46;
+pub const ENOLINK: errno_t = 47;
+pub const ENOMEM: errno_t = 48;
+pub const ENOMSG: errno_t = 49;
+pub const ENOPROTOOPT: errno_t = 50;
+pub const ENOSPC: errno_t = 51;
+pub const ENOSYS: errno_t = 52;
+pub const ENOTCONN: errno_t = 53;
+pub const ENOTDIR: errno_t = 54;
+pub const ENOTEMPTY: errno_t = 55;
+pub const ENOTRECOVERABLE: errno_t = 56;
+pub const ENOTSOCK: errno_t = 57;
+pub const ENOTSUP: errno_t = 58;
+pub const ENOTTY: errno_t = 59;
+pub const ENXIO: errno_t = 60;
+pub const EOVERFLOW: errno_t = 61;
+pub const EOWNERDEAD: errno_t = 62;
+pub const EPERM: errno_t = 63;
+pub const EPIPE: errno_t = 64;
+pub const EPROTO: errno_t = 65;
+pub const EPROTONOSUPPORT: errno_t = 66;
+pub const EPROTOTYPE: errno_t = 67;
+pub const ERANGE: errno_t = 68;
+pub const EROFS: errno_t = 69;
+pub const ESPIPE: errno_t = 70;
+pub const ESRCH: errno_t = 71;
+pub const ESTALE: errno_t = 72;
+pub const ETIMEDOUT: errno_t = 73;
+pub const ETXTBSY: errno_t = 74;
+pub const EXDEV: errno_t = 75;
+pub const ENOTCAPABLE: errno_t = 76;
+
+pub const event_t = extern struct {
+ userdata: userdata_t,
+ @"error": errno_t,
+ @"type": eventtype_t,
+ u: extern union {
+ fd_readwrite: extern struct {
+ nbytes: filesize_t,
+ flags: eventrwflags_t,
+ },
+ },
+};
+
+pub const eventrwflags_t = u16;
+pub const EVENT_FD_READWRITE_HANGUP: eventrwflags_t = 0x0001;
+
+pub const eventtype_t = u8;
+pub const EVENTTYPE_CLOCK: eventtype_t = 0;
+pub const EVENTTYPE_FD_READ: eventtype_t = 1;
+pub const EVENTTYPE_FD_WRITE: eventtype_t = 2;
+
+pub const exitcode_t = u32;
+
+pub const fd_t = u32;
+
+pub const fdflags_t = u16;
+pub const FDFLAG_APPEND: fdflags_t = 0x0001;
+pub const FDFLAG_DSYNC: fdflags_t = 0x0002;
+pub const FDFLAG_NONBLOCK: fdflags_t = 0x0004;
+pub const FDFLAG_RSYNC: fdflags_t = 0x0008;
+pub const FDFLAG_SYNC: fdflags_t = 0x0010;
+
+const fdstat_t = extern struct {
+ fs_filetype: filetype_t,
+ fs_flags: fdflags_t,
+ fs_rights_base: rights_t,
+ fs_rights_inheriting: rights_t,
+};
+
+pub const filedelta_t = i64;
+
+pub const filesize_t = u64;
+
+pub const filestat_t = extern struct {
+ st_dev: device_t,
+ st_ino: inode_t,
+ st_filetype: filetype_t,
+ st_nlink: linkcount_t,
+ st_size: filesize_t,
+ st_atim: timestamp_t,
+ st_mtim: timestamp_t,
+ st_ctim: timestamp_t,
+};
+
+pub const filetype_t = u8;
+pub const FILETYPE_UNKNOWN: filetype_t = 0;
+pub const FILETYPE_BLOCK_DEVICE: filetype_t = 1;
+pub const FILETYPE_CHARACTER_DEVICE: filetype_t = 2;
+pub const FILETYPE_DIRECTORY: filetype_t = 3;
+pub const FILETYPE_REGULAR_FILE: filetype_t = 4;
+pub const FILETYPE_SOCKET_DGRAM: filetype_t = 5;
+pub const FILETYPE_SOCKET_STREAM: filetype_t = 6;
+pub const FILETYPE_SYMBOLIC_LINK: filetype_t = 7;
+
+pub const fstflags_t = u16;
+pub const FILESTAT_SET_ATIM: fstflags_t = 0x0001;
+pub const FILESTAT_SET_ATIM_NOW: fstflags_t = 0x0002;
+pub const FILESTAT_SET_MTIM: fstflags_t = 0x0004;
+pub const FILESTAT_SET_MTIM_NOW: fstflags_t = 0x0008;
+
+pub const inode_t = u64;
+
+pub const iovec_t = extern struct {
+ buf: [*]u8,
+ buf_len: usize,
+};
+
+pub const linkcount_t = u32;
+
+pub const lookupflags_t = u32;
+pub const LOOKUP_SYMLINK_FOLLOW: lookupflags_t = 0x00000001;
+
+pub const oflags_t = u16;
+pub const O_CREAT: oflags_t = 0x0001;
+pub const O_DIRECTORY: oflags_t = 0x0002;
+pub const O_EXCL: oflags_t = 0x0004;
+pub const O_TRUNC: oflags_t = 0x0008;
+
+pub const preopentype_t = u8;
+pub const PREOPENTYPE_DIR: preopentype_t = 0;
+
+pub const prestat_t = extern struct {
+ pr_type: preopentype_t,
+ u: extern union {
+ dir: extern struct {
+ pr_name_len: usize,
+ },
+ },
+};
+
+pub const riflags_t = u16;
+pub const SOCK_RECV_PEEK: riflags_t = 0x0001;
+pub const SOCK_RECV_WAITALL: riflags_t = 0x0002;
+
+pub const rights_t = u64;
+pub const RIGHT_FD_DATASYNC: rights_t = 0x0000000000000001;
+pub const RIGHT_FD_READ: rights_t = 0x0000000000000002;
+pub const RIGHT_FD_SEEK: rights_t = 0x0000000000000004;
+pub const RIGHT_FD_FDSTAT_SET_FLAGS: rights_t = 0x0000000000000008;
+pub const RIGHT_FD_SYNC: rights_t = 0x0000000000000010;
+pub const RIGHT_FD_TELL: rights_t = 0x0000000000000020;
+pub const RIGHT_FD_WRITE: rights_t = 0x0000000000000040;
+pub const RIGHT_FD_ADVISE: rights_t = 0x0000000000000080;
+pub const RIGHT_FD_ALLOCATE: rights_t = 0x0000000000000100;
+pub const RIGHT_PATH_CREATE_DIRECTORY: rights_t = 0x0000000000000200;
+pub const RIGHT_PATH_CREATE_FILE: rights_t = 0x0000000000000400;
+pub const RIGHT_PATH_LINK_SOURCE: rights_t = 0x0000000000000800;
+pub const RIGHT_PATH_LINK_TARGET: rights_t = 0x0000000000001000;
+pub const RIGHT_PATH_OPEN: rights_t = 0x0000000000002000;
+pub const RIGHT_FD_READDIR: rights_t = 0x0000000000004000;
+pub const RIGHT_PATH_READLINK: rights_t = 0x0000000000008000;
+pub const RIGHT_PATH_RENAME_SOURCE: rights_t = 0x0000000000010000;
+pub const RIGHT_PATH_RENAME_TARGET: rights_t = 0x0000000000020000;
+pub const RIGHT_PATH_FILESTAT_GET: rights_t = 0x0000000000040000;
+pub const RIGHT_PATH_FILESTAT_SET_SIZE: rights_t = 0x0000000000080000;
+pub const RIGHT_PATH_FILESTAT_SET_TIMES: rights_t = 0x0000000000100000;
+pub const RIGHT_FD_FILESTAT_GET: rights_t = 0x0000000000200000;
+pub const RIGHT_FD_FILESTAT_SET_SIZE: rights_t = 0x0000000000400000;
+pub const RIGHT_FD_FILESTAT_SET_TIMES: rights_t = 0x0000000000800000;
+pub const RIGHT_PATH_SYMLINK: rights_t = 0x0000000001000000;
+pub const RIGHT_PATH_REMOVE_DIRECTORY: rights_t = 0x0000000002000000;
+pub const RIGHT_PATH_UNLINK_FILE: rights_t = 0x0000000004000000;
+pub const RIGHT_POLL_FD_READWRITE: rights_t = 0x0000000008000000;
+pub const RIGHT_SOCK_SHUTDOWN: rights_t = 0x0000000010000000;
+
+pub const roflags_t = u16;
+pub const SOCK_RECV_DATA_TRUNCATED: roflags_t = 0x0001;
+
+pub const sdflags_t = u8;
+pub const SHUT_RD: sdflags_t = 0x01;
+pub const SHUT_WR: sdflags_t = 0x02;
+
+pub const siflags_t = u16;
+
+pub const signal_t = u8;
+pub const SIGHUP: signal_t = 1;
+pub const SIGINT: signal_t = 2;
+pub const SIGQUIT: signal_t = 3;
+pub const SIGILL: signal_t = 4;
+pub const SIGTRAP: signal_t = 5;
+pub const SIGABRT: signal_t = 6;
+pub const SIGBUS: signal_t = 7;
+pub const SIGFPE: signal_t = 8;
+pub const SIGKILL: signal_t = 9;
+pub const SIGUSR1: signal_t = 10;
+pub const SIGSEGV: signal_t = 11;
+pub const SIGUSR2: signal_t = 12;
+pub const SIGPIPE: signal_t = 13;
+pub const SIGALRM: signal_t = 14;
+pub const SIGTERM: signal_t = 15;
+pub const SIGCHLD: signal_t = 16;
+pub const SIGCONT: signal_t = 17;
+pub const SIGSTOP: signal_t = 18;
+pub const SIGTSTP: signal_t = 19;
+pub const SIGTTIN: signal_t = 20;
+pub const SIGTTOU: signal_t = 21;
+pub const SIGURG: signal_t = 22;
+pub const SIGXCPU: signal_t = 23;
+pub const SIGXFSZ: signal_t = 24;
+pub const SIGVTALRM: signal_t = 25;
+pub const SIGPROF: signal_t = 26;
+pub const SIGWINCH: signal_t = 27;
+pub const SIGPOLL: signal_t = 28;
+pub const SIGPWR: signal_t = 29;
+pub const SIGSYS: signal_t = 30;
+
+pub const subclockflags_t = u16;
+pub const SUBSCRIPTION_CLOCK_ABSTIME: subclockflags_t = 0x0001;
+
+pub const subscription_t = extern struct {
+ userdata: userdata_t,
+ @"type": eventtype_t,
+ u: extern union {
+ clock: extern struct {
+ identifier: userdata_t,
+ clock_id: clockid_t,
+ timeout: timestamp_t,
+ precision: timestamp_t,
+ flags: subclockflags_t,
+ },
+ fd_readwrite: extern struct {
+ fd: fd_t,
+ },
+ },
+};
+
+pub const timestamp_t = u64;
+
+pub const userdata_t = u64;
+
+pub const whence_t = u8;
+pub const WHENCE_CUR: whence_t = 0;
+pub const WHENCE_END: whence_t = 1;
+pub const WHENCE_SET: whence_t = 2;
+
+pub extern "wasi_unstable" fn args_get(argv: [*][*]u8, argv_buf: [*]u8) errno_t;
+pub extern "wasi_unstable" fn args_sizes_get(argc: *usize, argv_buf_size: *usize) errno_t;
+
+pub extern "wasi_unstable" fn clock_res_get(clock_id: clockid_t, resolution: *timestamp_t) errno_t;
+pub extern "wasi_unstable" fn clock_time_get(clock_id: clockid_t, precision: timestamp_t, timestamp: *timestamp_t) errno_t;
+
+pub extern "wasi_unstable" fn environ_get(environ: [*]?[*]u8, environ_buf: [*]u8) errno_t;
+pub extern "wasi_unstable" fn environ_sizes_get(environ_count: *usize, environ_buf_size: *usize) errno_t;
+
+pub extern "wasi_unstable" fn fd_advise(fd: fd_t, offset: filesize_t, len: filesize_t, advice: advice_t) errno_t;
+pub extern "wasi_unstable" fn fd_allocate(fd: fd_t, offset: filesize_t, len: filesize_t) errno_t;
+pub extern "wasi_unstable" fn fd_close(fd: fd_t) errno_t;
+pub extern "wasi_unstable" fn fd_datasync(fd: fd_t) errno_t;
+pub extern "wasi_unstable" fn fd_pread(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, offset: filesize_t, nread: *usize) errno_t;
+pub extern "wasi_unstable" fn fd_pwrite(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, offset: filesize_t, nwritten: *usize) errno_t;
+pub extern "wasi_unstable" fn fd_read(fd: fd_t, iovs: *const iovec_t, iovs_len: usize, nread: *usize) errno_t;
+pub extern "wasi_unstable" fn fd_readdir(fd: fd_t, buf: [*]u8, buf_len: usize, cookie: dircookie_t, bufused: *usize) errno_t;
+pub extern "wasi_unstable" fn fd_renumber(from: fd_t, to: fd_t) errno_t;
+pub extern "wasi_unstable" fn fd_seek(fd: fd_t, offset: filedelta_t, whence: whence_t, newoffset: *filesize_t) errno_t;
+pub extern "wasi_unstable" fn fd_sync(fd: fd_t) errno_t;
+pub extern "wasi_unstable" fn fd_tell(fd: fd_t, newoffset: *filesize_t) errno_t;
+pub extern "wasi_unstable" fn fd_write(fd: fd_t, iovs: *const ciovec_t, iovs_len: usize, nwritten: *usize) errno_t;
+
+pub extern "wasi_unstable" fn fd_fdstat_get(fd: fd_t, buf: *fdstat_t) errno_t;
+pub extern "wasi_unstable" fn fd_fdstat_set_flags(fd: fd_t, flags: fdflags_t) errno_t;
+pub extern "wasi_unstable" fn fd_fdstat_set_rights(fd: fd_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t) errno_t;
+
+pub extern "wasi_unstable" fn fd_filestat_get(fd: fd_t, buf: *filestat_t) errno_t;
+pub extern "wasi_unstable" fn fd_filestat_set_size(fd: fd_t, st_size: filesize_t) errno_t;
+pub extern "wasi_unstable" fn fd_filestat_set_times(fd: fd_t, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t;
+
+pub extern "wasi_unstable" fn fd_prestat_get(fd: fd_t, buf: *prestat_t) errno_t;
+pub extern "wasi_unstable" fn fd_prestat_dir_name(fd: fd_t, path: [*]u8, path_len: usize) errno_t;
+
+pub extern "wasi_unstable" fn path_create_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t;
+pub extern "wasi_unstable" fn path_filestat_get(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, buf: *filestat_t) errno_t;
+pub extern "wasi_unstable" fn path_filestat_set_times(fd: fd_t, flags: lookupflags_t, path: [*]const u8, path_len: usize, st_atim: timestamp_t, st_mtim: timestamp_t, fstflags: fstflags_t) errno_t;
+pub extern "wasi_unstable" fn path_link(old_fd: fd_t, old_flags: lookupflags_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t;
+pub extern "wasi_unstable" fn path_open(dirfd: fd_t, dirflags: lookupflags_t, path: [*]const u8, path_len: usize, oflags: oflags_t, fs_rights_base: rights_t, fs_rights_inheriting: rights_t, fs_flags: fdflags_t, fd: *fd_t) errno_t;
+pub extern "wasi_unstable" fn path_readlink(fd: fd_t, path: [*]const u8, path_len: usize, buf: [*]u8, buf_len: usize, bufused: *usize) errno_t;
+pub extern "wasi_unstable" fn path_remove_directory(fd: fd_t, path: [*]const u8, path_len: usize) errno_t;
+pub extern "wasi_unstable" fn path_rename(old_fd: fd_t, old_path: [*]const u8, old_path_len: usize, new_fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t;
+pub extern "wasi_unstable" fn path_symlink(old_path: [*]const u8, old_path_len: usize, fd: fd_t, new_path: [*]const u8, new_path_len: usize) errno_t;
+pub extern "wasi_unstable" fn path_unlink_file(fd: fd_t, path: [*]const u8, path_len: usize) errno_t;
+
+pub extern "wasi_unstable" fn poll_oneoff(in: *const subscription_t, out: *event_t, nsubscriptions: usize, nevents: *usize) errno_t;
+
+pub extern "wasi_unstable" fn proc_exit(rval: exitcode_t) noreturn;
+pub extern "wasi_unstable" fn proc_raise(sig: signal_t) errno_t;
+
+pub extern "wasi_unstable" fn random_get(buf: [*]u8, buf_len: usize) errno_t;
+
+pub extern "wasi_unstable" fn sched_yield() errno_t;
+
+pub extern "wasi_unstable" fn sock_recv(sock: fd_t, ri_data: *const iovec_t, ri_data_len: usize, ri_flags: riflags_t, ro_datalen: *usize, ro_flags: *roflags_t) errno_t;
+pub extern "wasi_unstable" fn sock_send(sock: fd_t, si_data: *const ciovec_t, si_data_len: usize, si_flags: siflags_t, so_datalen: *usize) errno_t;
+pub extern "wasi_unstable" fn sock_shutdown(sock: fd_t, how: sdflags_t) errno_t;
diff --git a/std/os/windows.zig b/std/os/windows.zig
index 96274632ce..e134d87eae 100644
--- a/std/os/windows.zig
+++ b/std/os/windows.zig
@@ -239,6 +239,37 @@ pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000;
pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004;
pub const HEAP_NO_SERIALIZE = 0x00000001;
+// AllocationType values
+pub const MEM_COMMIT = 0x1000;
+pub const MEM_RESERVE = 0x2000;
+pub const MEM_RESET = 0x80000;
+pub const MEM_RESET_UNDO = 0x1000000;
+pub const MEM_LARGE_PAGES = 0x20000000;
+pub const MEM_PHYSICAL = 0x400000;
+pub const MEM_TOP_DOWN = 0x100000;
+pub const MEM_WRITE_WATCH = 0x200000;
+
+// Protect values
+pub const PAGE_EXECUTE = 0x10;
+pub const PAGE_EXECUTE_READ = 0x20;
+pub const PAGE_EXECUTE_READWRITE = 0x40;
+pub const PAGE_EXECUTE_WRITECOPY = 0x80;
+pub const PAGE_NOACCESS = 0x01;
+pub const PAGE_READONLY = 0x02;
+pub const PAGE_READWRITE = 0x04;
+pub const PAGE_WRITECOPY = 0x08;
+pub const PAGE_TARGETS_INVALID = 0x40000000;
+pub const PAGE_TARGETS_NO_UPDATE = 0x40000000; // Same as PAGE_TARGETS_INVALID
+pub const PAGE_GUARD = 0x100;
+pub const PAGE_NOCACHE = 0x200;
+pub const PAGE_WRITECOMBINE = 0x400;
+
+// FreeType values
+pub const MEM_COALESCE_PLACEHOLDERS = 0x1;
+pub const MEM_RESERVE_PLACEHOLDERS = 0x2;
+pub const MEM_DECOMMIT = 0x4000;
+pub const MEM_RELEASE = 0x8000;
+
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig
index 64a97ca87d..7e99f7eda9 100644
--- a/std/os/windows/kernel32.zig
+++ b/std/os/windows/kernel32.zig
@@ -116,6 +116,9 @@ pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: ?*const c_void) BOOL;
+pub extern "kernel32" stdcallcc fn VirtualAlloc(lpAddress: ?LPVOID, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD) ?LPVOID;
+pub extern "kernel32" stdcallcc fn VirtualFree(lpAddress: ?LPVOID, dwSize: SIZE_T, dwFreeType: DWORD) BOOL;
+
pub extern "kernel32" stdcallcc fn MoveFileExW(
lpExistingFileName: [*]const u16,
lpNewFileName: [*]const u16,
diff --git a/std/packed_int_array.zig b/std/packed_int_array.zig
new file mode 100644
index 0000000000..d4ed68c6ee
--- /dev/null
+++ b/std/packed_int_array.zig
@@ -0,0 +1,649 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const debug = std.debug;
+const testing = std.testing;
+
+pub fn PackedIntIo(comptime Int: type, comptime endian: builtin.Endian) type {
+ //The general technique employed here is to cast bytes in the array to a container
+ // integer (having bits % 8 == 0) large enough to contain the number of bits we want,
+ // then we can retrieve or store the new value with a relative minimum of masking
+ // and shifting. In this worst case, this means that we'll need an integer that's
+ // actually 1 byte larger than the minimum required to store the bits, because it
+ // is possible that the bits start at the end of the first byte, continue through
+ // zero or more, then end in the beginning of the last. But, if we try to access
+ // a value in the very last byte of memory with that integer size, that extra byte
+ // will be out of bounds. Depending on the circumstances of the memory, that might
+ // mean the OS fatally kills the program. Thus, we use a larger container (MaxIo)
+ // most of the time, but a smaller container (MinIo) when touching the last byte
+ // of the memory.
+ const int_bits = comptime std.meta.bitCount(Int);
+
+ //in the best case, this is the number of bytes we need to touch
+ // to read or write a value, as bits
+ const min_io_bits = ((int_bits + 7) / 8) * 8;
+
+ //in the worst case, this is the number of bytes we need to touch
+ // to read or write a value, as bits
+ const max_io_bits = switch (int_bits) {
+ 0 => 0,
+ 1 => 8,
+ 2...9 => 16,
+ 10...65535 => ((int_bits / 8) + 2) * 8,
+ else => unreachable,
+ };
+
+ //we bitcast the desired Int type to an unsigned version of itself
+ // to avoid issues with shifting signed ints.
+ const UnInt = @IntType(false, int_bits);
+
+ //The maximum container int type
+ const MinIo = @IntType(false, min_io_bits);
+
+ //The minimum container int type
+ const MaxIo = @IntType(false, max_io_bits);
+
+ return struct {
+ pub fn get(bytes: []const u8, index: usize, bit_offset: u7) Int {
+ if (int_bits == 0) return 0;
+
+ const bit_index = (index * int_bits) + bit_offset;
+ const max_end_byte = (bit_index + max_io_bits) / 8;
+
+ //Using the larger container size will potentially read out of bounds
+ if (max_end_byte > bytes.len) return getBits(bytes, MinIo, bit_index);
+ return getBits(bytes, MaxIo, bit_index);
+ }
+
+ fn getBits(bytes: []const u8, comptime Container: type, bit_index: usize) Int {
+ const container_bits = comptime std.meta.bitCount(Container);
+ const Shift = std.math.Log2Int(Container);
+
+ const start_byte = bit_index / 8;
+ const head_keep_bits = bit_index - (start_byte * 8);
+ const tail_keep_bits = container_bits - (int_bits + head_keep_bits);
+
+ //read bytes as container
+ const value_ptr = @ptrCast(*align(1) const Container, &bytes[start_byte]);
+ var value = value_ptr.*;
+
+ if (endian != builtin.endian) value = @bswap(Container, value);
+
+ switch (endian) {
+ .Big => {
+ value <<= @intCast(Shift, head_keep_bits);
+ value >>= @intCast(Shift, head_keep_bits);
+ value >>= @intCast(Shift, tail_keep_bits);
+ },
+ .Little => {
+ value <<= @intCast(Shift, tail_keep_bits);
+ value >>= @intCast(Shift, tail_keep_bits);
+ value >>= @intCast(Shift, head_keep_bits);
+ },
+ }
+
+ return @bitCast(Int, @truncate(UnInt, value));
+ }
+
+ pub fn set(bytes: []u8, index: usize, bit_offset: u3, int: Int) void {
+ if (int_bits == 0) return;
+
+ const bit_index = (index * int_bits) + bit_offset;
+ const max_end_byte = (bit_index + max_io_bits) / 8;
+
+ //Using the larger container size will potentially write out of bounds
+ if (max_end_byte > bytes.len) return setBits(bytes, MinIo, bit_index, int);
+ setBits(bytes, MaxIo, bit_index, int);
+ }
+
+ fn setBits(bytes: []u8, comptime Container: type, bit_index: usize, int: Int) void {
+ const container_bits = comptime std.meta.bitCount(Container);
+ const Shift = std.math.Log2Int(Container);
+
+ const start_byte = bit_index / 8;
+ const head_keep_bits = bit_index - (start_byte * 8);
+ const tail_keep_bits = container_bits - (int_bits + head_keep_bits);
+ const keep_shift = switch (endian) {
+ .Big => @intCast(Shift, tail_keep_bits),
+ .Little => @intCast(Shift, head_keep_bits),
+ };
+
+ //position the bits where they need to be in the container
+ const value = @intCast(Container, @bitCast(UnInt, int)) << keep_shift;
+
+ //read existing bytes
+ const target_ptr = @ptrCast(*align(1) Container, &bytes[start_byte]);
+ var target = target_ptr.*;
+
+ if (endian != builtin.endian) target = @bswap(Container, target);
+
+ //zero the bits we want to replace in the existing bytes
+ const inv_mask = @intCast(Container, std.math.maxInt(UnInt)) << keep_shift;
+ const mask = ~inv_mask;
+ target &= mask;
+
+ //merge the new value
+ target |= value;
+
+ if (endian != builtin.endian) target = @bswap(Container, target);
+
+ //save it back
+ target_ptr.* = target;
+ }
+
+ fn slice(bytes: []u8, bit_offset: u3, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
+ debug.assert(end >= start);
+
+ const length = end - start;
+ const bit_index = (start * int_bits) + bit_offset;
+ const start_byte = bit_index / 8;
+ const end_byte = (bit_index + (length * int_bits) + 7) / 8;
+ const new_bytes = bytes[start_byte..end_byte];
+
+ if (length == 0) return PackedIntSliceEndian(Int, endian).init(new_bytes[0..0], 0);
+
+ var new_slice = PackedIntSliceEndian(Int, endian).init(new_bytes, length);
+ new_slice.bit_offset = @intCast(u3, (bit_index - (start_byte * 8)));
+ return new_slice;
+ }
+
+ fn sliceCast(bytes: []u8, comptime NewInt: type, comptime new_endian: builtin.Endian, bit_offset: u3, old_len: usize) PackedIntSliceEndian(NewInt, new_endian) {
+ const new_int_bits = comptime std.meta.bitCount(NewInt);
+ const New = PackedIntSliceEndian(NewInt, new_endian);
+
+ const total_bits = (old_len * int_bits);
+ const new_int_count = total_bits / new_int_bits;
+
+ debug.assert(total_bits == new_int_count * new_int_bits);
+
+ var new = New.init(bytes, new_int_count);
+ new.bit_offset = bit_offset;
+
+ return new;
+ }
+ };
+}
+
+///Creates a bit-packed array of integers of type Int. Bits
+/// are packed using native endianess and without storing any meta
+/// data. PackedIntArray(i3, 8) will occupy exactly 3 bytes of memory.
+pub fn PackedIntArray(comptime Int: type, comptime int_count: usize) type {
+ return PackedIntArrayEndian(Int, builtin.endian, int_count);
+}
+
+///Creates a bit-packed array of integers of type Int. Bits
+/// are packed using specified endianess and without storing any meta
+/// data.
+pub fn PackedIntArrayEndian(comptime Int: type, comptime endian: builtin.Endian, comptime int_count: usize) type {
+ const int_bits = comptime std.meta.bitCount(Int);
+ const total_bits = int_bits * int_count;
+ const total_bytes = (total_bits + 7) / 8;
+
+ const Io = PackedIntIo(Int, endian);
+
+ return struct {
+ const Self = @This();
+
+ bytes: [total_bytes]u8,
+
+ ///Returns the number of elements in the packed array
+ pub fn len(self: Self) usize {
+ return int_count;
+ }
+
+ ///Initialize a packed array using an unpacked array
+ /// or, more likely, an array literal.
+ pub fn init(ints: [int_count]Int) Self {
+ var self = Self(undefined);
+ for (ints) |int, i| self.set(i, int);
+ return self;
+ }
+
+ ///Return the Int stored at index
+ pub fn get(self: Self, index: usize) Int {
+ debug.assert(index < int_count);
+ return Io.get(self.bytes, index, 0);
+ }
+
+ ///Copy int into the array at index
+ pub fn set(self: *Self, index: usize, int: Int) void {
+ debug.assert(index < int_count);
+ return Io.set(&self.bytes, index, 0, int);
+ }
+
+ ///Create a PackedIntSlice of the array from given start to given end
+ pub fn slice(self: *Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
+ debug.assert(start < int_count);
+ debug.assert(end <= int_count);
+ return Io.slice(&self.bytes, 0, start, end);
+ }
+
+ ///Create a PackedIntSlice of the array using NewInt as the bit width integer.
+ /// NewInt's bit width must fit evenly within the array's Int's total bits.
+ pub fn sliceCast(self: *Self, comptime NewInt: type) PackedIntSlice(NewInt) {
+ return self.sliceCastEndian(NewInt, endian);
+ }
+
+ ///Create a PackedIntSlice of the array using NewInt as the bit width integer
+ /// and new_endian as the new endianess. NewInt's bit width must fit evenly within
+ /// the array's Int's total bits.
+ pub fn sliceCastEndian(self: *Self, comptime NewInt: type, comptime new_endian: builtin.Endian) PackedIntSliceEndian(NewInt, new_endian) {
+ return Io.sliceCast(&self.bytes, NewInt, new_endian, 0, int_count);
+ }
+ };
+}
+
+///Uses a slice as a bit-packed block of int_count integers of type Int.
+/// Bits are packed using native endianess and without storing any meta
+/// data.
+pub fn PackedIntSlice(comptime Int: type) type {
+ return PackedIntSliceEndian(Int, builtin.endian);
+}
+
+///Uses a slice as a bit-packed block of int_count integers of type Int.
+/// Bits are packed using specified endianess and without storing any meta
+/// data.
+pub fn PackedIntSliceEndian(comptime Int: type, comptime endian: builtin.Endian) type {
+ const int_bits = comptime std.meta.bitCount(Int);
+ const Io = PackedIntIo(Int, endian);
+
+ return struct {
+ const Self = @This();
+
+ bytes: []u8,
+ int_count: usize,
+ bit_offset: u3,
+
+ ///Returns the number of elements in the packed slice
+ pub fn len(self: Self) usize {
+ return self.int_count;
+ }
+
+ ///Calculates the number of bytes required to store a desired count
+ /// of Ints
+ pub fn bytesRequired(int_count: usize) usize {
+ const total_bits = int_bits * int_count;
+ const total_bytes = (total_bits + 7) / 8;
+ return total_bytes;
+ }
+
+ ///Initialize a packed slice using the memory at bytes, with int_count
+ /// elements. bytes must be large enough to accomodate the requested
+ /// count.
+ pub fn init(bytes: []u8, int_count: usize) Self {
+ debug.assert(bytes.len >= bytesRequired(int_count));
+
+ return Self{
+ .bytes = bytes,
+ .int_count = int_count,
+ .bit_offset = 0,
+ };
+ }
+
+ ///Return the Int stored at index
+ pub fn get(self: Self, index: usize) Int {
+ debug.assert(index < self.int_count);
+ return Io.get(self.bytes, index, self.bit_offset);
+ }
+
+ ///Copy int into the array at index
+ pub fn set(self: *Self, index: usize, int: Int) void {
+ debug.assert(index < self.int_count);
+ return Io.set(self.bytes, index, self.bit_offset, int);
+ }
+
+ ///Create a PackedIntSlice of this slice from given start to given end
+ pub fn slice(self: Self, start: usize, end: usize) PackedIntSliceEndian(Int, endian) {
+ debug.assert(start < self.int_count);
+ debug.assert(end <= self.int_count);
+ return Io.slice(self.bytes, self.bit_offset, start, end);
+ }
+
+ ///Create a PackedIntSlice of this slice using NewInt as the bit width integer.
+ /// NewInt's bit width must fit evenly within this slice's Int's total bits.
+ pub fn sliceCast(self: Self, comptime NewInt: type) PackedIntSliceEndian(NewInt, endian) {
+ return self.sliceCastEndian(NewInt, endian);
+ }
+
+ ///Create a PackedIntSlice of this slice using NewInt as the bit width integer
+ /// and new_endian as the new endianess. NewInt's bit width must fit evenly within
+ /// this slice's Int's total bits.
+ pub fn sliceCastEndian(self: Self, comptime NewInt: type, comptime new_endian: builtin.Endian) PackedIntSliceEndian(NewInt, new_endian) {
+ return Io.sliceCast(self.bytes, NewInt, new_endian, self.bit_offset, self.int_count);
+ }
+ };
+}
+
+test "PackedIntArray" {
+ @setEvalBranchQuota(10000);
+ const max_bits = 256;
+ const int_count = 19;
+
+ comptime var bits = 0;
+ inline while (bits <= 256) : (bits += 1) {
+ //alternate unsigned and signed
+ const even = bits % 2 == 0;
+ const I = @IntType(even, bits);
+
+ const PackedArray = PackedIntArray(I, int_count);
+ const expected_bytes = ((bits * int_count) + 7) / 8;
+ testing.expect(@sizeOf(PackedArray) == expected_bytes);
+
+ var data = PackedArray(undefined);
+
+ //write values, counting up
+ var i = usize(0);
+ var count = I(0);
+ while (i < data.len()) : (i += 1) {
+ data.set(i, count);
+ if (bits > 0) count +%= 1;
+ }
+
+ //read and verify values
+ i = 0;
+ count = 0;
+ while (i < data.len()) : (i += 1) {
+ const val = data.get(i);
+ testing.expect(val == count);
+ if (bits > 0) count +%= 1;
+ }
+ }
+}
+
+test "PackedIntArray init" {
+ const PackedArray = PackedIntArray(u3, 8);
+ var packed_array = PackedArray.init([]u3{ 0, 1, 2, 3, 4, 5, 6, 7 });
+ var i = usize(0);
+ while (i < packed_array.len()) : (i += 1) testing.expect(packed_array.get(i) == i);
+}
+
+test "PackedIntSlice" {
+ @setEvalBranchQuota(10000);
+ const max_bits = 256;
+ const int_count = 19;
+ const total_bits = max_bits * int_count;
+ const total_bytes = (total_bits + 7) / 8;
+
+ var buffer: [total_bytes]u8 = undefined;
+
+ comptime var bits = 0;
+ inline while (bits <= 256) : (bits += 1) {
+ //alternate unsigned and signed
+ const even = bits % 2 == 0;
+ const I = @IntType(even, bits);
+ const P = PackedIntSlice(I);
+
+ var data = P.init(&buffer, int_count);
+
+ //write values, counting up
+ var i = usize(0);
+ var count = I(0);
+ while (i < data.len()) : (i += 1) {
+ data.set(i, count);
+ if (bits > 0) count +%= 1;
+ }
+
+ //read and verify values
+ i = 0;
+ count = 0;
+ while (i < data.len()) : (i += 1) {
+ const val = data.get(i);
+ testing.expect(val == count);
+ if (bits > 0) count +%= 1;
+ }
+ }
+}
+
+test "PackedIntSlice of PackedInt(Array/Slice)" {
+ const max_bits = 16;
+ const int_count = 19;
+
+ comptime var bits = 0;
+ inline while (bits <= max_bits) : (bits += 1) {
+ const Int = @IntType(false, bits);
+
+ const PackedArray = PackedIntArray(Int, int_count);
+ var packed_array = PackedArray(undefined);
+
+ const limit = (1 << bits);
+
+ var i = usize(0);
+ while (i < packed_array.len()) : (i += 1) {
+ packed_array.set(i, @intCast(Int, i % limit));
+ }
+
+ //slice of array
+ var packed_slice = packed_array.slice(2, 5);
+ testing.expect(packed_slice.len() == 3);
+ const ps_bit_count = (bits * packed_slice.len()) + packed_slice.bit_offset;
+ const ps_expected_bytes = (ps_bit_count + 7) / 8;
+ testing.expect(packed_slice.bytes.len == ps_expected_bytes);
+ testing.expect(packed_slice.get(0) == 2 % limit);
+ testing.expect(packed_slice.get(1) == 3 % limit);
+ testing.expect(packed_slice.get(2) == 4 % limit);
+ packed_slice.set(1, 7 % limit);
+ testing.expect(packed_slice.get(1) == 7 % limit);
+
+ //write through slice
+ testing.expect(packed_array.get(3) == 7 % limit);
+
+ //slice of a slice
+ const packed_slice_two = packed_slice.slice(0, 3);
+ testing.expect(packed_slice_two.len() == 3);
+ const ps2_bit_count = (bits * packed_slice_two.len()) + packed_slice_two.bit_offset;
+ const ps2_expected_bytes = (ps2_bit_count + 7) / 8;
+ testing.expect(packed_slice_two.bytes.len == ps2_expected_bytes);
+ testing.expect(packed_slice_two.get(1) == 7 % limit);
+ testing.expect(packed_slice_two.get(2) == 4 % limit);
+
+ //size one case
+ const packed_slice_three = packed_slice_two.slice(1, 2);
+ testing.expect(packed_slice_three.len() == 1);
+ const ps3_bit_count = (bits * packed_slice_three.len()) + packed_slice_three.bit_offset;
+ const ps3_expected_bytes = (ps3_bit_count + 7) / 8;
+ testing.expect(packed_slice_three.bytes.len == ps3_expected_bytes);
+ testing.expect(packed_slice_three.get(0) == 7 % limit);
+
+ //empty slice case
+ const packed_slice_empty = packed_slice.slice(0, 0);
+ testing.expect(packed_slice_empty.len() == 0);
+ testing.expect(packed_slice_empty.bytes.len == 0);
+
+ //slicing at byte boundaries
+ const packed_slice_edge = packed_array.slice(8, 16);
+ testing.expect(packed_slice_edge.len() == 8);
+ const pse_bit_count = (bits * packed_slice_edge.len()) + packed_slice_edge.bit_offset;
+ const pse_expected_bytes = (pse_bit_count + 7) / 8;
+ testing.expect(packed_slice_edge.bytes.len == pse_expected_bytes);
+ testing.expect(packed_slice_edge.bit_offset == 0);
+ }
+}
+
+test "PackedIntSlice accumulating bit offsets" {
+ //bit_offset is u3, so standard debugging asserts should catch
+ // anything
+ {
+ const PackedArray = PackedIntArray(u3, 16);
+ var packed_array = PackedArray(undefined);
+
+ var packed_slice = packed_array.slice(0, packed_array.len());
+ var i = usize(0);
+ while (i < packed_array.len() - 1) : (i += 1) {
+ packed_slice = packed_slice.slice(1, packed_slice.len());
+ }
+ }
+ {
+ const PackedArray = PackedIntArray(u11, 88);
+ var packed_array = PackedArray(undefined);
+
+ var packed_slice = packed_array.slice(0, packed_array.len());
+ var i = usize(0);
+ while (i < packed_array.len() - 1) : (i += 1) {
+ packed_slice = packed_slice.slice(1, packed_slice.len());
+ }
+ }
+}
+
+//@NOTE: As I do not have a big endian system to test this on,
+// big endian values were not tested
+test "PackedInt(Array/Slice) sliceCast" {
+ const PackedArray = PackedIntArray(u1, 16);
+ var packed_array = PackedArray.init([]u1{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 });
+ const packed_slice_cast_2 = packed_array.sliceCast(u2);
+ const packed_slice_cast_4 = packed_slice_cast_2.sliceCast(u4);
+ var packed_slice_cast_9 = packed_array.slice(0, (packed_array.len() / 9) * 9).sliceCast(u9);
+ const packed_slice_cast_3 = packed_slice_cast_9.sliceCast(u3);
+
+ var i = usize(0);
+ while (i < packed_slice_cast_2.len()) : (i += 1) {
+ const val = switch (builtin.endian) {
+ .Big => 0b01,
+ .Little => 0b10,
+ };
+ testing.expect(packed_slice_cast_2.get(i) == val);
+ }
+ i = 0;
+ while (i < packed_slice_cast_4.len()) : (i += 1) {
+ const val = switch (builtin.endian) {
+ .Big => 0b0101,
+ .Little => 0b1010,
+ };
+ testing.expect(packed_slice_cast_4.get(i) == val);
+ }
+ i = 0;
+ while (i < packed_slice_cast_9.len()) : (i += 1) {
+ const val = 0b010101010;
+ testing.expect(packed_slice_cast_9.get(i) == val);
+ packed_slice_cast_9.set(i, 0b111000111);
+ }
+ i = 0;
+ while (i < packed_slice_cast_3.len()) : (i += 1) {
+ const val = switch (builtin.endian) {
+ .Big => if (i % 2 == 0) u3(0b111) else u3(0b000),
+ .Little => if (i % 2 == 0) u3(0b111) else u3(0b000),
+ };
+ testing.expect(packed_slice_cast_3.get(i) == val);
+ }
+}
+
+test "PackedInt(Array/Slice)Endian" {
+ {
+ const PackedArrayBe = PackedIntArrayEndian(u4, .Big, 8);
+ var packed_array_be = PackedArrayBe.init([]u4{
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ });
+ testing.expect(packed_array_be.bytes[0] == 0b00000001);
+ testing.expect(packed_array_be.bytes[1] == 0b00100011);
+
+ var i = usize(0);
+ while (i < packed_array_be.len()) : (i += 1) {
+ testing.expect(packed_array_be.get(i) == i);
+ }
+
+ var packed_slice_le = packed_array_be.sliceCastEndian(u4, .Little);
+ i = 0;
+ while (i < packed_slice_le.len()) : (i += 1) {
+ const val = if (i % 2 == 0) i + 1 else i - 1;
+ testing.expect(packed_slice_le.get(i) == val);
+ }
+
+ var packed_slice_le_shift = packed_array_be.slice(1, 5).sliceCastEndian(u4, .Little);
+ i = 0;
+ while (i < packed_slice_le_shift.len()) : (i += 1) {
+ const val = if (i % 2 == 0) i else i + 2;
+ testing.expect(packed_slice_le_shift.get(i) == val);
+ }
+ }
+
+ {
+ const PackedArrayBe = PackedIntArrayEndian(u11, .Big, 8);
+ var packed_array_be = PackedArrayBe.init([]u11{
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ });
+ testing.expect(packed_array_be.bytes[0] == 0b00000000);
+ testing.expect(packed_array_be.bytes[1] == 0b00000000);
+ testing.expect(packed_array_be.bytes[2] == 0b00000100);
+ testing.expect(packed_array_be.bytes[3] == 0b00000001);
+ testing.expect(packed_array_be.bytes[4] == 0b00000000);
+
+ var i = usize(0);
+ while (i < packed_array_be.len()) : (i += 1) {
+ testing.expect(packed_array_be.get(i) == i);
+ }
+
+ var packed_slice_le = packed_array_be.sliceCastEndian(u11, .Little);
+ testing.expect(packed_slice_le.get(0) == 0b00000000000);
+ testing.expect(packed_slice_le.get(1) == 0b00010000000);
+ testing.expect(packed_slice_le.get(2) == 0b00000000100);
+ testing.expect(packed_slice_le.get(3) == 0b00000000000);
+ testing.expect(packed_slice_le.get(4) == 0b00010000011);
+ testing.expect(packed_slice_le.get(5) == 0b00000000010);
+ testing.expect(packed_slice_le.get(6) == 0b10000010000);
+ testing.expect(packed_slice_le.get(7) == 0b00000111001);
+
+ var packed_slice_le_shift = packed_array_be.slice(1, 5).sliceCastEndian(u11, .Little);
+ testing.expect(packed_slice_le_shift.get(0) == 0b00010000000);
+ testing.expect(packed_slice_le_shift.get(1) == 0b00000000100);
+ testing.expect(packed_slice_le_shift.get(2) == 0b00000000000);
+ testing.expect(packed_slice_le_shift.get(3) == 0b00010000011);
+ }
+}
+
+//@NOTE: Need to manually update this list as more posix os's get
+// added to DirectAllocator. Windows can be added too when DirectAllocator
+// switches to VirtualAlloc.
+
+//These tests prove we aren't accidentally accessing memory past
+// the end of the array/slice by placing it at the end of a page
+// and reading the last element. The assumption is that the page
+// after this one is not mapped and will cause a segfault if we
+// don't account for the bounds.
+test "PackedIntArray at end of available memory" {
+ switch (builtin.os) {
+ .linux, .macosx, .ios, .freebsd, .netbsd => {},
+ else => return,
+ }
+ const PackedArray = PackedIntArray(u3, 8);
+
+ const Padded = struct {
+ _: [std.os.page_size - @sizeOf(PackedArray)]u8,
+ p: PackedArray,
+ };
+
+ var da = std.heap.DirectAllocator.init();
+ const allocator = &da.allocator;
+
+ var pad = try allocator.create(Padded);
+ defer allocator.destroy(pad);
+ pad.p.set(7, std.math.maxInt(u3));
+}
+
+test "PackedIntSlice at end of available memory" {
+ switch (builtin.os) {
+ .linux, .macosx, .ios, .freebsd, .netbsd => {},
+ else => return,
+ }
+ const PackedSlice = PackedIntSlice(u11);
+
+ var da = std.heap.DirectAllocator.init();
+ const allocator = &da.allocator;
+
+ var page = try allocator.alloc(u8, std.os.page_size);
+ defer allocator.free(page);
+
+ var p = PackedSlice.init(page[std.os.page_size - 2 ..], 1);
+ p.set(0, std.math.maxInt(u11));
+}
diff --git a/std/pdb.zig b/std/pdb.zig
index 2b02a84871..043be2bcf4 100644
--- a/std/pdb.zig
+++ b/std/pdb.zig
@@ -588,7 +588,7 @@ const SuperBlock = packed struct {
const MsfStream = struct {
in_file: os.File,
- pos: usize,
+ pos: u64,
blocks: []u32,
block_size: u32,
@@ -598,7 +598,7 @@ const MsfStream = struct {
pub const Error = @typeOf(read).ReturnType.ErrorSet;
pub const Stream = io.InStream(Error);
- fn init(block_size: u32, block_count: u32, pos: usize, file: os.File, allocator: *mem.Allocator) !MsfStream {
+ fn init(block_size: u32, block_count: u32, pos: u64, file: os.File, allocator: *mem.Allocator) !MsfStream {
var stream = MsfStream{
.in_file = file,
.pos = 0,
@@ -660,23 +660,24 @@ const MsfStream = struct {
return size;
}
- fn seekForward(self: *MsfStream, len: usize) !void {
+ // XXX: The `len` parameter should be signed
+ fn seekForward(self: *MsfStream, len: u64) !void {
self.pos += len;
if (self.pos >= self.blocks.len * self.block_size)
return error.EOF;
}
- fn seekTo(self: *MsfStream, len: usize) !void {
+ fn seekTo(self: *MsfStream, len: u64) !void {
self.pos = len;
if (self.pos >= self.blocks.len * self.block_size)
return error.EOF;
}
- fn getSize(self: *const MsfStream) usize {
+ fn getSize(self: *const MsfStream) u64 {
return self.blocks.len * self.block_size;
}
- fn getFilePos(self: MsfStream) usize {
+ fn getFilePos(self: MsfStream) u64 {
const block_id = self.pos / self.block_size;
const block = self.blocks[block_id];
const offset = self.pos % self.block_size;
diff --git a/std/rand.zig b/std/rand.zig
index a2fdfed6fd..4a6563f65a 100644
--- a/std/rand.zig
+++ b/std/rand.zig
@@ -768,10 +768,10 @@ pub const Isaac64 = struct {
const x = self.m[base + m1];
self.a = mix +% self.m[base + m2];
- const y = self.a +% self.b +% self.m[(x >> 3) % self.m.len];
+ const y = self.a +% self.b +% self.m[@intCast(usize, (x >> 3) % self.m.len)];
self.m[base + m1] = y;
- self.b = x +% self.m[(y >> 11) % self.m.len];
+ self.b = x +% self.m[@intCast(usize, (y >> 11) % self.m.len)];
self.r[self.r.len - 1 - base - m1] = self.b;
}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 32f913a5b0..e6505c836b 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -20,6 +20,10 @@ comptime {
}
nakedcc fn _start() noreturn {
+ if (builtin.os == builtin.Os.wasi) {
+ std.os.wasi.proc_exit(callMain());
+ }
+
switch (builtin.arch) {
builtin.Arch.x86_64 => {
argc_ptr = asm ("lea (%%rsp), %[argc]"
@@ -63,24 +67,19 @@ fn posixCallMainAndExit() noreturn {
var envp_count: usize = 0;
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
+
if (builtin.os == builtin.Os.linux) {
- // Scan auxiliary vector.
+ // Find the beginning of the auxiliary vector
const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
std.os.linux_elf_aux_maybe = auxv;
- var i: usize = 0;
- var at_phdr: usize = 0;
- var at_phnum: usize = 0;
- var at_phent: usize = 0;
- while (auxv[i].a_un.a_val != 0) : (i += 1) {
- switch (auxv[i].a_type) {
- std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
- std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
- std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
- std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
- else => {},
- }
+ // Initialize the TLS area
+ std.os.linux.tls.initTLS();
+
+ if (std.os.linux.tls.tls_image) |tls_img| {
+ const tls_addr = std.os.linux.tls.allocateTLS(tls_img.alloc_size);
+ const tp = std.os.linux.tls.copyTLS(tls_addr);
+ std.os.linux.tls.setThreadPointer(tp);
}
- if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
}
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
@@ -136,50 +135,3 @@ inline fn callMain() u8 {
const main_thread_tls_align = 32;
var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
-
-fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
- var phdr_addr = at_phdr;
- var n = at_phnum;
- var base: usize = 0;
- while (n != 0) : ({
- n -= 1;
- phdr_addr += at_phent;
- }) {
- const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
- // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
- switch (phdr.p_type) {
- std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
- std.elf.PT_TLS => std.os.linux_tls_phdr = phdr,
- else => continue,
- }
- }
- const tls_phdr = std.os.linux_tls_phdr orelse return;
- std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
- const end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
- const max_end_addr = @ptrToInt(&main_thread_tls_bytes) + main_thread_tls_bytes.len;
- assert(max_end_addr >= end_addr + @sizeOf(usize)); // not enough preallocated Thread Local Storage
- assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
- @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz);
- const end_ptr = @intToPtr(*usize, end_addr);
- end_ptr.* = end_addr;
- linuxSetThreadArea(end_addr);
-}
-
-fn linuxSetThreadArea(addr: usize) void {
- switch (builtin.arch) {
- builtin.Arch.x86_64 => {
- const ARCH_SET_FS = 0x1002;
- const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
- // acrh_prctl is documented to never fail
- assert(rc == 0);
- },
- builtin.Arch.aarch64 => {
- asm volatile (
- \\ msr tpidr_el0,x0
- \\ mov w0,#0
- \\ ret
- );
- },
- else => @compileError("Unsupported architecture"),
- }
-}
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index 56cfe3bcb5..dfc3838577 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -94,6 +94,16 @@ pub fn main() !void {
return usageAndErr(&builder, false, try stderr_stream);
});
builder.addSearchPrefix(search_prefix);
+ } else if (mem.eql(u8, arg, "--override-std-dir")) {
+ builder.override_std_dir = try unwrapArg(arg_it.next(allocator) orelse {
+ warn("Expected argument after --override-std-dir\n\n");
+ return usageAndErr(&builder, false, try stderr_stream);
+ });
+ } else if (mem.eql(u8, arg, "--override-lib-dir")) {
+ builder.override_lib_dir = try unwrapArg(arg_it.next(allocator) orelse {
+ warn("Expected argument after --override-lib-dir\n\n");
+ return usageAndErr(&builder, false, try stderr_stream);
+ });
} else if (mem.eql(u8, arg, "--verbose-tokenize")) {
builder.verbose_tokenize = true;
} else if (mem.eql(u8, arg, "--verbose-ast")) {
@@ -187,15 +197,17 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void {
try out_stream.write(
\\
\\Advanced Options:
- \\ --build-file [file] Override path to build.zig
- \\ --cache-dir [path] Override path to zig cache directory
- \\ --verbose-tokenize Enable compiler debug output for tokenization
- \\ --verbose-ast Enable compiler debug output for parsing into an AST
- \\ --verbose-link Enable compiler debug output for linking
- \\ --verbose-ir Enable compiler debug output for Zig IR
- \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
- \\ --verbose-cimport Enable compiler debug output for C imports
- \\ --verbose-cc Enable compiler debug output for C compilation
+ \\ --build-file [file] Override path to build.zig
+ \\ --cache-dir [path] Override path to zig cache directory
+ \\ --override-std-dir [arg] Override path to Zig standard library
+ \\ --override-lib-dir [arg] Override path to Zig lib directory
+ \\ --verbose-tokenize Enable compiler debug output for tokenization
+ \\ --verbose-ast Enable compiler debug output for parsing into an AST
+ \\ --verbose-link Enable compiler debug output for linking
+ \\ --verbose-ir Enable compiler debug output for Zig IR
+ \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR
+ \\ --verbose-cimport Enable compiler debug output for C imports
+ \\ --verbose-cc Enable compiler debug output for C compilation
\\
);
}
diff --git a/std/special/compiler_rt.zig b/std/special/compiler_rt.zig
index e8d522eea1..b4df7aea5f 100644
--- a/std/special/compiler_rt.zig
+++ b/std/special/compiler_rt.zig
@@ -5,20 +5,47 @@ comptime {
const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
+ switch (builtin.arch) {
+ .i386, .x86_64 => @export("__zig_probe_stack", @import("compiler_rt/stack_probe.zig").zig_probe_stack, linkage),
+ else => {},
+ }
+
+ @export("__lesf2", @import("compiler_rt/comparesf2.zig").__lesf2, linkage);
+ @export("__ledf2", @import("compiler_rt/comparedf2.zig").__ledf2, linkage);
@export("__letf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage);
+
+ @export("__gesf2", @import("compiler_rt/comparesf2.zig").__gesf2, linkage);
+ @export("__gedf2", @import("compiler_rt/comparedf2.zig").__gedf2, linkage);
@export("__getf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage);
if (!is_test) {
// only create these aliases when not testing
+ @export("__cmpsf2", @import("compiler_rt/comparesf2.zig").__lesf2, linkage);
+ @export("__cmpdf2", @import("compiler_rt/comparedf2.zig").__ledf2, linkage);
@export("__cmptf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage);
+
+ @export("__eqsf2", @import("compiler_rt/comparesf2.zig").__eqsf2, linkage);
+ @export("__eqdf2", @import("compiler_rt/comparedf2.zig").__eqdf2, linkage);
@export("__eqtf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage);
+
+ @export("__ltsf2", @import("compiler_rt/comparesf2.zig").__ltsf2, linkage);
+ @export("__ltdf2", @import("compiler_rt/comparedf2.zig").__ltdf2, linkage);
@export("__lttf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage);
+
+ @export("__nesf2", @import("compiler_rt/comparesf2.zig").__nesf2, linkage);
+ @export("__nedf2", @import("compiler_rt/comparedf2.zig").__nedf2, linkage);
@export("__netf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage);
+
+ @export("__gtsf2", @import("compiler_rt/comparesf2.zig").__gtsf2, linkage);
+ @export("__gtdf2", @import("compiler_rt/comparedf2.zig").__gtdf2, linkage);
@export("__gttf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage);
+
@export("__gnu_h2f_ieee", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage);
@export("__gnu_f2h_ieee", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage);
}
+ @export("__unordsf2", @import("compiler_rt/comparesf2.zig").__unordsf2, linkage);
+ @export("__unorddf2", @import("compiler_rt/comparedf2.zig").__unorddf2, linkage);
@export("__unordtf2", @import("compiler_rt/comparetf2.zig").__unordtf2, linkage);
@export("__addsf3", @import("compiler_rt/addXf3.zig").__addsf3, linkage);
@@ -35,6 +62,17 @@ comptime {
@export("__divsf3", @import("compiler_rt/divsf3.zig").__divsf3, linkage);
@export("__divdf3", @import("compiler_rt/divdf3.zig").__divdf3, linkage);
+ @export("__ashlti3", @import("compiler_rt/ashlti3.zig").__ashlti3, linkage);
+ @export("__lshrti3", @import("compiler_rt/lshrti3.zig").__lshrti3, linkage);
+ @export("__ashrti3", @import("compiler_rt/ashrti3.zig").__ashrti3, linkage);
+
+ @export("__floatsidf", @import("compiler_rt/floatsiXf.zig").__floatsidf, linkage);
+ @export("__floatsisf", @import("compiler_rt/floatsiXf.zig").__floatsisf, linkage);
+ @export("__floatdidf", @import("compiler_rt/floatdidf.zig").__floatdidf, linkage);
+ @export("__floatsitf", @import("compiler_rt/floatsiXf.zig").__floatsitf, linkage);
+ @export("__floatunsidf", @import("compiler_rt/floatunsidf.zig").__floatunsidf, linkage);
+ @export("__floatundidf", @import("compiler_rt/floatundidf.zig").__floatundidf, linkage);
+
@export("__floattitf", @import("compiler_rt/floattitf.zig").__floattitf, linkage);
@export("__floattidf", @import("compiler_rt/floattidf.zig").__floattidf, linkage);
@export("__floattisf", @import("compiler_rt/floattisf.zig").__floattisf, linkage);
@@ -55,6 +93,10 @@ comptime {
@export("__trunctfdf2", @import("compiler_rt/truncXfYf2.zig").__trunctfdf2, linkage);
@export("__trunctfsf2", @import("compiler_rt/truncXfYf2.zig").__trunctfsf2, linkage);
+ @export("__truncdfsf2", @import("compiler_rt/truncXfYf2.zig").__truncdfsf2, linkage);
+
+ @export("__extendsfdf2", @import("compiler_rt/extendXfYf2.zig").__extendsfdf2, linkage);
+
@export("__fixunssfsi", @import("compiler_rt/fixunssfsi.zig").__fixunssfsi, linkage);
@export("__fixunssfdi", @import("compiler_rt/fixunssfdi.zig").__fixunssfdi, linkage);
@export("__fixunssfti", @import("compiler_rt/fixunssfti.zig").__fixunssfti, linkage);
@@ -80,18 +122,33 @@ comptime {
@export("__udivmoddi4", @import("compiler_rt/udivmoddi4.zig").__udivmoddi4, linkage);
@export("__popcountdi2", @import("compiler_rt/popcountdi2.zig").__popcountdi2, linkage);
+ @export("__divmoddi4", __divmoddi4, linkage);
+ @export("__divsi3", __divsi3, linkage);
+ @export("__divdi3", __divdi3, linkage);
@export("__udivsi3", __udivsi3, linkage);
@export("__udivdi3", __udivdi3, linkage);
+ @export("__modsi3", __modsi3, linkage);
+ @export("__moddi3", __moddi3, linkage);
+ @export("__umodsi3", __umodsi3, linkage);
@export("__umoddi3", __umoddi3, linkage);
+ @export("__divmodsi4", __divmodsi4, linkage);
@export("__udivmodsi4", __udivmodsi4, linkage);
@export("__negsf2", @import("compiler_rt/negXf2.zig").__negsf2, linkage);
@export("__negdf2", @import("compiler_rt/negXf2.zig").__negdf2, linkage);
if (is_arm_arch and !is_arm_64) {
+ @export("__aeabi_unwind_cpp_pr0", __aeabi_unwind_cpp_pr0, strong_linkage);
+ @export("__aeabi_unwind_cpp_pr1", __aeabi_unwind_cpp_pr1, linkage);
+ @export("__aeabi_unwind_cpp_pr2", __aeabi_unwind_cpp_pr2, linkage);
+
+ @export("__aeabi_ldivmod", __aeabi_ldivmod, linkage);
@export("__aeabi_uldivmod", __aeabi_uldivmod, linkage);
- @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage);
+
+ @export("__aeabi_idiv", __divsi3, linkage);
+ @export("__aeabi_idivmod", __aeabi_idivmod, linkage);
@export("__aeabi_uidiv", __udivsi3, linkage);
+ @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage);
@export("__aeabi_memcpy", __aeabi_memcpy, linkage);
@export("__aeabi_memcpy4", __aeabi_memcpy, linkage);
@@ -113,6 +170,12 @@ comptime {
@export("__aeabi_memcmp4", __aeabi_memcmp, linkage);
@export("__aeabi_memcmp8", __aeabi_memcmp, linkage);
+ @export("__aeabi_f2d", @import("compiler_rt/extendXfYf2.zig").__extendsfdf2, linkage);
+ @export("__aeabi_i2d", @import("compiler_rt/floatsiXf.zig").__floatsidf, linkage);
+ @export("__aeabi_l2d", @import("compiler_rt/floatdidf.zig").__floatdidf, linkage);
+ @export("__aeabi_ui2d", @import("compiler_rt/floatunsidf.zig").__floatunsidf, linkage);
+ @export("__aeabi_ul2d", @import("compiler_rt/floatundidf.zig").__floatundidf, linkage);
+
@export("__aeabi_fneg", @import("compiler_rt/negXf2.zig").__negsf2, linkage);
@export("__aeabi_dneg", @import("compiler_rt/negXf2.zig").__negdf2, linkage);
@@ -132,6 +195,9 @@ comptime {
@export("__aeabi_h2f", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage);
@export("__aeabi_f2h", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage);
+ @export("__aeabi_i2f", @import("compiler_rt/floatsiXf.zig").__floatsisf, linkage);
+ @export("__aeabi_d2f", @import("compiler_rt/truncXfYf2.zig").__truncdfsf2, linkage);
+
@export("__aeabi_fadd", @import("compiler_rt/addXf3.zig").__addsf3, linkage);
@export("__aeabi_dadd", @import("compiler_rt/addXf3.zig").__adddf3, linkage);
@export("__aeabi_fsub", @import("compiler_rt/addXf3.zig").__subsf3, linkage);
@@ -144,26 +210,41 @@ comptime {
@export("__aeabi_fdiv", @import("compiler_rt/divsf3.zig").__divsf3, linkage);
@export("__aeabi_ddiv", @import("compiler_rt/divdf3.zig").__divdf3, linkage);
+
+ @export("__aeabi_fcmpeq", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpeq, linkage);
+ @export("__aeabi_fcmplt", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmplt, linkage);
+ @export("__aeabi_fcmple", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmple, linkage);
+ @export("__aeabi_fcmpge", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpge, linkage);
+ @export("__aeabi_fcmpgt", @import("compiler_rt/arm/aeabi_fcmp.zig").__aeabi_fcmpgt, linkage);
+ @export("__aeabi_fcmpun", @import("compiler_rt/comparesf2.zig").__unordsf2, linkage);
+
+ @export("__aeabi_dcmpeq", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpeq, linkage);
+ @export("__aeabi_dcmplt", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmplt, linkage);
+ @export("__aeabi_dcmple", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmple, linkage);
+ @export("__aeabi_dcmpge", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpge, linkage);
+ @export("__aeabi_dcmpgt", @import("compiler_rt/arm/aeabi_dcmp.zig").__aeabi_dcmpgt, linkage);
+ @export("__aeabi_dcmpun", @import("compiler_rt/comparedf2.zig").__unorddf2, linkage);
}
if (builtin.os == builtin.Os.windows) {
+ if (!builtin.link_libc) {
+ @export("_chkstk", @import("compiler_rt/stack_probe.zig")._chkstk, strong_linkage);
+ @export("__chkstk", @import("compiler_rt/stack_probe.zig").__chkstk, strong_linkage);
+ @export("___chkstk", @import("compiler_rt/stack_probe.zig").___chkstk, strong_linkage);
+ @export("__chkstk_ms", @import("compiler_rt/stack_probe.zig").__chkstk_ms, strong_linkage);
+ @export("___chkstk_ms", @import("compiler_rt/stack_probe.zig").___chkstk_ms, strong_linkage);
+ }
+
switch (builtin.arch) {
builtin.Arch.i386 => {
- if (!builtin.link_libc) {
- @export("_chkstk", _chkstk, strong_linkage);
- @export("__chkstk_ms", __chkstk_ms, linkage);
- }
@export("_aulldiv", @import("compiler_rt/aulldiv.zig")._aulldiv, strong_linkage);
@export("_aullrem", @import("compiler_rt/aullrem.zig")._aullrem, strong_linkage);
},
builtin.Arch.x86_64 => {
- if (!builtin.link_libc) {
- @export("__chkstk", __chkstk, strong_linkage);
- @export("___chkstk_ms", ___chkstk_ms, linkage);
- }
+ // The "ti" functions must use @Vector(2, u64) parameter types to adhere to the ABI
+ // that LLVM expects compiler-rt to have.
@export("__divti3", @import("compiler_rt/divti3.zig").__divti3_windows_x86_64, linkage);
@export("__modti3", @import("compiler_rt/modti3.zig").__modti3_windows_x86_64, linkage);
@export("__multi3", @import("compiler_rt/multi3.zig").__multi3_windows_x86_64, linkage);
- @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4_windows_x86_64, linkage);
@export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, linkage);
@export("__udivmodti4", @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
@export("__umodti3", @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64, linkage);
@@ -174,11 +255,12 @@ comptime {
@export("__divti3", @import("compiler_rt/divti3.zig").__divti3, linkage);
@export("__modti3", @import("compiler_rt/modti3.zig").__modti3, linkage);
@export("__multi3", @import("compiler_rt/multi3.zig").__multi3, linkage);
- @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage);
@export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3, linkage);
@export("__udivmodti4", @import("compiler_rt/udivmodti4.zig").__udivmodti4, linkage);
@export("__umodti3", @import("compiler_rt/umodti3.zig").__umodti3, linkage);
}
+ @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage);
+ @export("__mulodi4", @import("compiler_rt/mulodi4.zig").__mulodi4, linkage);
}
const std = @import("std");
@@ -198,15 +280,49 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn
}
}
-pub fn setXmm0(comptime T: type, value: T) void {
- comptime assert(builtin.arch == builtin.Arch.x86_64);
- const aligned_value: T align(16) = value;
- asm volatile (
- \\movaps (%[ptr]), %%xmm0
- :
- : [ptr] "r" (&aligned_value)
- : "xmm0"
- );
+extern fn __aeabi_unwind_cpp_pr0() void {
+ unreachable;
+}
+extern fn __aeabi_unwind_cpp_pr1() void {
+ unreachable;
+}
+extern fn __aeabi_unwind_cpp_pr2() void {
+ unreachable;
+}
+
+extern fn __divmoddi4(a: i64, b: i64, rem: *i64) i64 {
+ @setRuntimeSafety(is_test);
+
+ const d = __divdi3(a, b);
+ rem.* = a -% (d *% b);
+ return d;
+}
+
+extern fn __divdi3(a: i64, b: i64) i64 {
+ @setRuntimeSafety(is_test);
+
+ // Set aside the sign of the quotient.
+ const sign = @bitCast(u64, (a ^ b) >> 63);
+ // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63).
+ const abs_a = (a ^ (a >> 63)) -% (a >> 63);
+ const abs_b = (b ^ (b >> 63)) -% (b >> 63);
+ // Unsigned division
+ const res = __udivmoddi4(@bitCast(u64, abs_a), @bitCast(u64, abs_b), null);
+ // Apply sign of quotient to result and return.
+ return @bitCast(i64, (res ^ sign) -% sign);
+}
+
+extern fn __moddi3(a: i64, b: i64) i64 {
+ @setRuntimeSafety(is_test);
+
+ // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63).
+ const abs_a = (a ^ (a >> 63)) -% (a >> 63);
+ const abs_b = (b ^ (b >> 63)) -% (b >> 63);
+ // Unsigned division
+ var r: u64 = undefined;
+ _ = __udivmoddi4(@bitCast(u64, abs_a), @bitCast(u64, abs_b), &r);
+ // Apply the sign of the dividend and return.
+ return (@bitCast(i64, r) ^ (a >> 63)) -% (a >> 63);
}
extern fn __udivdi3(a: u64, b: u64) u64 {
@@ -222,14 +338,35 @@ extern fn __umoddi3(a: u64, b: u64) u64 {
return r;
}
-const AeabiUlDivModResult = extern struct {
- quot: u64,
- rem: u64,
-};
-extern fn __aeabi_uldivmod(numerator: u64, denominator: u64) AeabiUlDivModResult {
+extern fn __aeabi_uidivmod(n: u32, d: u32) extern struct{q: u32, r: u32} {
+ @setRuntimeSafety(is_test);
+
+ var result: @typeOf(__aeabi_uidivmod).ReturnType = undefined;
+ result.q = __udivmodsi4(n, d, &result.r);
+ return result;
+}
+
+extern fn __aeabi_uldivmod(n: u64, d: u64) extern struct{q: u64, r: u64} {
+ @setRuntimeSafety(is_test);
+
+ var result: @typeOf(__aeabi_uldivmod).ReturnType = undefined;
+ result.q = __udivmoddi4(n, d, &result.r);
+ return result;
+}
+
+extern fn __aeabi_idivmod(n: i32, d: i32) extern struct{q: i32, r: i32} {
+ @setRuntimeSafety(is_test);
+
+ var result: @typeOf(__aeabi_idivmod).ReturnType = undefined;
+ result.q = __divmodsi4(n, d, &result.r);
+ return result;
+}
+
+extern fn __aeabi_ldivmod(n: i64, d: i64) extern struct{q: i64, r:i64} {
@setRuntimeSafety(is_test);
- var result: AeabiUlDivModResult = undefined;
- result.quot = __udivmoddi4(numerator, denominator, &result.rem);
+
+ var result: @typeOf(__aeabi_ldivmod).ReturnType = undefined;
+ result.q = __divmoddi4(n, d, &result.r);
return result;
}
@@ -253,29 +390,74 @@ const is_arm_arch = switch (builtin.arch) {
const is_arm_32 = is_arm_arch and !is_arm_64;
-const use_thumb_1 = is_arm_32 and switch (builtin.arch.arm) {
- builtin.Arch.Arm32.v6,
- builtin.Arch.Arm32.v6m,
- builtin.Arch.Arm32.v6k,
- builtin.Arch.Arm32.v6t2,
- => true,
- else => false,
-};
+const use_thumb_1 = usesThumb1(builtin.arch);
+
+fn usesThumb1(arch: builtin.Arch) bool {
+ return switch (arch) {
+ .arm => switch (arch.arm) {
+ .v6m => true,
+ else => false,
+ },
+ .armeb => switch (arch.armeb) {
+ .v6m => true,
+ else => false,
+ },
+ .thumb => switch (arch.thumb) {
+ .v5,
+ .v5te,
+ .v4t,
+ .v6,
+ .v6m,
+ .v6k,
+ => true,
+ else => false,
+ },
+ .thumbeb => switch (arch.thumbeb) {
+ .v5,
+ .v5te,
+ .v4t,
+ .v6,
+ .v6m,
+ .v6k,
+ => true,
+ else => false,
+ },
+ else => false,
+ };
+}
-nakedcc fn __aeabi_uidivmod() void {
- @setRuntimeSafety(false);
- asm volatile (
- \\ push { lr }
- \\ sub sp, sp, #4
- \\ mov r2, sp
- \\ bl __udivmodsi4
- \\ ldr r1, [sp]
- \\ add sp, sp, #4
- \\ pop { pc }
- :
- :
- : "r2", "r1"
- );
+test "usesThumb1" {
+ testing.expect(usesThumb1(builtin.Arch{ .arm = .v6m }));
+ testing.expect(!usesThumb1(builtin.Arch{ .arm = .v5 }));
+ //etc.
+
+ testing.expect(usesThumb1(builtin.Arch{ .armeb = .v6m }));
+ testing.expect(!usesThumb1(builtin.Arch{ .armeb = .v5 }));
+ //etc.
+
+ testing.expect(usesThumb1(builtin.Arch{ .thumb = .v5 }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumb = .v5te }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumb = .v4t }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumb = .v6 }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumb = .v6k }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumb = .v6m }));
+ testing.expect(!usesThumb1(builtin.Arch{ .thumb = .v6t2 }));
+ //etc.
+
+ testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v5 }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v5te }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v4t }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v6 }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v6k }));
+ testing.expect(usesThumb1(builtin.Arch{ .thumbeb = .v6m }));
+ testing.expect(!usesThumb1(builtin.Arch{ .thumbeb = .v6t2 }));
+ //etc.
+
+ testing.expect(!usesThumb1(builtin.Arch{ .aarch64 = .v8 }));
+ testing.expect(!usesThumb1(builtin.Arch{ .aarch64_be = .v8 }));
+ testing.expect(!usesThumb1(builtin.Arch.x86_64));
+ testing.expect(!usesThumb1(builtin.Arch.riscv32));
+ //etc.
}
nakedcc fn __aeabi_memcpy() noreturn {
@@ -368,107 +550,12 @@ nakedcc fn __aeabi_memcmp() noreturn {
unreachable;
}
-// _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments,
-// then decrement %esp by %eax. Preserves all registers except %esp and flags.
-// This routine is windows specific
-// http://msdn.microsoft.com/en-us/library/ms648426.aspx
-nakedcc fn _chkstk() align(4) void {
- @setRuntimeSafety(false);
-
- asm volatile (
- \\ push %%ecx
- \\ push %%eax
- \\ cmp $0x1000,%%eax
- \\ lea 12(%%esp),%%ecx
- \\ jb 1f
- \\ 2:
- \\ sub $0x1000,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ sub $0x1000,%%eax
- \\ cmp $0x1000,%%eax
- \\ ja 2b
- \\ 1:
- \\ sub %%eax,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ pop %%eax
- \\ pop %%ecx
- \\ ret
- );
-}
-
-nakedcc fn __chkstk() align(4) void {
- @setRuntimeSafety(false);
-
- asm volatile (
- \\ push %%rcx
- \\ push %%rax
- \\ cmp $0x1000,%%rax
- \\ lea 24(%%rsp),%%rcx
- \\ jb 1f
- \\2:
- \\ sub $0x1000,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ sub $0x1000,%%rax
- \\ cmp $0x1000,%%rax
- \\ ja 2b
- \\1:
- \\ sub %%rax,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ pop %%rax
- \\ pop %%rcx
- \\ ret
- );
-}
-
-// _chkstk routine
-// This routine is windows specific
-// http://msdn.microsoft.com/en-us/library/ms648426.aspx
-nakedcc fn __chkstk_ms() align(4) void {
- @setRuntimeSafety(false);
-
- asm volatile (
- \\ push %%ecx
- \\ push %%eax
- \\ cmp $0x1000,%%eax
- \\ lea 12(%%esp),%%ecx
- \\ jb 1f
- \\ 2:
- \\ sub $0x1000,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ sub $0x1000,%%eax
- \\ cmp $0x1000,%%eax
- \\ ja 2b
- \\ 1:
- \\ sub %%eax,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ pop %%eax
- \\ pop %%ecx
- \\ ret
- );
-}
-
-nakedcc fn ___chkstk_ms() align(4) void {
- @setRuntimeSafety(false);
+extern fn __divmodsi4(a: i32, b: i32, rem: *i32) i32 {
+ @setRuntimeSafety(is_test);
- asm volatile (
- \\ push %%rcx
- \\ push %%rax
- \\ cmp $0x1000,%%rax
- \\ lea 24(%%rsp),%%rcx
- \\ jb 1f
- \\2:
- \\ sub $0x1000,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ sub $0x1000,%%rax
- \\ cmp $0x1000,%%rax
- \\ ja 2b
- \\1:
- \\ sub %%rax,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ pop %%rax
- \\ pop %%rcx
- \\ ret
- );
+ const d = __divsi3(a, b);
+ rem.* = a -% (d * b);
+ return d;
}
extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 {
@@ -479,6 +566,20 @@ extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 {
return d;
}
+extern fn __divsi3(n: i32, d: i32) i32 {
+ @setRuntimeSafety(is_test);
+
+ // Set aside the sign of the quotient.
+ const sign = @bitCast(u32, (n ^ d) >> 31);
+ // Take absolute value of a and b via abs(x) = (x^(x >> 31)) - (x >> 31).
+ const abs_n = (n ^ (n >> 31)) -% (n >> 31);
+ const abs_d = (d ^ (d >> 31)) -% (d >> 31);
+ // abs(a) / abs(b)
+ const res = @bitCast(u32, abs_n) / @bitCast(u32, abs_d);
+ // Apply sign of quotient to result and return.
+ return @bitCast(i32, (res ^ sign) -% sign);
+}
+
extern fn __udivsi3(n: u32, d: u32) u32 {
@setRuntimeSafety(is_test);
@@ -520,6 +621,18 @@ extern fn __udivsi3(n: u32, d: u32) u32 {
return q;
}
+extern fn __modsi3(n: i32, d: i32) i32 {
+ @setRuntimeSafety(is_test);
+
+ return n -% __divsi3(n, d) *% d;
+}
+
+extern fn __umodsi3(n: u32, d: u32) u32 {
+ @setRuntimeSafety(is_test);
+
+ return n -% __udivsi3(n, d) *% d;
+}
+
test "test_umoddi3" {
test_one_umoddi3(0, 1, 0);
test_one_umoddi3(2, 1, 0);
@@ -1206,3 +1319,279 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void {
const q: u32 = __udivsi3(a, b);
testing.expect(q == expected_q);
}
+
+test "test_divsi3" {
+ const cases = [][3]i32{
+ []i32{ 0, 1, 0 },
+ []i32{ 0, -1, 0 },
+ []i32{ 2, 1, 2 },
+ []i32{ 2, -1, -2 },
+ []i32{ -2, 1, -2 },
+ []i32{ -2, -1, 2 },
+
+ []i32{ @bitCast(i32, u32(0x80000000)), 1, @bitCast(i32, u32(0x80000000)) },
+ []i32{ @bitCast(i32, u32(0x80000000)), -1, @bitCast(i32, u32(0x80000000)) },
+ []i32{ @bitCast(i32, u32(0x80000000)), -2, 0x40000000 },
+ []i32{ @bitCast(i32, u32(0x80000000)), 2, @bitCast(i32, u32(0xC0000000)) },
+ };
+
+ for (cases) |case| {
+ test_one_divsi3(case[0], case[1], case[2]);
+ }
+}
+
+fn test_one_divsi3(a: i32, b: i32, expected_q: i32) void {
+ const q: i32 = __divsi3(a, b);
+ testing.expect(q == expected_q);
+}
+
+test "test_divmodsi4" {
+ const cases = [][4]i32{
+ []i32{ 0, 1, 0, 0 },
+ []i32{ 0, -1, 0, 0 },
+ []i32{ 2, 1, 2, 0 },
+ []i32{ 2, -1, -2, 0 },
+ []i32{ -2, 1, -2, 0 },
+ []i32{ -2, -1, 2, 0 },
+ []i32{ 7, 5, 1, 2 },
+ []i32{ -7, 5, -1, -2 },
+ []i32{ 19, 5, 3, 4 },
+ []i32{ 19, -5, -3, 4 },
+
+ []i32{ @bitCast(i32, u32(0x80000000)), 8, @bitCast(i32, u32(0xf0000000)), 0 },
+ []i32{ @bitCast(i32, u32(0x80000007)), 8, @bitCast(i32, u32(0xf0000001)), -1 },
+ };
+
+ for (cases) |case| {
+ test_one_divmodsi4(case[0], case[1], case[2], case[3]);
+ }
+}
+
+fn test_one_divmodsi4(a: i32, b: i32, expected_q: i32, expected_r: i32) void {
+ var r: i32 = undefined;
+ const q: i32 = __divmodsi4(a, b, &r);
+ testing.expect(q == expected_q and r == expected_r);
+}
+
+test "test_divdi3" {
+ const cases = [][3]i64{
+ []i64{ 0, 1, 0 },
+ []i64{ 0, -1, 0 },
+ []i64{ 2, 1, 2 },
+ []i64{ 2, -1, -2 },
+ []i64{ -2, 1, -2 },
+ []i64{ -2, -1, 2 },
+
+ []i64{ @bitCast(i64, u64(0x8000000000000000)), 1, @bitCast(i64, u64(0x8000000000000000)) },
+ []i64{ @bitCast(i64, u64(0x8000000000000000)), -1, @bitCast(i64, u64(0x8000000000000000)) },
+ []i64{ @bitCast(i64, u64(0x8000000000000000)), -2, 0x4000000000000000 },
+ []i64{ @bitCast(i64, u64(0x8000000000000000)), 2, @bitCast(i64, u64(0xC000000000000000)) },
+ };
+
+ for (cases) |case| {
+ test_one_divdi3(case[0], case[1], case[2]);
+ }
+}
+
+fn test_one_divdi3(a: i64, b: i64, expected_q: i64) void {
+ const q: i64 = __divdi3(a, b);
+ testing.expect(q == expected_q);
+}
+
+test "test_moddi3" {
+ const cases = [][3]i64{
+ []i64{ 0, 1, 0 },
+ []i64{ 0, -1, 0 },
+ []i64{ 5, 3, 2 },
+ []i64{ 5, -3, 2 },
+ []i64{ -5, 3, -2 },
+ []i64{ -5, -3, -2 },
+
+ []i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), 1, 0 },
+ []i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), -1, 0 },
+ []i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), 2, 0 },
+ []i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), -2, 0 },
+ []i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), 3, -2 },
+ []i64{ @bitCast(i64, @intCast(u64, 0x8000000000000000)), -3, -2 },
+ };
+
+ for (cases) |case| {
+ test_one_moddi3(case[0], case[1], case[2]);
+ }
+}
+
+fn test_one_moddi3(a: i64, b: i64, expected_r: i64) void {
+ const r: i64 = __moddi3(a, b);
+ testing.expect(r == expected_r);
+}
+
+test "test_modsi3" {
+ const cases = [][3]i32{
+ []i32{ 0, 1, 0 },
+ []i32{ 0, -1, 0 },
+ []i32{ 5, 3, 2 },
+ []i32{ 5, -3, 2 },
+ []i32{ -5, 3, -2 },
+ []i32{ -5, -3, -2 },
+ []i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 1, 0x0 },
+ []i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 2, 0x0 },
+ []i32{ @bitCast(i32, @intCast(u32, 0x80000000)), -2, 0x0 },
+ []i32{ @bitCast(i32, @intCast(u32, 0x80000000)), 3, -2 },
+ []i32{ @bitCast(i32, @intCast(u32, 0x80000000)), -3, -2 },
+ };
+
+ for (cases) |case| {
+ test_one_modsi3(case[0], case[1], case[2]);
+ }
+}
+
+fn test_one_modsi3(a: i32, b: i32, expected_r: i32) void {
+ const r: i32 = __modsi3(a, b);
+ testing.expect(r == expected_r);
+}
+
+test "test_umodsi3" {
+ const cases = [][3]u32{
+ []u32{ 0x00000000, 0x00000001, 0x00000000 },
+ []u32{ 0x00000000, 0x00000002, 0x00000000 },
+ []u32{ 0x00000000, 0x00000003, 0x00000000 },
+ []u32{ 0x00000000, 0x00000010, 0x00000000 },
+ []u32{ 0x00000000, 0x078644FA, 0x00000000 },
+ []u32{ 0x00000000, 0x0747AE14, 0x00000000 },
+ []u32{ 0x00000000, 0x7FFFFFFF, 0x00000000 },
+ []u32{ 0x00000000, 0x80000000, 0x00000000 },
+ []u32{ 0x00000000, 0xFFFFFFFD, 0x00000000 },
+ []u32{ 0x00000000, 0xFFFFFFFE, 0x00000000 },
+ []u32{ 0x00000000, 0xFFFFFFFF, 0x00000000 },
+ []u32{ 0x00000001, 0x00000001, 0x00000000 },
+ []u32{ 0x00000001, 0x00000002, 0x00000001 },
+ []u32{ 0x00000001, 0x00000003, 0x00000001 },
+ []u32{ 0x00000001, 0x00000010, 0x00000001 },
+ []u32{ 0x00000001, 0x078644FA, 0x00000001 },
+ []u32{ 0x00000001, 0x0747AE14, 0x00000001 },
+ []u32{ 0x00000001, 0x7FFFFFFF, 0x00000001 },
+ []u32{ 0x00000001, 0x80000000, 0x00000001 },
+ []u32{ 0x00000001, 0xFFFFFFFD, 0x00000001 },
+ []u32{ 0x00000001, 0xFFFFFFFE, 0x00000001 },
+ []u32{ 0x00000001, 0xFFFFFFFF, 0x00000001 },
+ []u32{ 0x00000002, 0x00000001, 0x00000000 },
+ []u32{ 0x00000002, 0x00000002, 0x00000000 },
+ []u32{ 0x00000002, 0x00000003, 0x00000002 },
+ []u32{ 0x00000002, 0x00000010, 0x00000002 },
+ []u32{ 0x00000002, 0x078644FA, 0x00000002 },
+ []u32{ 0x00000002, 0x0747AE14, 0x00000002 },
+ []u32{ 0x00000002, 0x7FFFFFFF, 0x00000002 },
+ []u32{ 0x00000002, 0x80000000, 0x00000002 },
+ []u32{ 0x00000002, 0xFFFFFFFD, 0x00000002 },
+ []u32{ 0x00000002, 0xFFFFFFFE, 0x00000002 },
+ []u32{ 0x00000002, 0xFFFFFFFF, 0x00000002 },
+ []u32{ 0x00000003, 0x00000001, 0x00000000 },
+ []u32{ 0x00000003, 0x00000002, 0x00000001 },
+ []u32{ 0x00000003, 0x00000003, 0x00000000 },
+ []u32{ 0x00000003, 0x00000010, 0x00000003 },
+ []u32{ 0x00000003, 0x078644FA, 0x00000003 },
+ []u32{ 0x00000003, 0x0747AE14, 0x00000003 },
+ []u32{ 0x00000003, 0x7FFFFFFF, 0x00000003 },
+ []u32{ 0x00000003, 0x80000000, 0x00000003 },
+ []u32{ 0x00000003, 0xFFFFFFFD, 0x00000003 },
+ []u32{ 0x00000003, 0xFFFFFFFE, 0x00000003 },
+ []u32{ 0x00000003, 0xFFFFFFFF, 0x00000003 },
+ []u32{ 0x00000010, 0x00000001, 0x00000000 },
+ []u32{ 0x00000010, 0x00000002, 0x00000000 },
+ []u32{ 0x00000010, 0x00000003, 0x00000001 },
+ []u32{ 0x00000010, 0x00000010, 0x00000000 },
+ []u32{ 0x00000010, 0x078644FA, 0x00000010 },
+ []u32{ 0x00000010, 0x0747AE14, 0x00000010 },
+ []u32{ 0x00000010, 0x7FFFFFFF, 0x00000010 },
+ []u32{ 0x00000010, 0x80000000, 0x00000010 },
+ []u32{ 0x00000010, 0xFFFFFFFD, 0x00000010 },
+ []u32{ 0x00000010, 0xFFFFFFFE, 0x00000010 },
+ []u32{ 0x00000010, 0xFFFFFFFF, 0x00000010 },
+ []u32{ 0x078644FA, 0x00000001, 0x00000000 },
+ []u32{ 0x078644FA, 0x00000002, 0x00000000 },
+ []u32{ 0x078644FA, 0x00000003, 0x00000000 },
+ []u32{ 0x078644FA, 0x00000010, 0x0000000A },
+ []u32{ 0x078644FA, 0x078644FA, 0x00000000 },
+ []u32{ 0x078644FA, 0x0747AE14, 0x003E96E6 },
+ []u32{ 0x078644FA, 0x7FFFFFFF, 0x078644FA },
+ []u32{ 0x078644FA, 0x80000000, 0x078644FA },
+ []u32{ 0x078644FA, 0xFFFFFFFD, 0x078644FA },
+ []u32{ 0x078644FA, 0xFFFFFFFE, 0x078644FA },
+ []u32{ 0x078644FA, 0xFFFFFFFF, 0x078644FA },
+ []u32{ 0x0747AE14, 0x00000001, 0x00000000 },
+ []u32{ 0x0747AE14, 0x00000002, 0x00000000 },
+ []u32{ 0x0747AE14, 0x00000003, 0x00000002 },
+ []u32{ 0x0747AE14, 0x00000010, 0x00000004 },
+ []u32{ 0x0747AE14, 0x078644FA, 0x0747AE14 },
+ []u32{ 0x0747AE14, 0x0747AE14, 0x00000000 },
+ []u32{ 0x0747AE14, 0x7FFFFFFF, 0x0747AE14 },
+ []u32{ 0x0747AE14, 0x80000000, 0x0747AE14 },
+ []u32{ 0x0747AE14, 0xFFFFFFFD, 0x0747AE14 },
+ []u32{ 0x0747AE14, 0xFFFFFFFE, 0x0747AE14 },
+ []u32{ 0x0747AE14, 0xFFFFFFFF, 0x0747AE14 },
+ []u32{ 0x7FFFFFFF, 0x00000001, 0x00000000 },
+ []u32{ 0x7FFFFFFF, 0x00000002, 0x00000001 },
+ []u32{ 0x7FFFFFFF, 0x00000003, 0x00000001 },
+ []u32{ 0x7FFFFFFF, 0x00000010, 0x0000000F },
+ []u32{ 0x7FFFFFFF, 0x078644FA, 0x00156B65 },
+ []u32{ 0x7FFFFFFF, 0x0747AE14, 0x043D70AB },
+ []u32{ 0x7FFFFFFF, 0x7FFFFFFF, 0x00000000 },
+ []u32{ 0x7FFFFFFF, 0x80000000, 0x7FFFFFFF },
+ []u32{ 0x7FFFFFFF, 0xFFFFFFFD, 0x7FFFFFFF },
+ []u32{ 0x7FFFFFFF, 0xFFFFFFFE, 0x7FFFFFFF },
+ []u32{ 0x7FFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF },
+ []u32{ 0x80000000, 0x00000001, 0x00000000 },
+ []u32{ 0x80000000, 0x00000002, 0x00000000 },
+ []u32{ 0x80000000, 0x00000003, 0x00000002 },
+ []u32{ 0x80000000, 0x00000010, 0x00000000 },
+ []u32{ 0x80000000, 0x078644FA, 0x00156B66 },
+ []u32{ 0x80000000, 0x0747AE14, 0x043D70AC },
+ []u32{ 0x80000000, 0x7FFFFFFF, 0x00000001 },
+ []u32{ 0x80000000, 0x80000000, 0x00000000 },
+ []u32{ 0x80000000, 0xFFFFFFFD, 0x80000000 },
+ []u32{ 0x80000000, 0xFFFFFFFE, 0x80000000 },
+ []u32{ 0x80000000, 0xFFFFFFFF, 0x80000000 },
+ []u32{ 0xFFFFFFFD, 0x00000001, 0x00000000 },
+ []u32{ 0xFFFFFFFD, 0x00000002, 0x00000001 },
+ []u32{ 0xFFFFFFFD, 0x00000003, 0x00000001 },
+ []u32{ 0xFFFFFFFD, 0x00000010, 0x0000000D },
+ []u32{ 0xFFFFFFFD, 0x078644FA, 0x002AD6C9 },
+ []u32{ 0xFFFFFFFD, 0x0747AE14, 0x01333341 },
+ []u32{ 0xFFFFFFFD, 0x7FFFFFFF, 0x7FFFFFFE },
+ []u32{ 0xFFFFFFFD, 0x80000000, 0x7FFFFFFD },
+ []u32{ 0xFFFFFFFD, 0xFFFFFFFD, 0x00000000 },
+ []u32{ 0xFFFFFFFD, 0xFFFFFFFE, 0xFFFFFFFD },
+ []u32{ 0xFFFFFFFD, 0xFFFFFFFF, 0xFFFFFFFD },
+ []u32{ 0xFFFFFFFE, 0x00000001, 0x00000000 },
+ []u32{ 0xFFFFFFFE, 0x00000002, 0x00000000 },
+ []u32{ 0xFFFFFFFE, 0x00000003, 0x00000002 },
+ []u32{ 0xFFFFFFFE, 0x00000010, 0x0000000E },
+ []u32{ 0xFFFFFFFE, 0x078644FA, 0x002AD6CA },
+ []u32{ 0xFFFFFFFE, 0x0747AE14, 0x01333342 },
+ []u32{ 0xFFFFFFFE, 0x7FFFFFFF, 0x00000000 },
+ []u32{ 0xFFFFFFFE, 0x80000000, 0x7FFFFFFE },
+ []u32{ 0xFFFFFFFE, 0xFFFFFFFD, 0x00000001 },
+ []u32{ 0xFFFFFFFE, 0xFFFFFFFE, 0x00000000 },
+ []u32{ 0xFFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFE },
+ []u32{ 0xFFFFFFFF, 0x00000001, 0x00000000 },
+ []u32{ 0xFFFFFFFF, 0x00000002, 0x00000001 },
+ []u32{ 0xFFFFFFFF, 0x00000003, 0x00000000 },
+ []u32{ 0xFFFFFFFF, 0x00000010, 0x0000000F },
+ []u32{ 0xFFFFFFFF, 0x078644FA, 0x002AD6CB },
+ []u32{ 0xFFFFFFFF, 0x0747AE14, 0x01333343 },
+ []u32{ 0xFFFFFFFF, 0x7FFFFFFF, 0x00000001 },
+ []u32{ 0xFFFFFFFF, 0x80000000, 0x7FFFFFFF },
+ []u32{ 0xFFFFFFFF, 0xFFFFFFFD, 0x00000002 },
+ []u32{ 0xFFFFFFFF, 0xFFFFFFFE, 0x00000001 },
+ []u32{ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000 },
+ };
+
+ for (cases) |case| {
+ test_one_umodsi3(case[0], case[1], case[2]);
+ }
+}
+
+fn test_one_umodsi3(a: u32, b: u32, expected_r: u32) void {
+ const r: u32 = __umodsi3(a, b);
+ testing.expect(r == expected_r);
+}
diff --git a/std/special/compiler_rt/addXf3.zig b/std/special/compiler_rt/addXf3.zig
index 3a70461eca..5852f3e50d 100644
--- a/std/special/compiler_rt/addXf3.zig
+++ b/std/special/compiler_rt/addXf3.zig
@@ -78,8 +78,8 @@ fn addXf3(comptime T: type, a: T, b: T) T {
const infRep = @bitCast(Z, std.math.inf(T));
// Detect if a or b is zero, infinity, or NaN.
- if (aAbs - Z(1) >= infRep - Z(1) or
- bAbs - Z(1) >= infRep - Z(1))
+ if (aAbs -% Z(1) >= infRep - Z(1) or
+ bAbs -% Z(1) >= infRep - Z(1))
{
// NaN + anything = qNaN
if (aAbs > infRep) return @bitCast(T, @bitCast(Z, a) | quietBit);
diff --git a/std/special/compiler_rt/arm/aeabi_dcmp.zig b/std/special/compiler_rt/arm/aeabi_dcmp.zig
new file mode 100644
index 0000000000..a51d9854ce
--- /dev/null
+++ b/std/special/compiler_rt/arm/aeabi_dcmp.zig
@@ -0,0 +1,108 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_dcmp.S
+
+const compiler_rt_armhf_target = false; // TODO
+
+const ConditionalOperator = enum {
+ Eq,
+ Lt,
+ Le,
+ Ge,
+ Gt,
+};
+
+pub nakedcc fn __aeabi_dcmpeq() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_dcmp(.Eq);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_dcmplt() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_dcmp(.Lt);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_dcmple() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_dcmp(.Le);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_dcmpge() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_dcmp(.Ge);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_dcmpgt() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_dcmp(.Gt);
+ unreachable;
+}
+
+inline fn convert_dcmp_args_to_df2_args() void {
+ asm volatile (
+ \\ vmov d0, r0, r1
+ \\ vmov d1, r2, r3
+ );
+}
+
+inline fn aeabi_dcmp(comptime cond: ConditionalOperator) void {
+ @setRuntimeSafety(false);
+ asm volatile (
+ \\ push { r4, lr }
+ );
+
+ if (compiler_rt_armhf_target) {
+ convert_dcmp_args_to_df2_args();
+ }
+
+ switch (cond) {
+ .Eq => asm volatile (
+ \\ bl __eqdf2
+ \\ cmp r0, #0
+ \\ beq 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Lt => asm volatile (
+ \\ bl __ltdf2
+ \\ cmp r0, #0
+ \\ blt 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Le => asm volatile (
+ \\ bl __ledf2
+ \\ cmp r0, #0
+ \\ ble 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Ge => asm volatile (
+ \\ bl __ltdf2
+ \\ cmp r0, #0
+ \\ bge 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Gt => asm volatile (
+ \\ bl __gtdf2
+ \\ cmp r0, #0
+ \\ bgt 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ }
+ asm volatile (
+ \\ movs r0, #1
+ \\ pop { r4, pc }
+ );
+}
diff --git a/std/special/compiler_rt/arm/aeabi_fcmp.zig b/std/special/compiler_rt/arm/aeabi_fcmp.zig
new file mode 100644
index 0000000000..f82dd25270
--- /dev/null
+++ b/std/special/compiler_rt/arm/aeabi_fcmp.zig
@@ -0,0 +1,108 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/arm/aeabi_fcmp.S
+
+const compiler_rt_armhf_target = false; // TODO
+
+const ConditionalOperator = enum {
+ Eq,
+ Lt,
+ Le,
+ Ge,
+ Gt,
+};
+
+pub nakedcc fn __aeabi_fcmpeq() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_fcmp(.Eq);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_fcmplt() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_fcmp(.Lt);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_fcmple() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_fcmp(.Le);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_fcmpge() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_fcmp(.Ge);
+ unreachable;
+}
+
+pub nakedcc fn __aeabi_fcmpgt() noreturn {
+ @setRuntimeSafety(false);
+ aeabi_fcmp(.Gt);
+ unreachable;
+}
+
+inline fn convert_fcmp_args_to_sf2_args() void {
+ asm volatile (
+ \\ vmov s0, r0
+ \\ vmov s1, r1
+ );
+}
+
+inline fn aeabi_fcmp(comptime cond: ConditionalOperator) void {
+ @setRuntimeSafety(false);
+ asm volatile (
+ \\ push { r4, lr }
+ );
+
+ if (compiler_rt_armhf_target) {
+ convert_fcmp_args_to_sf2_args();
+ }
+
+ switch (cond) {
+ .Eq => asm volatile (
+ \\ bl __eqsf2
+ \\ cmp r0, #0
+ \\ beq 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Lt => asm volatile (
+ \\ bl __ltsf2
+ \\ cmp r0, #0
+ \\ blt 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Le => asm volatile (
+ \\ bl __lesf2
+ \\ cmp r0, #0
+ \\ ble 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Ge => asm volatile (
+ \\ bl __ltsf2
+ \\ cmp r0, #0
+ \\ bge 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ .Gt => asm volatile (
+ \\ bl __gtsf2
+ \\ cmp r0, #0
+ \\ bgt 1f
+ \\ movs r0, #0
+ \\ pop { r4, pc }
+ \\ 1:
+ ),
+ }
+ asm volatile (
+ \\ movs r0, #1
+ \\ pop { r4, pc }
+ );
+}
diff --git a/std/special/compiler_rt/ashlti3.zig b/std/special/compiler_rt/ashlti3.zig
new file mode 100644
index 0000000000..65b23f22e5
--- /dev/null
+++ b/std/special/compiler_rt/ashlti3.zig
@@ -0,0 +1,41 @@
+const builtin = @import("builtin");
+const compiler_rt = @import("../compiler_rt.zig");
+
+pub extern fn __ashlti3(a: i128, b: i32) i128 {
+ var input = twords{ .all = a };
+ var result: twords = undefined;
+
+ if (b > 63) {
+ // 64 <= b < 128
+ result.s.low = 0;
+ result.s.high = input.s.low << @intCast(u6, b - 64);
+ } else {
+ // 0 <= b < 64
+ if (b == 0) return a;
+ result.s.low = input.s.low << @intCast(u6, b);
+ result.s.high = input.s.low >> @intCast(u6, 64 - b);
+ result.s.high |= input.s.high << @intCast(u6, b);
+ }
+
+ return result.all;
+}
+
+const twords = extern union {
+ all: i128,
+ s: S,
+
+ const S = if (builtin.endian == builtin.Endian.Little)
+ struct {
+ low: u64,
+ high: u64,
+ }
+ else
+ struct {
+ high: u64,
+ low: u64,
+ };
+};
+
+test "import ashlti3" {
+ _ = @import("ashlti3_test.zig");
+}
diff --git a/std/special/compiler_rt/ashlti3_test.zig b/std/special/compiler_rt/ashlti3_test.zig
new file mode 100644
index 0000000000..4ba21c138e
--- /dev/null
+++ b/std/special/compiler_rt/ashlti3_test.zig
@@ -0,0 +1,46 @@
+const __ashlti3 = @import("ashlti3.zig").__ashlti3;
+const testing = @import("std").testing;
+
+fn test__ashlti3(a: i128, b: i32, expected: i128) void {
+ const x = __ashlti3(a, b);
+ testing.expect(x == expected);
+}
+
+test "ashlti3" {
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642BFDB97530ECA8642A)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C857FB72EA61D950C854)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190AFF6E5D4C3B2A190A8)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xEDCBA9876543215FEDCBA98765432150)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x876543215FEDCBA98765432150000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x0ECA8642BFDB97530ECA8642A0000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x1D950C857FB72EA61D950C8540000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x3B2A190AFF6E5D4C3B2A190A80000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x76543215FEDCBA987654321500000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xECA8642BFDB97530ECA8642A00000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xD950C857FB72EA61D950C85400000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xB2A190AFF6E5D4C3B2A190A800000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x6543215FEDCBA9876543215000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x5FEDCBA9876543215000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xBFDB97530ECA8642A000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x7FB72EA61D950C854000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190A8000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFEDCBA98765432150000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFDB97530ECA8642A0000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFB72EA61D950C8540000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xF6E5D4C3B2A190A80000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xEDCBA987654321500000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x87654321500000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x0ECA8642A00000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x1D950C85400000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x3B2A190A800000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x76543215000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xECA8642A000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xD950C854000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xB2A190A8000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x65432150000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x50000000000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xA0000000000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x40000000000000000000000000000000)));
+ test__ashlti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x80000000000000000000000000000000)));
+}
diff --git a/std/special/compiler_rt/ashrti3.zig b/std/special/compiler_rt/ashrti3.zig
new file mode 100644
index 0000000000..40ee89c3c4
--- /dev/null
+++ b/std/special/compiler_rt/ashrti3.zig
@@ -0,0 +1,42 @@
+const builtin = @import("builtin");
+const compiler_rt = @import("../compiler_rt.zig");
+
+pub extern fn __ashrti3(a: i128, b: i32) i128 {
+ var input = twords{ .all = a };
+ var result: twords = undefined;
+
+ if (b > 63) {
+ // 64 <= b < 128
+ result.s.low = input.s.high >> @intCast(u6, b - 64);
+ result.s.high = input.s.high >> 63;
+ } else {
+ // 0 <= b < 64
+ if (b == 0) return a;
+ result.s.low = input.s.high << @intCast(u6, 64 - b);
+ // Avoid sign-extension here
+ result.s.low |= @bitCast(i64, @bitCast(u64, input.s.low) >> @intCast(u6, b));
+ result.s.high = input.s.high >> @intCast(u6, b);
+ }
+
+ return result.all;
+}
+
+const twords = extern union {
+ all: i128,
+ s: S,
+
+ const S = if (builtin.endian == builtin.Endian.Little)
+ struct {
+ low: i64,
+ high: i64,
+ }
+ else
+ struct {
+ high: i64,
+ low: i64,
+ };
+};
+
+test "import ashrti3" {
+ _ = @import("ashrti3_test.zig");
+}
diff --git a/std/special/compiler_rt/ashrti3_test.zig b/std/special/compiler_rt/ashrti3_test.zig
new file mode 100644
index 0000000000..04ae5abd82
--- /dev/null
+++ b/std/special/compiler_rt/ashrti3_test.zig
@@ -0,0 +1,58 @@
+const __ashrti3 = @import("ashrti3.zig").__ashrti3;
+const testing = @import("std").testing;
+
+fn test__ashrti3(a: i128, b: i32, expected: i128) void {
+ const x = __ashrti3(a, b);
+ // @import("std").debug.warn("got 0x{x}\nexp 0x{x}\n", @truncate(u64,
+// @bitCast(u128, x) >> 64), @truncate(u64, @bitCast(u128, expected)) >> 64);
+ testing.expect(x == expected);
+}
+
+test "ashrti3" {
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0xFF6E5D4C3B2A190AFF6E5D4C3B2A190A)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0xFFB72EA61D950C857FB72EA61D950C85)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0xFFDB97530ECA8642BFDB97530ECA8642)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0xFFEDCBA9876543215FEDCBA987654321)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0xFFFFFFFFEDCBA9876543215FEDCBA987)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0xFFFFFFFFF6E5D4C3B2A190AFF6E5D4C3)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFB72EA61D950C857FB72EA61)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFDB97530ECA8642BFDB97530)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFEDCBA9876543215FEDCBA98)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFF6E5D4C3B2A190AFF6E5D4C)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFB72EA61D950C857FB72EA6)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFDB97530ECA8642BFDB9753)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFEDCBA9876543215FEDCBA9)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFEDCBA9876543215F)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFF6E5D4C3B2A190AF)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFB72EA61D950C857)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFDB97530ECA8642B)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFEDCBA9876543215)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFF6E5D4C3B2A190A)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFB72EA61D950C85)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFDB97530ECA8642)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFEDCBA987654321)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFEDCBA987)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C3)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFB72EA61)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFDB97530)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA98)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFF6E5D4C)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFB72EA6)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFDB9753)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFEDCBA9)));
+
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
+ test__ashrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)));
+}
diff --git a/std/special/compiler_rt/comparedf2.zig b/std/special/compiler_rt/comparedf2.zig
new file mode 100644
index 0000000000..f97e2474be
--- /dev/null
+++ b/std/special/compiler_rt/comparedf2.zig
@@ -0,0 +1,122 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/comparedf2.c
+
+const std = @import("std");
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const fp_t = f64;
+const rep_t = u64;
+const srep_t = i64;
+
+const typeWidth = rep_t.bit_count;
+const significandBits = std.math.floatMantissaBits(fp_t);
+const exponentBits = std.math.floatExponentBits(fp_t);
+const signBit = (rep_t(1) << (significandBits + exponentBits));
+const absMask = signBit - 1;
+const implicitBit = rep_t(1) << significandBits;
+const significandMask = implicitBit - 1;
+const exponentMask = absMask ^ significandMask;
+const infRep = @bitCast(rep_t, std.math.inf(fp_t));
+
+// TODO https://github.com/ziglang/zig/issues/641
+// and then make the return types of some of these functions the enum instead of c_int
+const LE_LESS = c_int(-1);
+const LE_EQUAL = c_int(0);
+const LE_GREATER = c_int(1);
+const LE_UNORDERED = c_int(1);
+
+pub extern fn __ledf2(a: fp_t, b: fp_t) c_int {
+ @setRuntimeSafety(is_test);
+ const aInt: srep_t = @bitCast(srep_t, a);
+ const bInt: srep_t = @bitCast(srep_t, b);
+ const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask;
+ const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask;
+
+ // If either a or b is NaN, they are unordered.
+ if (aAbs > infRep or bAbs > infRep) return LE_UNORDERED;
+
+ // If a and b are both zeros, they are equal.
+ if ((aAbs | bAbs) == 0) return LE_EQUAL;
+
+ // If at least one of a and b is positive, we get the same result comparing
+ // a and b as signed integers as we would with a fp_ting-point compare.
+ if ((aInt & bInt) >= 0) {
+ if (aInt < bInt) {
+ return LE_LESS;
+ } else if (aInt == bInt) {
+ return LE_EQUAL;
+ } else return LE_GREATER;
+ }
+
+ // Otherwise, both are negative, so we need to flip the sense of the
+ // comparison to get the correct result. (This assumes a twos- or ones-
+ // complement integer representation; if integers are represented in a
+ // sign-magnitude representation, then this flip is incorrect).
+ else {
+ if (aInt > bInt) {
+ return LE_LESS;
+ } else if (aInt == bInt) {
+ return LE_EQUAL;
+ } else return LE_GREATER;
+ }
+}
+
+// TODO https://github.com/ziglang/zig/issues/641
+// and then make the return types of some of these functions the enum instead of c_int
+const GE_LESS = c_int(-1);
+const GE_EQUAL = c_int(0);
+const GE_GREATER = c_int(1);
+const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED
+
+pub extern fn __gedf2(a: fp_t, b: fp_t) c_int {
+ @setRuntimeSafety(is_test);
+ const aInt: srep_t = @bitCast(srep_t, a);
+ const bInt: srep_t = @bitCast(srep_t, b);
+ const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask;
+ const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask;
+
+ if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED;
+ if ((aAbs | bAbs) == 0) return GE_EQUAL;
+ if ((aInt & bInt) >= 0) {
+ if (aInt < bInt) {
+ return GE_LESS;
+ } else if (aInt == bInt) {
+ return GE_EQUAL;
+ } else return GE_GREATER;
+ } else {
+ if (aInt > bInt) {
+ return GE_LESS;
+ } else if (aInt == bInt) {
+ return GE_EQUAL;
+ } else return GE_GREATER;
+ }
+}
+
+pub extern fn __unorddf2(a: fp_t, b: fp_t) c_int {
+ @setRuntimeSafety(is_test);
+ const aAbs: rep_t = @bitCast(rep_t, a) & absMask;
+ const bAbs: rep_t = @bitCast(rep_t, b) & absMask;
+ return @boolToInt(aAbs > infRep or bAbs > infRep);
+}
+
+pub extern fn __eqdf2(a: fp_t, b: fp_t) c_int {
+ return __ledf2(a, b);
+}
+
+pub extern fn __ltdf2(a: fp_t, b: fp_t) c_int {
+ return __ledf2(a, b);
+}
+
+pub extern fn __nedf2(a: fp_t, b: fp_t) c_int {
+ return __ledf2(a, b);
+}
+
+pub extern fn __gtdf2(a: fp_t, b: fp_t) c_int {
+ return __gedf2(a, b);
+}
+
+test "import comparedf2" {
+ _ = @import("comparedf2_test.zig");
+}
diff --git a/std/special/compiler_rt/comparedf2_test.zig b/std/special/compiler_rt/comparedf2_test.zig
new file mode 100644
index 0000000000..9c08389994
--- /dev/null
+++ b/std/special/compiler_rt/comparedf2_test.zig
@@ -0,0 +1,101 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/comparedf2_test.c
+
+const std = @import("std");
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const comparedf2 = @import("comparedf2.zig");
+
+const TestVector = struct {
+ a: f64,
+ b: f64,
+ eqReference: c_int,
+ geReference: c_int,
+ gtReference: c_int,
+ leReference: c_int,
+ ltReference: c_int,
+ neReference: c_int,
+ unReference: c_int,
+};
+
+fn test__cmpdf2(vector: TestVector) bool {
+ if (comparedf2.__eqdf2(vector.a, vector.b) != vector.eqReference) {
+ return false;
+ }
+ if (comparedf2.__gedf2(vector.a, vector.b) != vector.geReference) {
+ return false;
+ }
+ if (comparedf2.__gtdf2(vector.a, vector.b) != vector.gtReference) {
+ return false;
+ }
+ if (comparedf2.__ledf2(vector.a, vector.b) != vector.leReference) {
+ return false;
+ }
+ if (comparedf2.__ltdf2(vector.a, vector.b) != vector.ltReference) {
+ return false;
+ }
+ if (comparedf2.__nedf2(vector.a, vector.b) != vector.neReference) {
+ return false;
+ }
+ if (comparedf2.__unorddf2(vector.a, vector.b) != vector.unReference) {
+ return false;
+ }
+ return true;
+}
+
+const arguments = []f64{
+ std.math.nan(f64),
+ -std.math.inf(f64),
+ -0x1.fffffffffffffp1023,
+ -0x1.0000000000001p0 - 0x1.0000000000000p0,
+ -0x1.fffffffffffffp-1,
+ -0x1.0000000000000p-1022,
+ -0x0.fffffffffffffp-1022,
+ -0x0.0000000000001p-1022,
+ -0.0,
+ 0.0,
+ 0x0.0000000000001p-1022,
+ 0x0.fffffffffffffp-1022,
+ 0x1.0000000000000p-1022,
+ 0x1.fffffffffffffp-1,
+ 0x1.0000000000000p0,
+ 0x1.0000000000001p0,
+ 0x1.fffffffffffffp1023,
+ std.math.inf(f64),
+};
+
+fn generateVector(comptime a: f64, comptime b: f64) TestVector {
+ const leResult = if (a < b) -1 else if (a == b) 0 else 1;
+ const geResult = if (a > b) 1 else if (a == b) 0 else -1;
+ const unResult = if (a != a or b != b) 1 else 0;
+ return TestVector{
+ .a = a,
+ .b = b,
+ .eqReference = leResult,
+ .geReference = geResult,
+ .gtReference = geResult,
+ .leReference = leResult,
+ .ltReference = leResult,
+ .neReference = leResult,
+ .unReference = unResult,
+ };
+}
+
+const test_vectors = init: {
+ @setEvalBranchQuota(10000);
+ var vectors: [arguments.len * arguments.len]TestVector = undefined;
+ for (arguments[0..]) |arg_i, i| {
+ for (arguments[0..]) |arg_j, j| {
+ vectors[(i * arguments.len) + j] = generateVector(arg_i, arg_j);
+ }
+ }
+ break :init vectors;
+};
+
+test "compare f64" {
+ for (test_vectors) |vector, i| {
+ std.testing.expect(test__cmpdf2(vector));
+ }
+}
diff --git a/std/special/compiler_rt/comparesf2.zig b/std/special/compiler_rt/comparesf2.zig
new file mode 100644
index 0000000000..e99e0bb3dd
--- /dev/null
+++ b/std/special/compiler_rt/comparesf2.zig
@@ -0,0 +1,122 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/lib/builtins/comparesf2.c
+
+const std = @import("std");
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const fp_t = f32;
+const rep_t = u32;
+const srep_t = i32;
+
+const typeWidth = rep_t.bit_count;
+const significandBits = std.math.floatMantissaBits(fp_t);
+const exponentBits = std.math.floatExponentBits(fp_t);
+const signBit = (rep_t(1) << (significandBits + exponentBits));
+const absMask = signBit - 1;
+const implicitBit = rep_t(1) << significandBits;
+const significandMask = implicitBit - 1;
+const exponentMask = absMask ^ significandMask;
+const infRep = @bitCast(rep_t, std.math.inf(fp_t));
+
+// TODO https://github.com/ziglang/zig/issues/641
+// and then make the return types of some of these functions the enum instead of c_int
+const LE_LESS = c_int(-1);
+const LE_EQUAL = c_int(0);
+const LE_GREATER = c_int(1);
+const LE_UNORDERED = c_int(1);
+
+pub extern fn __lesf2(a: fp_t, b: fp_t) c_int {
+ @setRuntimeSafety(is_test);
+ const aInt: srep_t = @bitCast(srep_t, a);
+ const bInt: srep_t = @bitCast(srep_t, b);
+ const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask;
+ const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask;
+
+ // If either a or b is NaN, they are unordered.
+ if (aAbs > infRep or bAbs > infRep) return LE_UNORDERED;
+
+ // If a and b are both zeros, they are equal.
+ if ((aAbs | bAbs) == 0) return LE_EQUAL;
+
+ // If at least one of a and b is positive, we get the same result comparing
+ // a and b as signed integers as we would with a fp_ting-point compare.
+ if ((aInt & bInt) >= 0) {
+ if (aInt < bInt) {
+ return LE_LESS;
+ } else if (aInt == bInt) {
+ return LE_EQUAL;
+ } else return LE_GREATER;
+ }
+
+ // Otherwise, both are negative, so we need to flip the sense of the
+ // comparison to get the correct result. (This assumes a twos- or ones-
+ // complement integer representation; if integers are represented in a
+ // sign-magnitude representation, then this flip is incorrect).
+ else {
+ if (aInt > bInt) {
+ return LE_LESS;
+ } else if (aInt == bInt) {
+ return LE_EQUAL;
+ } else return LE_GREATER;
+ }
+}
+
+// TODO https://github.com/ziglang/zig/issues/641
+// and then make the return types of some of these functions the enum instead of c_int
+const GE_LESS = c_int(-1);
+const GE_EQUAL = c_int(0);
+const GE_GREATER = c_int(1);
+const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED
+
+pub extern fn __gesf2(a: fp_t, b: fp_t) c_int {
+ @setRuntimeSafety(is_test);
+ const aInt: srep_t = @bitCast(srep_t, a);
+ const bInt: srep_t = @bitCast(srep_t, b);
+ const aAbs: rep_t = @bitCast(rep_t, aInt) & absMask;
+ const bAbs: rep_t = @bitCast(rep_t, bInt) & absMask;
+
+ if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED;
+ if ((aAbs | bAbs) == 0) return GE_EQUAL;
+ if ((aInt & bInt) >= 0) {
+ if (aInt < bInt) {
+ return GE_LESS;
+ } else if (aInt == bInt) {
+ return GE_EQUAL;
+ } else return GE_GREATER;
+ } else {
+ if (aInt > bInt) {
+ return GE_LESS;
+ } else if (aInt == bInt) {
+ return GE_EQUAL;
+ } else return GE_GREATER;
+ }
+}
+
+pub extern fn __unordsf2(a: fp_t, b: fp_t) c_int {
+ @setRuntimeSafety(is_test);
+ const aAbs: rep_t = @bitCast(rep_t, a) & absMask;
+ const bAbs: rep_t = @bitCast(rep_t, b) & absMask;
+ return @boolToInt(aAbs > infRep or bAbs > infRep);
+}
+
+pub extern fn __eqsf2(a: fp_t, b: fp_t) c_int {
+ return __lesf2(a, b);
+}
+
+pub extern fn __ltsf2(a: fp_t, b: fp_t) c_int {
+ return __lesf2(a, b);
+}
+
+pub extern fn __nesf2(a: fp_t, b: fp_t) c_int {
+ return __lesf2(a, b);
+}
+
+pub extern fn __gtsf2(a: fp_t, b: fp_t) c_int {
+ return __gesf2(a, b);
+}
+
+test "import comparesf2" {
+ _ = @import("comparesf2_test.zig");
+}
diff --git a/std/special/compiler_rt/comparesf2_test.zig b/std/special/compiler_rt/comparesf2_test.zig
new file mode 100644
index 0000000000..e460634fad
--- /dev/null
+++ b/std/special/compiler_rt/comparesf2_test.zig
@@ -0,0 +1,101 @@
+// Ported from:
+//
+// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/comparesf2_test.c
+
+const std = @import("std");
+const builtin = @import("builtin");
+const is_test = builtin.is_test;
+
+const comparesf2 = @import("comparesf2.zig");
+
+const TestVector = struct {
+ a: f32,
+ b: f32,
+ eqReference: c_int,
+ geReference: c_int,
+ gtReference: c_int,
+ leReference: c_int,
+ ltReference: c_int,
+ neReference: c_int,
+ unReference: c_int,
+};
+
+fn test__cmpsf2(vector: TestVector) bool {
+ if (comparesf2.__eqsf2(vector.a, vector.b) != vector.eqReference) {
+ return false;
+ }
+ if (comparesf2.__gesf2(vector.a, vector.b) != vector.geReference) {
+ return false;
+ }
+ if (comparesf2.__gtsf2(vector.a, vector.b) != vector.gtReference) {
+ return false;
+ }
+ if (comparesf2.__lesf2(vector.a, vector.b) != vector.leReference) {
+ return false;
+ }
+ if (comparesf2.__ltsf2(vector.a, vector.b) != vector.ltReference) {
+ return false;
+ }
+ if (comparesf2.__nesf2(vector.a, vector.b) != vector.neReference) {
+ return false;
+ }
+ if (comparesf2.__unordsf2(vector.a, vector.b) != vector.unReference) {
+ return false;
+ }
+ return true;
+}
+
+const arguments = []f32{
+ std.math.nan(f32),
+ -std.math.inf(f32),
+ -0x1.fffffep127,
+ -0x1.000002p0 - 0x1.000000p0,
+ -0x1.fffffep-1,
+ -0x1.000000p-126,
+ -0x0.fffffep-126,
+ -0x0.000002p-126,
+ -0.0,
+ 0.0,
+ 0x0.000002p-126,
+ 0x0.fffffep-126,
+ 0x1.000000p-126,
+ 0x1.fffffep-1,
+ 0x1.000000p0,
+ 0x1.000002p0,
+ 0x1.fffffep127,
+ std.math.inf(f32),
+};
+
+fn generateVector(comptime a: f32, comptime b: f32) TestVector {
+ const leResult = if (a < b) -1 else if (a == b) 0 else 1;
+ const geResult = if (a > b) 1 else if (a == b) 0 else -1;
+ const unResult = if (a != a or b != b) 1 else 0;
+ return TestVector{
+ .a = a,
+ .b = b,
+ .eqReference = leResult,
+ .geReference = geResult,
+ .gtReference = geResult,
+ .leReference = leResult,
+ .ltReference = leResult,
+ .neReference = leResult,
+ .unReference = unResult,
+ };
+}
+
+const test_vectors = init: {
+ @setEvalBranchQuota(10000);
+ var vectors: [arguments.len * arguments.len]TestVector = undefined;
+ for (arguments[0..]) |arg_i, i| {
+ for (arguments[0..]) |arg_j, j| {
+ vectors[(i * arguments.len) + j] = generateVector(arg_i, arg_j);
+ }
+ }
+ break :init vectors;
+};
+
+test "compare f32" {
+ for (test_vectors) |vector, i| {
+ std.testing.expect(test__cmpsf2(vector));
+ }
+}
diff --git a/std/special/compiler_rt/divti3.zig b/std/special/compiler_rt/divti3.zig
index e89a1ada5c..d5b2778a34 100644
--- a/std/special/compiler_rt/divti3.zig
+++ b/std/special/compiler_rt/divti3.zig
@@ -16,9 +16,9 @@ pub extern fn __divti3(a: i128, b: i128) i128 {
return (@bitCast(i128, r) ^ s) -% s;
}
-pub extern fn __divti3_windows_x86_64(a: *const i128, b: *const i128) void {
- @setRuntimeSafety(builtin.is_test);
- compiler_rt.setXmm0(i128, __divti3(a.*, b.*));
+const v128 = @Vector(2, u64);
+pub extern fn __divti3_windows_x86_64(a: v128, b: v128) v128 {
+ return @bitCast(v128, @inlineCall(__divti3, @bitCast(i128, a), @bitCast(i128, b)));
}
test "import divti3" {
diff --git a/std/special/compiler_rt/extendXfYf2.zig b/std/special/compiler_rt/extendXfYf2.zig
index 099e27b74a..42559784bb 100644
--- a/std/special/compiler_rt/extendXfYf2.zig
+++ b/std/special/compiler_rt/extendXfYf2.zig
@@ -2,21 +2,27 @@ const std = @import("std");
const builtin = @import("builtin");
const is_test = builtin.is_test;
+pub extern fn __extendsfdf2(a: f32) f64 {
+ return @inlineCall(extendXfYf2, f64, f32, @bitCast(u32, a));
+}
+
pub extern fn __extenddftf2(a: f64) f128 {
- return extendXfYf2(f128, f64, a);
+ return @inlineCall(extendXfYf2, f128, f64, @bitCast(u64, a));
}
pub extern fn __extendsftf2(a: f32) f128 {
- return extendXfYf2(f128, f32, a);
+ return @inlineCall(extendXfYf2, f128, f32, @bitCast(u32, a));
}
pub extern fn __extendhfsf2(a: u16) f32 {
- return extendXfYf2(f32, f16, @bitCast(f16, a));
+ return @inlineCall(extendXfYf2, f32, f16, a);
}
const CHAR_BIT = 8;
-inline fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
+fn extendXfYf2(comptime dst_t: type, comptime src_t: type, a: @IntType(false, @typeInfo(src_t).Float.bits)) dst_t {
+ @setRuntimeSafety(builtin.is_test);
+
const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits);
const srcSigBits = std.math.floatMantissaBits(src_t);
diff --git a/std/special/compiler_rt/floatdidf.zig b/std/special/compiler_rt/floatdidf.zig
new file mode 100644
index 0000000000..1610136413
--- /dev/null
+++ b/std/special/compiler_rt/floatdidf.zig
@@ -0,0 +1,22 @@
+const builtin = @import("builtin");
+const std = @import("std");
+
+const twop52: f64 = 0x1.0p52;
+const twop32: f64 = 0x1.0p32;
+
+pub extern fn __floatdidf(a: i64) f64 {
+ @setRuntimeSafety(builtin.is_test);
+
+ if (a == 0) return 0;
+
+ var low = @bitCast(i64, twop52);
+ const high = @intToFloat(f64, @truncate(i32, a >> 32)) * twop32;
+
+ low |= @bitCast(i64, a & 0xFFFFFFFF);
+
+ return (high - twop52) + @bitCast(f64, low);
+}
+
+test "import floatdidf" {
+ _ = @import("floatdidf_test.zig");
+}
diff --git a/std/special/compiler_rt/floatdidf_test.zig b/std/special/compiler_rt/floatdidf_test.zig
new file mode 100644
index 0000000000..c854183809
--- /dev/null
+++ b/std/special/compiler_rt/floatdidf_test.zig
@@ -0,0 +1,53 @@
+const __floatdidf = @import("floatdidf.zig").__floatdidf;
+const testing = @import("std").testing;
+
+fn test__floatdidf(a: i64, expected: f64) void {
+ const r = __floatdidf(a);
+ testing.expect(r == expected);
+}
+
+test "floatdidf" {
+ test__floatdidf(0, 0.0);
+ test__floatdidf(1, 1.0);
+ test__floatdidf(2, 2.0);
+ test__floatdidf(20, 20.0);
+ test__floatdidf(-1, -1.0);
+ test__floatdidf(-2, -2.0);
+ test__floatdidf(-20, -20.0);
+ test__floatdidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floatdidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62);
+ test__floatdidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+ test__floatdidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62);
+ test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000008000000000)), -0x1.FFFFFEp+62);
+ test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000800)), -0x1.FFFFFFFFFFFFEp+62);
+ test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000010000000000)), -0x1.FFFFFCp+62);
+ test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000001000)), -0x1.FFFFFFFFFFFFCp+62);
+ test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000000)), -0x1.000000p+63);
+ test__floatdidf(@bitCast(i64, @intCast(u64, 0x8000000000000001)), -0x1.000000p+63);
+ test__floatdidf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+ test__floatdidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50);
+ test__floatdidf(0x0007FB72EB000000, 0x1.FEDCBACp+50);
+ test__floatdidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50);
+ test__floatdidf(0x0007FB72EC000000, 0x1.FEDCBBp+50);
+ test__floatdidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50);
+ test__floatdidf(0x0007FB72E6000000, 0x1.FEDCB98p+50);
+ test__floatdidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50);
+ test__floatdidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50);
+ test__floatdidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50);
+ test__floatdidf(0x0007FB72E4000000, 0x1.FEDCB9p+50);
+ test__floatdidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57);
+ test__floatdidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57);
+ test__floatdidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57);
+ test__floatdidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57);
+ test__floatdidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57);
+ test__floatdidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57);
+}
diff --git a/std/special/compiler_rt/floatsiXf.zig b/std/special/compiler_rt/floatsiXf.zig
new file mode 100644
index 0000000000..83b3940c1e
--- /dev/null
+++ b/std/special/compiler_rt/floatsiXf.zig
@@ -0,0 +1,109 @@
+const builtin = @import("builtin");
+const std = @import("std");
+const maxInt = std.math.maxInt;
+
+fn floatsiXf(comptime T: type, a: i32) T {
+ @setRuntimeSafety(builtin.is_test);
+
+ const Z = @IntType(false, T.bit_count);
+ const S = @IntType(false, T.bit_count - @clz(Z(T.bit_count) - 1));
+
+ if (a == 0) {
+ return T(0.0);
+ }
+
+ const significandBits = std.math.floatMantissaBits(T);
+ const exponentBits = std.math.floatExponentBits(T);
+ const exponentBias = ((1 << exponentBits - 1) - 1);
+
+ const implicitBit = Z(1) << significandBits;
+ const signBit = Z(1 << Z.bit_count - 1);
+
+ const sign = a >> 31;
+ // Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31).
+ const abs_a = (a ^ sign) -% sign;
+ // The exponent is the width of abs(a)
+ const exp = Z(31 - @clz(abs_a));
+
+ const sign_bit = if (sign < 0) signBit else 0;
+
+ var mantissa: Z = undefined;
+ // Shift a into the significand field and clear the implicit bit.
+ if (exp <= significandBits) {
+ // No rounding needed
+ const shift = @intCast(S, significandBits - exp);
+ mantissa = @intCast(Z, @bitCast(u32, abs_a)) << shift ^ implicitBit;
+ } else {
+ const shift = @intCast(S, exp - significandBits);
+ // Round to the nearest number after truncation
+ mantissa = @intCast(Z, @bitCast(u32, abs_a)) >> shift ^ implicitBit;
+ // Align to the left and check if the truncated part is halfway over
+ const round = @bitCast(u32, abs_a) << @intCast(u5, 31 - shift);
+ mantissa += @boolToInt(round > 0x80000000);
+ // Tie to even
+ mantissa += mantissa & 1;
+ }
+
+ // Use the addition instead of a or since we may have a carry from the
+ // mantissa to the exponent
+ var result = mantissa;
+ result += (exp + exponentBias) << significandBits;
+ result += sign_bit;
+
+ return @bitCast(T, result);
+}
+
+pub extern fn __floatsisf(arg: i32) f32 {
+ @setRuntimeSafety(builtin.is_test);
+ return @inlineCall(floatsiXf, f32, arg);
+}
+
+pub extern fn __floatsidf(arg: i32) f64 {
+ @setRuntimeSafety(builtin.is_test);
+ return @inlineCall(floatsiXf, f64, arg);
+}
+
+pub extern fn __floatsitf(arg: i32) f128 {
+ @setRuntimeSafety(builtin.is_test);
+ return @inlineCall(floatsiXf, f128, arg);
+}
+
+fn test_one_floatsitf(a: i32, expected: u128) void {
+ const r = __floatsitf(a);
+ std.testing.expect(@bitCast(u128, r) == expected);
+}
+
+fn test_one_floatsidf(a: i32, expected: u64) void {
+ const r = __floatsidf(a);
+ std.testing.expect(@bitCast(u64, r) == expected);
+}
+
+fn test_one_floatsisf(a: i32, expected: u32) void {
+ const r = __floatsisf(a);
+ std.testing.expect(@bitCast(u32, r) == expected);
+}
+
+test "floatsidf" {
+ test_one_floatsidf(0, 0x0000000000000000);
+ test_one_floatsidf(1, 0x3ff0000000000000);
+ test_one_floatsidf(-1, 0xbff0000000000000);
+ test_one_floatsidf(0x7FFFFFFF, 0x41dfffffffc00000);
+ test_one_floatsidf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc1e0000000000000);
+}
+
+test "floatsisf" {
+ test_one_floatsisf(0, 0x00000000);
+ test_one_floatsisf(1, 0x3f800000);
+ test_one_floatsisf(-1, 0xbf800000);
+ test_one_floatsisf(0x7FFFFFFF, 0x4f000000);
+ test_one_floatsisf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xcf000000);
+}
+
+test "floatsitf" {
+ test_one_floatsitf(0, 0);
+ test_one_floatsitf(0x7FFFFFFF, 0x401dfffffffc00000000000000000000);
+ test_one_floatsitf(0x12345678, 0x401b2345678000000000000000000000);
+ test_one_floatsitf(-0x12345678, 0xc01b2345678000000000000000000000);
+ test_one_floatsitf(@bitCast(i32, @intCast(u32, 0xffffffff)), 0xbfff0000000000000000000000000000);
+ test_one_floatsitf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc01e0000000000000000000000000000);
+}
diff --git a/std/special/compiler_rt/floatundidf.zig b/std/special/compiler_rt/floatundidf.zig
new file mode 100644
index 0000000000..68759a2acd
--- /dev/null
+++ b/std/special/compiler_rt/floatundidf.zig
@@ -0,0 +1,24 @@
+const builtin = @import("builtin");
+const std = @import("std");
+
+const twop52: f64 = 0x1.0p52;
+const twop84: f64 = 0x1.0p84;
+const twop84_plus_twop52: f64 = 0x1.00000001p84;
+
+pub extern fn __floatundidf(a: u64) f64 {
+ @setRuntimeSafety(builtin.is_test);
+
+ if (a == 0) return 0;
+
+ var high = @bitCast(u64, twop84);
+ var low = @bitCast(u64, twop52);
+
+ high |= a >> 32;
+ low |= a & 0xFFFFFFFF;
+
+ return (@bitCast(f64, high) - twop84_plus_twop52) + @bitCast(f64, low);
+}
+
+test "import floatundidf" {
+ _ = @import("floatundidf_test.zig");
+}
diff --git a/std/special/compiler_rt/floatundidf_test.zig b/std/special/compiler_rt/floatundidf_test.zig
new file mode 100644
index 0000000000..084ada51c9
--- /dev/null
+++ b/std/special/compiler_rt/floatundidf_test.zig
@@ -0,0 +1,50 @@
+const __floatundidf = @import("floatundidf.zig").__floatundidf;
+const testing = @import("std").testing;
+
+fn test__floatundidf(a: u64, expected: f64) void {
+ const r = __floatundidf(a);
+ testing.expect(r == expected);
+}
+
+test "floatundidf" {
+ test__floatundidf(0, 0.0);
+ test__floatundidf(1, 1.0);
+ test__floatundidf(2, 2.0);
+ test__floatundidf(20, 20.0);
+ test__floatundidf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
+ test__floatundidf(0x7FFFFFFFFFFFF800, 0x1.FFFFFFFFFFFFEp+62);
+ test__floatundidf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
+ test__floatundidf(0x7FFFFFFFFFFFF000, 0x1.FFFFFFFFFFFFCp+62);
+ test__floatundidf(0x8000008000000000, 0x1.000001p+63);
+ test__floatundidf(0x8000000000000800, 0x1.0000000000001p+63);
+ test__floatundidf(0x8000010000000000, 0x1.000002p+63);
+ test__floatundidf(0x8000000000001000, 0x1.0000000000002p+63);
+ test__floatundidf(0x8000000000000000, 0x1p+63);
+ test__floatundidf(0x8000000000000001, 0x1p+63);
+ test__floatundidf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
+ test__floatundidf(0x0007FB72EA000000, 0x1.FEDCBA8p+50);
+ test__floatundidf(0x0007FB72EB000000, 0x1.FEDCBACp+50);
+ test__floatundidf(0x0007FB72EBFFFFFF, 0x1.FEDCBAFFFFFFCp+50);
+ test__floatundidf(0x0007FB72EC000000, 0x1.FEDCBBp+50);
+ test__floatundidf(0x0007FB72E8000001, 0x1.FEDCBA0000004p+50);
+ test__floatundidf(0x0007FB72E6000000, 0x1.FEDCB98p+50);
+ test__floatundidf(0x0007FB72E7000000, 0x1.FEDCB9Cp+50);
+ test__floatundidf(0x0007FB72E7FFFFFF, 0x1.FEDCB9FFFFFFCp+50);
+ test__floatundidf(0x0007FB72E4000001, 0x1.FEDCB90000004p+50);
+ test__floatundidf(0x0007FB72E4000000, 0x1.FEDCB9p+50);
+ test__floatundidf(0x023479FD0E092DC0, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DA1, 0x1.1A3CFE870496Dp+57);
+ test__floatundidf(0x023479FD0E092DB0, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DB8, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DB6, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DBF, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DC1, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DC7, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DC8, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DCF, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DD0, 0x1.1A3CFE870496Ep+57);
+ test__floatundidf(0x023479FD0E092DD1, 0x1.1A3CFE870496Fp+57);
+ test__floatundidf(0x023479FD0E092DD8, 0x1.1A3CFE870496Fp+57);
+ test__floatundidf(0x023479FD0E092DDF, 0x1.1A3CFE870496Fp+57);
+ test__floatundidf(0x023479FD0E092DE0, 0x1.1A3CFE870496Fp+57);
+}
diff --git a/std/special/compiler_rt/floatunsidf.zig b/std/special/compiler_rt/floatunsidf.zig
new file mode 100644
index 0000000000..db02894448
--- /dev/null
+++ b/std/special/compiler_rt/floatunsidf.zig
@@ -0,0 +1,33 @@
+const builtin = @import("builtin");
+const std = @import("std");
+const maxInt = std.math.maxInt;
+
+const implicitBit = u64(1) << 52;
+
+pub extern fn __floatunsidf(arg: u32) f64 {
+ @setRuntimeSafety(builtin.is_test);
+
+ if (arg == 0) return 0.0;
+
+ // The exponent is the width of abs(a)
+ const exp = u64(31) - @clz(arg);
+ // Shift a into the significand field and clear the implicit bit
+ const shift = @intCast(u6, 52 - exp);
+ const mant = u64(arg) << shift ^ implicitBit;
+
+ return @bitCast(f64, mant | (exp + 1023) << 52);
+}
+
+fn test_one_floatunsidf(a: u32, expected: u64) void {
+ const r = __floatunsidf(a);
+ std.testing.expect(@bitCast(u64, r) == expected);
+}
+
+test "floatsidf" {
+ // Test the produced bit pattern
+ test_one_floatunsidf(0, 0x0000000000000000);
+ test_one_floatunsidf(1, 0x3ff0000000000000);
+ test_one_floatunsidf(0x7FFFFFFF, 0x41dfffffffc00000);
+ test_one_floatunsidf(@intCast(u32, 0x80000000), 0x41e0000000000000);
+ test_one_floatunsidf(@intCast(u32, 0xFFFFFFFF), 0x41efffffffe00000);
+}
diff --git a/std/special/compiler_rt/lshrti3.zig b/std/special/compiler_rt/lshrti3.zig
new file mode 100644
index 0000000000..329968ae40
--- /dev/null
+++ b/std/special/compiler_rt/lshrti3.zig
@@ -0,0 +1,41 @@
+const builtin = @import("builtin");
+const compiler_rt = @import("../compiler_rt.zig");
+
+pub extern fn __lshrti3(a: i128, b: i32) i128 {
+ var input = twords{ .all = a };
+ var result: twords = undefined;
+
+ if (b > 63) {
+ // 64 <= b < 128
+ result.s.low = input.s.high >> @intCast(u6, b - 64);
+ result.s.high = 0;
+ } else {
+ // 0 <= b < 64
+ if (b == 0) return a;
+ result.s.low = input.s.high << @intCast(u6, 64 - b);
+ result.s.low |= input.s.low >> @intCast(u6, b);
+ result.s.high = input.s.high >> @intCast(u6, b);
+ }
+
+ return result.all;
+}
+
+const twords = extern union {
+ all: i128,
+ s: S,
+
+ const S = if (builtin.endian == builtin.Endian.Little)
+ struct {
+ low: u64,
+ high: u64,
+ }
+ else
+ struct {
+ high: u64,
+ low: u64,
+ };
+};
+
+test "import lshrti3" {
+ _ = @import("lshrti3_test.zig");
+}
diff --git a/std/special/compiler_rt/lshrti3_test.zig b/std/special/compiler_rt/lshrti3_test.zig
new file mode 100644
index 0000000000..60f83d816e
--- /dev/null
+++ b/std/special/compiler_rt/lshrti3_test.zig
@@ -0,0 +1,46 @@
+const __lshrti3 = @import("lshrti3.zig").__lshrti3;
+const testing = @import("std").testing;
+
+fn test__lshrti3(a: i128, b: i32, expected: i128) void {
+ const x = __lshrti3(a, b);
+ testing.expect(x == expected);
+}
+
+test "lshrti3" {
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 0, @bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 1, @bitCast(i128, @intCast(u128, 0x7F6E5D4C3B2A190AFF6E5D4C3B2A190A)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 2, @bitCast(i128, @intCast(u128, 0x3FB72EA61D950C857FB72EA61D950C85)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 3, @bitCast(i128, @intCast(u128, 0x1FDB97530ECA8642BFDB97530ECA8642)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 4, @bitCast(i128, @intCast(u128, 0x0FEDCBA9876543215FEDCBA987654321)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 28, @bitCast(i128, @intCast(u128, 0x0000000FEDCBA9876543215FEDCBA987)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 29, @bitCast(i128, @intCast(u128, 0x00000007F6E5D4C3B2A190AFF6E5D4C3)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 30, @bitCast(i128, @intCast(u128, 0x00000003FB72EA61D950C857FB72EA61)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 31, @bitCast(i128, @intCast(u128, 0x00000001FDB97530ECA8642BFDB97530)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 32, @bitCast(i128, @intCast(u128, 0x00000000FEDCBA9876543215FEDCBA98)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 33, @bitCast(i128, @intCast(u128, 0x000000007F6E5D4C3B2A190AFF6E5D4C)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 34, @bitCast(i128, @intCast(u128, 0x000000003FB72EA61D950C857FB72EA6)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 35, @bitCast(i128, @intCast(u128, 0x000000001FDB97530ECA8642BFDB9753)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 36, @bitCast(i128, @intCast(u128, 0x000000000FEDCBA9876543215FEDCBA9)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 60, @bitCast(i128, @intCast(u128, 0x000000000000000FEDCBA9876543215F)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 61, @bitCast(i128, @intCast(u128, 0x0000000000000007F6E5D4C3B2A190AF)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 62, @bitCast(i128, @intCast(u128, 0x0000000000000003FB72EA61D950C857)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 63, @bitCast(i128, @intCast(u128, 0x0000000000000001FDB97530ECA8642B)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 64, @bitCast(i128, @intCast(u128, 0x0000000000000000FEDCBA9876543215)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 65, @bitCast(i128, @intCast(u128, 0x00000000000000007F6E5D4C3B2A190A)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 66, @bitCast(i128, @intCast(u128, 0x00000000000000003FB72EA61D950C85)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 67, @bitCast(i128, @intCast(u128, 0x00000000000000001FDB97530ECA8642)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 68, @bitCast(i128, @intCast(u128, 0x00000000000000000FEDCBA987654321)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 92, @bitCast(i128, @intCast(u128, 0x00000000000000000000000FEDCBA987)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 93, @bitCast(i128, @intCast(u128, 0x000000000000000000000007F6E5D4C3)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 94, @bitCast(i128, @intCast(u128, 0x000000000000000000000003FB72EA61)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 95, @bitCast(i128, @intCast(u128, 0x000000000000000000000001FDB97530)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 96, @bitCast(i128, @intCast(u128, 0x000000000000000000000000FEDCBA98)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 97, @bitCast(i128, @intCast(u128, 0x0000000000000000000000007F6E5D4C)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 98, @bitCast(i128, @intCast(u128, 0x0000000000000000000000003FB72EA6)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 99, @bitCast(i128, @intCast(u128, 0x0000000000000000000000001FDB9753)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 100, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000FEDCBA9)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 124, @bitCast(i128, @intCast(u128, 0x0000000000000000000000000000000F)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 125, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000007)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 126, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000003)));
+ test__lshrti3(@bitCast(i128, @intCast(u128, 0xFEDCBA9876543215FEDCBA9876543215)), 127, @bitCast(i128, @intCast(u128, 0x00000000000000000000000000000001)));
+}
diff --git a/std/special/compiler_rt/modti3.zig b/std/special/compiler_rt/modti3.zig
index 03222cadf5..16f2f38ba3 100644
--- a/std/special/compiler_rt/modti3.zig
+++ b/std/special/compiler_rt/modti3.zig
@@ -20,9 +20,9 @@ pub extern fn __modti3(a: i128, b: i128) i128 {
return (@bitCast(i128, r) ^ s_a) -% s_a; // negate if s == -1
}
-pub extern fn __modti3_windows_x86_64(a: *const i128, b: *const i128) void {
- @setRuntimeSafety(builtin.is_test);
- compiler_rt.setXmm0(i128, __modti3(a.*, b.*));
+const v128 = @Vector(2, u64);
+pub extern fn __modti3_windows_x86_64(a: v128, b: v128) v128 {
+ return @bitCast(v128, @inlineCall(__modti3, @bitCast(i128, a), @bitCast(i128, b)));
}
test "import modti3" {
diff --git a/std/special/compiler_rt/mulodi4.zig b/std/special/compiler_rt/mulodi4.zig
new file mode 100644
index 0000000000..82e9ef3253
--- /dev/null
+++ b/std/special/compiler_rt/mulodi4.zig
@@ -0,0 +1,44 @@
+const builtin = @import("builtin");
+const compiler_rt = @import("../compiler_rt.zig");
+const maxInt = std.math.maxInt;
+const minInt = std.math.minInt;
+
+pub extern fn __mulodi4(a: i64, b: i64, overflow: *c_int) i64 {
+ @setRuntimeSafety(builtin.is_test);
+
+ const min = @bitCast(i64, u64(1 << (i64.bit_count - 1)));
+ const max = ~min;
+
+ overflow.* = 0;
+ const result = a *% b;
+
+ // Edge cases
+ if (a == min) {
+ if (b != 0 and b != 1) overflow.* = 1;
+ return result;
+ }
+ if (b == min) {
+ if (a != 0 and a != 1) overflow.* = 1;
+ return result;
+ }
+
+ // Take absolute value of a and b via abs(x) = (x^(x >> 63)) - (x >> 63).
+ const abs_a = (a ^ (a >> 63)) -% (a >> 63);
+ const abs_b = (b ^ (b >> 63)) -% (b >> 63);
+
+ // Unitary magnitude, cannot have overflow
+ if (abs_a < 2 or abs_b < 2) return result;
+
+ // Compare the signs of the operands
+ if ((a ^ b) >> 63 != 0) {
+ if (abs_a > @divTrunc(max, abs_b)) overflow.* = 1;
+ } else {
+ if (abs_a > @divTrunc(min, -abs_b)) overflow.* = 1;
+ }
+
+ return result;
+}
+
+test "import mulodi4" {
+ _ = @import("mulodi4_test.zig");
+}
diff --git a/std/special/compiler_rt/mulodi4_test.zig b/std/special/compiler_rt/mulodi4_test.zig
new file mode 100644
index 0000000000..7575c77044
--- /dev/null
+++ b/std/special/compiler_rt/mulodi4_test.zig
@@ -0,0 +1,85 @@
+const __mulodi4 = @import("mulodi4.zig").__mulodi4;
+const testing = @import("std").testing;
+
+fn test__mulodi4(a: i64, b: i64, expected: i64, expected_overflow: c_int) void {
+ var overflow: c_int = undefined;
+ const x = __mulodi4(a, b, &overflow);
+ testing.expect(overflow == expected_overflow and (expected_overflow != 0 or x == expected));
+}
+
+test "mulodi4" {
+ test__mulodi4(0, 0, 0, 0);
+ test__mulodi4(0, 1, 0, 0);
+ test__mulodi4(1, 0, 0, 0);
+ test__mulodi4(0, 10, 0, 0);
+ test__mulodi4(10, 0, 0, 0);
+ test__mulodi4(0, 81985529216486895, 0, 0);
+ test__mulodi4(81985529216486895, 0, 0, 0);
+
+ test__mulodi4(0, -1, 0, 0);
+ test__mulodi4(-1, 0, 0, 0);
+ test__mulodi4(0, -10, 0, 0);
+ test__mulodi4(-10, 0, 0, 0);
+ test__mulodi4(0, -81985529216486895, 0, 0);
+ test__mulodi4(-81985529216486895, 0, 0, 0);
+
+ test__mulodi4(1, 1, 1, 0);
+ test__mulodi4(1, 10, 10, 0);
+ test__mulodi4(10, 1, 10, 0);
+ test__mulodi4(1, 81985529216486895, 81985529216486895, 0);
+ test__mulodi4(81985529216486895, 1, 81985529216486895, 0);
+
+ test__mulodi4(1, -1, -1, 0);
+ test__mulodi4(1, -10, -10, 0);
+ test__mulodi4(-10, 1, -10, 0);
+ test__mulodi4(1, -81985529216486895, -81985529216486895, 0);
+ test__mulodi4(-81985529216486895, 1, -81985529216486895, 0);
+
+ test__mulodi4(3037000499, 3037000499, 9223372030926249001, 0);
+ test__mulodi4(-3037000499, 3037000499, -9223372030926249001, 0);
+ test__mulodi4(3037000499, -3037000499, -9223372030926249001, 0);
+ test__mulodi4(-3037000499, -3037000499, 9223372030926249001, 0);
+
+ test__mulodi4(4398046511103, 2097152, 9223372036852678656, 0);
+ test__mulodi4(-4398046511103, 2097152, -9223372036852678656, 0);
+ test__mulodi4(4398046511103, -2097152, -9223372036852678656, 0);
+ test__mulodi4(-4398046511103, -2097152, 9223372036852678656, 0);
+
+ test__mulodi4(2097152, 4398046511103, 9223372036852678656, 0);
+ test__mulodi4(-2097152, 4398046511103, -9223372036852678656, 0);
+ test__mulodi4(2097152, -4398046511103, -9223372036852678656, 0);
+ test__mulodi4(-2097152, -4398046511103, 9223372036852678656, 0);
+
+ test__mulodi4(0x7FFFFFFFFFFFFFFF, -2, 2, 1);
+ test__mulodi4(-2, 0x7FFFFFFFFFFFFFFF, 2, 1);
+ test__mulodi4(0x7FFFFFFFFFFFFFFF, -1, @bitCast(i64, u64(0x8000000000000001)), 0);
+ test__mulodi4(-1, 0x7FFFFFFFFFFFFFFF, @bitCast(i64, u64(0x8000000000000001)), 0);
+ test__mulodi4(0x7FFFFFFFFFFFFFFF, 0, 0, 0);
+ test__mulodi4(0, 0x7FFFFFFFFFFFFFFF, 0, 0);
+ test__mulodi4(0x7FFFFFFFFFFFFFFF, 1, 0x7FFFFFFFFFFFFFFF, 0);
+ test__mulodi4(1, 0x7FFFFFFFFFFFFFFF, 0x7FFFFFFFFFFFFFFF, 0);
+ test__mulodi4(0x7FFFFFFFFFFFFFFF, 2, @bitCast(i64, u64(0x8000000000000001)), 1);
+ test__mulodi4(2, 0x7FFFFFFFFFFFFFFF, @bitCast(i64, u64(0x8000000000000001)), 1);
+
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), -2, @bitCast(i64, u64(0x8000000000000000)), 1);
+ test__mulodi4(-2, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 1);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), -1, @bitCast(i64, u64(0x8000000000000000)), 1);
+ test__mulodi4(-1, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 1);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), 0, 0, 0);
+ test__mulodi4(0, @bitCast(i64, u64(0x8000000000000000)), 0, 0);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), 1, @bitCast(i64, u64(0x8000000000000000)), 0);
+ test__mulodi4(1, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 0);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000000)), 2, @bitCast(i64, u64(0x8000000000000000)), 1);
+ test__mulodi4(2, @bitCast(i64, u64(0x8000000000000000)), @bitCast(i64, u64(0x8000000000000000)), 1);
+
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), -2, @bitCast(i64, u64(0x8000000000000001)), 1);
+ test__mulodi4(-2, @bitCast(i64, u64(0x8000000000000001)), @bitCast(i64, u64(0x8000000000000001)), 1);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), -1, 0x7FFFFFFFFFFFFFFF, 0);
+ test__mulodi4(-1, @bitCast(i64, u64(0x8000000000000001)), 0x7FFFFFFFFFFFFFFF, 0);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), 0, 0, 0);
+ test__mulodi4(0, @bitCast(i64, u64(0x8000000000000001)), 0, 0);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), 1, @bitCast(i64, u64(0x8000000000000001)), 0);
+ test__mulodi4(1, @bitCast(i64, u64(0x8000000000000001)), @bitCast(i64, u64(0x8000000000000001)), 0);
+ test__mulodi4(@bitCast(i64, u64(0x8000000000000001)), 2, @bitCast(i64, u64(0x8000000000000000)), 1);
+ test__mulodi4(2, @bitCast(i64, u64(0x8000000000000001)), @bitCast(i64, u64(0x8000000000000000)), 1);
+}
diff --git a/std/special/compiler_rt/muloti4.zig b/std/special/compiler_rt/muloti4.zig
index fd6855072b..ccde8e3e6c 100644
--- a/std/special/compiler_rt/muloti4.zig
+++ b/std/special/compiler_rt/muloti4.zig
@@ -1,4 +1,3 @@
-const udivmod = @import("udivmod.zig").udivmod;
const builtin = @import("builtin");
const compiler_rt = @import("../compiler_rt.zig");
@@ -33,11 +32,11 @@ pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 {
}
if (sa == sb) {
- if (abs_a > @divFloor(max, abs_b)) {
+ if (abs_a > @divTrunc(max, abs_b)) {
overflow.* = 1;
}
} else {
- if (abs_a > @divFloor(min, -abs_b)) {
+ if (abs_a > @divTrunc(min, -abs_b)) {
overflow.* = 1;
}
}
@@ -45,11 +44,6 @@ pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 {
return r;
}
-pub extern fn __muloti4_windows_x86_64(a: *const i128, b: *const i128, overflow: *c_int) void {
- @setRuntimeSafety(builtin.is_test);
- compiler_rt.setXmm0(i128, __muloti4(a.*, b.*, overflow));
-}
-
test "import muloti4" {
_ = @import("muloti4_test.zig");
}
diff --git a/std/special/compiler_rt/multi3.zig b/std/special/compiler_rt/multi3.zig
index a0c84adaf4..799b1f575d 100644
--- a/std/special/compiler_rt/multi3.zig
+++ b/std/special/compiler_rt/multi3.zig
@@ -14,9 +14,9 @@ pub extern fn __multi3(a: i128, b: i128) i128 {
return r.all;
}
-pub extern fn __multi3_windows_x86_64(a: *const i128, b: *const i128) void {
- @setRuntimeSafety(builtin.is_test);
- compiler_rt.setXmm0(i128, __multi3(a.*, b.*));
+const v128 = @Vector(2, u64);
+pub extern fn __multi3_windows_x86_64(a: v128, b: v128) v128 {
+ return @bitCast(v128, @inlineCall(__multi3, @bitCast(i128, a), @bitCast(i128, b)));
}
fn __mulddi3(a: u64, b: u64) i128 {
diff --git a/std/special/compiler_rt/stack_probe.zig b/std/special/compiler_rt/stack_probe.zig
new file mode 100644
index 0000000000..71a349d2a8
--- /dev/null
+++ b/std/special/compiler_rt/stack_probe.zig
@@ -0,0 +1,206 @@
+const builtin = @import("builtin");
+
+// Zig's own stack-probe routine (available only on x86 and x86_64)
+pub nakedcc fn zig_probe_stack() void {
+ @setRuntimeSafety(false);
+
+ // Versions of the Linux kernel before 5.1 treat any access below SP as
+ // invalid so let's update it on the go, otherwise we'll get a segfault
+ // instead of triggering the stack growth.
+
+ switch (builtin.arch) {
+ .x86_64 => {
+ // %rax = probe length, %rsp = stack pointer
+ asm volatile (
+ \\ push %%rcx
+ \\ mov %%rax, %%rcx
+ \\ cmp $0x1000,%%rcx
+ \\ jb 2f
+ \\ 1:
+ \\ sub $0x1000,%%rsp
+ \\ orl $0,16(%%rsp)
+ \\ sub $0x1000,%%rcx
+ \\ cmp $0x1000,%%rcx
+ \\ ja 1b
+ \\ 2:
+ \\ sub %%rcx, %%rsp
+ \\ orl $0,16(%%rsp)
+ \\ add %%rax,%%rsp
+ \\ pop %%rcx
+ \\ ret
+ );
+ },
+ .i386 => {
+ // %eax = probe length, %esp = stack pointer
+ asm volatile (
+ \\ push %%ecx
+ \\ mov %%eax, %%ecx
+ \\ cmp $0x1000,%%ecx
+ \\ jb 2f
+ \\ 1:
+ \\ sub $0x1000,%%esp
+ \\ orl $0,8(%%esp)
+ \\ sub $0x1000,%%ecx
+ \\ cmp $0x1000,%%ecx
+ \\ ja 1b
+ \\ 2:
+ \\ sub %%ecx, %%esp
+ \\ orl $0,8(%%esp)
+ \\ add %%eax,%%esp
+ \\ pop %%ecx
+ \\ ret
+ );
+ },
+ else => { }
+ }
+
+ unreachable;
+}
+
+fn win_probe_stack_only() void {
+ @setRuntimeSafety(false);
+
+ switch (builtin.arch) {
+ .x86_64 => {
+ asm volatile (
+ \\ push %%rcx
+ \\ push %%rax
+ \\ cmp $0x1000,%%rax
+ \\ lea 24(%%rsp),%%rcx
+ \\ jb 1f
+ \\ 2:
+ \\ sub $0x1000,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ sub $0x1000,%%rax
+ \\ cmp $0x1000,%%rax
+ \\ ja 2b
+ \\ 1:
+ \\ sub %%rax,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ pop %%rax
+ \\ pop %%rcx
+ \\ ret
+ );
+ },
+ .i386 => {
+ asm volatile (
+ \\ push %%ecx
+ \\ push %%eax
+ \\ cmp $0x1000,%%eax
+ \\ lea 12(%%esp),%%ecx
+ \\ jb 1f
+ \\ 2:
+ \\ sub $0x1000,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ sub $0x1000,%%eax
+ \\ cmp $0x1000,%%eax
+ \\ ja 2b
+ \\ 1:
+ \\ sub %%eax,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ pop %%eax
+ \\ pop %%ecx
+ \\ ret
+ );
+ },
+ else => { }
+ }
+
+ unreachable;
+}
+
+fn win_probe_stack_adjust_sp() void {
+ @setRuntimeSafety(false);
+
+ switch (builtin.arch) {
+ .x86_64 => {
+ asm volatile (
+ \\ push %%rcx
+ \\ cmp $0x1000,%%rax
+ \\ lea 16(%%rsp),%%rcx
+ \\ jb 1f
+ \\ 2:
+ \\ sub $0x1000,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ sub $0x1000,%%rax
+ \\ cmp $0x1000,%%rax
+ \\ ja 2b
+ \\ 1:
+ \\ sub %%rax,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\
+ \\ lea 8(%%rsp),%%rax
+ \\ mov %%rcx,%%rsp
+ \\ mov -8(%%rax),%%rcx
+ \\ push (%%rax)
+ \\ sub %%rsp,%%rax
+ \\ ret
+ );
+ },
+ .i386 => {
+ asm volatile (
+ \\ push %%ecx
+ \\ cmp $0x1000,%%eax
+ \\ lea 8(%%esp),%%ecx
+ \\ jb 1f
+ \\ 2:
+ \\ sub $0x1000,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ sub $0x1000,%%eax
+ \\ cmp $0x1000,%%eax
+ \\ ja 2b
+ \\ 1:
+ \\ sub %%eax,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\
+ \\ lea 4(%%esp),%%eax
+ \\ mov %%ecx,%%esp
+ \\ mov -4(%%eax),%%ecx
+ \\ push (%%eax)
+ \\ sub %%esp,%%eax
+ \\ ret
+ );
+ },
+ else => { },
+ }
+
+ unreachable;
+}
+
+// Windows has a multitude of stack-probing functions with similar names and
+// slightly different behaviours: some behave as alloca() and update the stack
+// pointer after probing the stack, other do not.
+//
+// Function name | Adjusts the SP? |
+// | x86 | x86_64 |
+// ----------------------------------------
+// _chkstk (_alloca) | yes | yes |
+// __chkstk | yes | no |
+// __chkstk_ms | no | no |
+// ___chkstk (__alloca) | yes | yes |
+// ___chkstk_ms | no | no |
+
+pub nakedcc fn _chkstk() void {
+ @setRuntimeSafety(false);
+ @inlineCall(win_probe_stack_adjust_sp);
+}
+pub nakedcc fn __chkstk() void {
+ @setRuntimeSafety(false);
+ switch (builtin.arch) {
+ .i386 => @inlineCall(win_probe_stack_adjust_sp),
+ .x86_64 => @inlineCall(win_probe_stack_only),
+ else => unreachable
+ }
+}
+pub nakedcc fn ___chkstk() void {
+ @setRuntimeSafety(false);
+ @inlineCall(win_probe_stack_adjust_sp);
+}
+pub nakedcc fn __chkstk_ms() void {
+ @setRuntimeSafety(false);
+ @inlineCall(win_probe_stack_only);
+}
+pub nakedcc fn ___chkstk_ms() void {
+ @setRuntimeSafety(false);
+ @inlineCall(win_probe_stack_only);
+}
diff --git a/std/special/compiler_rt/truncXfYf2.zig b/std/special/compiler_rt/truncXfYf2.zig
index b385090a93..e4c4aa38a7 100644
--- a/std/special/compiler_rt/truncXfYf2.zig
+++ b/std/special/compiler_rt/truncXfYf2.zig
@@ -16,6 +16,10 @@ pub extern fn __trunctfdf2(a: f128) f64 {
return truncXfYf2(f64, f128, a);
}
+pub extern fn __truncdfsf2(a: f64) f32 {
+ return truncXfYf2(f32, f64, a);
+}
+
inline fn truncXfYf2(comptime dst_t: type, comptime src_t: type, a: src_t) dst_t {
const src_rep_t = @IntType(false, @typeInfo(src_t).Float.bits);
const dst_rep_t = @IntType(false, @typeInfo(dst_t).Float.bits);
diff --git a/std/special/compiler_rt/truncXfYf2_test.zig b/std/special/compiler_rt/truncXfYf2_test.zig
index 429372c3f1..eccf7efb7e 100644
--- a/std/special/compiler_rt/truncXfYf2_test.zig
+++ b/std/special/compiler_rt/truncXfYf2_test.zig
@@ -200,3 +200,40 @@ test "trunctfdf2" {
test__trunctfdf2(0x1.2f34dd5f437e849b4baab754cdefp+4534, 0x7ff0000000000000);
test__trunctfdf2(0x1.edcbff8ad76ab5bf46463233214fp-435, 0x24cedcbff8ad76ab);
}
+
+const __truncdfsf2 = @import("truncXfYf2.zig").__truncdfsf2;
+
+fn test__truncdfsf2(a: f64, expected: u32) void {
+ const x = __truncdfsf2(a);
+
+ const rep = @bitCast(u32, x);
+ if (rep == expected) {
+ return;
+ }
+ // test other possible NaN representation(signal NaN)
+ else if (expected == 0x7fc00000) {
+ if ((rep & 0x7f800000) == 0x7f800000 and (rep & 0x7fffff) > 0) {
+ return;
+ }
+ }
+
+ @import("std").debug.warn("got 0x{x} wanted 0x{x}\n", rep, expected);
+
+ @panic("__trunctfsf2 test failure");
+}
+
+test "truncdfsf2" {
+ // nan & qnan
+ test__truncdfsf2(@bitCast(f64, u64(0x7ff8000000000000)), 0x7fc00000);
+ test__truncdfsf2(@bitCast(f64, u64(0x7ff0000000000001)), 0x7fc00000);
+ // inf
+ test__truncdfsf2(@bitCast(f64, u64(0x7ff0000000000000)), 0x7f800000);
+ test__truncdfsf2(@bitCast(f64, u64(0xfff0000000000000)), 0xff800000);
+
+ test__truncdfsf2(0.0, 0x0);
+ test__truncdfsf2(1.0, 0x3f800000);
+ test__truncdfsf2(-1.0, 0xbf800000);
+
+ // huge number becomes inf
+ test__truncdfsf2(340282366920938463463374607431768211456.0, 0x7f800000);
+}
diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig
index 6a037d3bae..c74dff512d 100644
--- a/std/special/compiler_rt/udivmodti4.zig
+++ b/std/special/compiler_rt/udivmodti4.zig
@@ -7,9 +7,10 @@ pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 {
return udivmod(u128, a, b, maybe_rem);
}
-pub extern fn __udivmodti4_windows_x86_64(a: *const u128, b: *const u128, maybe_rem: ?*u128) void {
+const v128 = @Vector(2, u64);
+pub extern fn __udivmodti4_windows_x86_64(a: v128, b: v128, maybe_rem: ?*u128) v128 {
@setRuntimeSafety(builtin.is_test);
- compiler_rt.setXmm0(u128, udivmod(u128, a.*, b.*, maybe_rem));
+ return @bitCast(v128, udivmod(u128, @bitCast(u128, a), @bitCast(u128, b), maybe_rem));
}
test "import udivmodti4" {
diff --git a/std/special/compiler_rt/udivti3.zig b/std/special/compiler_rt/udivti3.zig
index 510e21ac1d..ab451859bf 100644
--- a/std/special/compiler_rt/udivti3.zig
+++ b/std/special/compiler_rt/udivti3.zig
@@ -6,7 +6,8 @@ pub extern fn __udivti3(a: u128, b: u128) u128 {
return udivmodti4.__udivmodti4(a, b, null);
}
-pub extern fn __udivti3_windows_x86_64(a: *const u128, b: *const u128) void {
+const v128 = @Vector(2, u64);
+pub extern fn __udivti3_windows_x86_64(a: v128, b: v128) v128 {
@setRuntimeSafety(builtin.is_test);
- udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
+ return udivmodti4.__udivmodti4_windows_x86_64(a, b, null);
}
diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig
index 12aca8b036..7add0b2ffe 100644
--- a/std/special/compiler_rt/umodti3.zig
+++ b/std/special/compiler_rt/umodti3.zig
@@ -9,7 +9,7 @@ pub extern fn __umodti3(a: u128, b: u128) u128 {
return r;
}
-pub extern fn __umodti3_windows_x86_64(a: *const u128, b: *const u128) void {
- @setRuntimeSafety(builtin.is_test);
- compiler_rt.setXmm0(u128, __umodti3(a.*, b.*));
+const v128 = @Vector(2, u64);
+pub extern fn __umodti3_windows_x86_64(a: v128, b: v128) v128 {
+ return @bitCast(v128, @inlineCall(__umodti3, @bitCast(u128, a), @bitCast(u128, b)));
}
diff --git a/std/special/fmt_runner.zig b/std/special/fmt_runner.zig
deleted file mode 100644
index f0ed6704ed..0000000000
--- a/std/special/fmt_runner.zig
+++ /dev/null
@@ -1,260 +0,0 @@
-const std = @import("std");
-const builtin = @import("builtin");
-
-const os = std.os;
-const io = std.io;
-const mem = std.mem;
-const Allocator = mem.Allocator;
-const ArrayList = std.ArrayList;
-const Buffer = std.Buffer;
-const ast = std.zig.ast;
-
-const arg = @import("fmt/arg.zig");
-const self_hosted_main = @import("fmt/main.zig");
-const Args = arg.Args;
-const Flag = arg.Flag;
-const errmsg = @import("fmt/errmsg.zig");
-
-var stderr_file: os.File = undefined;
-var stderr: *io.OutStream(os.File.WriteError) = undefined;
-var stdout: *io.OutStream(os.File.WriteError) = undefined;
-
-// This brings `zig fmt` to stage 1.
-pub fn main() !void {
- // Here we use an ArenaAllocator backed by a DirectAllocator because `zig fmt` is a short-lived,
- // one shot program. We don't need to waste time freeing memory and finding places to squish
- // bytes into. So we free everything all at once at the very end.
- var direct_allocator = std.heap.DirectAllocator.init();
- var arena = std.heap.ArenaAllocator.init(&direct_allocator.allocator);
- const allocator = &arena.allocator;
-
- var stdout_file = try std.io.getStdOut();
- var stdout_out_stream = stdout_file.outStream();
- stdout = &stdout_out_stream.stream;
-
- stderr_file = try std.io.getStdErr();
- var stderr_out_stream = stderr_file.outStream();
- stderr = &stderr_out_stream.stream;
- const args = try std.os.argsAlloc(allocator);
-
- var flags = try Args.parse(allocator, self_hosted_main.args_fmt_spec, args[1..]);
- defer flags.deinit();
-
- if (flags.present("help")) {
- try stdout.write(self_hosted_main.usage_fmt);
- os.exit(0);
- }
-
- const color = blk: {
- if (flags.single("color")) |color_flag| {
- if (mem.eql(u8, color_flag, "auto")) {
- break :blk errmsg.Color.Auto;
- } else if (mem.eql(u8, color_flag, "on")) {
- break :blk errmsg.Color.On;
- } else if (mem.eql(u8, color_flag, "off")) {
- break :blk errmsg.Color.Off;
- } else unreachable;
- } else {
- break :blk errmsg.Color.Auto;
- }
- };
-
- if (flags.present("stdin")) {
- if (flags.positionals.len != 0) {
- try stderr.write("cannot use --stdin with positional arguments\n");
- os.exit(1);
- }
-
- var stdin_file = try io.getStdIn();
- var stdin = stdin_file.inStream();
-
- const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size);
- defer allocator.free(source_code);
-
- var tree = std.zig.parse(allocator, source_code) catch |err| {
- try stderr.print("error parsing stdin: {}\n", err);
- os.exit(1);
- };
- defer tree.deinit();
-
- var error_it = tree.errors.iterator(0);
- while (error_it.next()) |parse_error| {
- try printErrMsgToFile(allocator, parse_error, &tree, "<stdin>", stderr_file, color);
- }
- if (tree.errors.len != 0) {
- os.exit(1);
- }
- if (flags.present("check")) {
- const anything_changed = try std.zig.render(allocator, io.null_out_stream, &tree);
- const code = if (anything_changed) u8(1) else u8(0);
- os.exit(code);
- }
-
- _ = try std.zig.render(allocator, stdout, &tree);
- return;
- }
-
- if (flags.positionals.len == 0) {
- try stderr.write("expected at least one source file argument\n");
- os.exit(1);
- }
-
- var fmt = Fmt{
- .seen = Fmt.SeenMap.init(allocator),
- .any_error = false,
- .color = color,
- .allocator = allocator,
- };
-
- const check_mode = flags.present("check");
-
- for (flags.positionals.toSliceConst()) |file_path| {
- try fmtPath(&fmt, file_path, check_mode);
- }
- if (fmt.any_error) {
- os.exit(1);
- }
-}
-
-const FmtError = error{
- SystemResources,
- OperationAborted,
- IoPending,
- BrokenPipe,
- Unexpected,
- WouldBlock,
- FileClosed,
- DestinationAddressRequired,
- DiskQuota,
- FileTooBig,
- InputOutput,
- NoSpaceLeft,
- AccessDenied,
- OutOfMemory,
- RenameAcrossMountPoints,
- ReadOnlyFileSystem,
- LinkQuotaExceeded,
- FileBusy,
-} || os.File.OpenError;
-
-fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
- const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
- defer fmt.allocator.free(file_path);
-
- if (try fmt.seen.put(file_path, {})) |_| return;
-
- const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => {
- // TODO make event based (and dir.next())
- var dir = try std.os.Dir.open(fmt.allocator, file_path);
- defer dir.close();
-
- while (try dir.next()) |entry| {
- if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
- const full_path = try os.path.join(fmt.allocator, [][]const u8{ file_path, entry.name });
- try fmtPath(fmt, full_path, check_mode);
- }
- }
- return;
- },
- else => {
- // TODO lock stderr printing
- try stderr.print("unable to open '{}': {}\n", file_path, err);
- fmt.any_error = true;
- return;
- },
- };
- defer fmt.allocator.free(source_code);
-
- var tree = std.zig.parse(fmt.allocator, source_code) catch |err| {
- try stderr.print("error parsing file '{}': {}\n", file_path, err);
- fmt.any_error = true;
- return;
- };
- defer tree.deinit();
-
- var error_it = tree.errors.iterator(0);
- while (error_it.next()) |parse_error| {
- try printErrMsgToFile(fmt.allocator, parse_error, &tree, file_path, stderr_file, fmt.color);
- }
- if (tree.errors.len != 0) {
- fmt.any_error = true;
- return;
- }
-
- if (check_mode) {
- const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, &tree);
- if (anything_changed) {
- try stderr.print("{}\n", file_path);
- fmt.any_error = true;
- }
- } else {
- // TODO make this evented
- const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path);
- defer baf.destroy();
-
- const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), &tree);
- if (anything_changed) {
- try stderr.print("{}\n", file_path);
- try baf.finish();
- }
- }
-}
-
-const Fmt = struct {
- seen: SeenMap,
- any_error: bool,
- color: errmsg.Color,
- allocator: *mem.Allocator,
-
- const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8);
-};
-
-fn printErrMsgToFile(allocator: *mem.Allocator, parse_error: *const ast.Error, tree: *ast.Tree,
- path: []const u8, file: os.File, color: errmsg.Color,) !void
-{
- const color_on = switch (color) {
- errmsg.Color.Auto => file.isTty(),
- errmsg.Color.On => true,
- errmsg.Color.Off => false,
- };
- const lok_token = parse_error.loc();
- const span = errmsg.Span{
- .first = lok_token,
- .last = lok_token,
- };
-
- const first_token = tree.tokens.at(span.first);
- const last_token = tree.tokens.at(span.last);
- const start_loc = tree.tokenLocationPtr(0, first_token);
- const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
-
- var text_buf = try std.Buffer.initSize(allocator, 0);
- var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
- try parse_error.render(&tree.tokens, out_stream);
- const text = text_buf.toOwnedSlice();
-
- const stream = &file.outStream().stream;
- if (!color_on) {
- try stream.print(
- "{}:{}:{}: error: {}\n",
- path,
- start_loc.line + 1,
- start_loc.column + 1,
- text,
- );
- return;
- }
-
- try stream.print(
- "{}:{}:{}: error: {}\n{}\n",
- path,
- start_loc.line + 1,
- start_loc.column + 1,
- text,
- tree.source[start_loc.line_start..start_loc.line_end],
- );
- try stream.writeByteNTimes(' ', start_loc.column);
- try stream.writeByteNTimes('~', last_token.end - first_token.start);
- try stream.write("\n");
-}
diff --git a/std/special/panic.zig b/std/special/panic.zig
index 7cb7143955..40b1d5e7fe 100644
--- a/std/special/panic.zig
+++ b/std/special/panic.zig
@@ -9,10 +9,15 @@ const std = @import("std");
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
@setCold(true);
switch (builtin.os) {
- // TODO: fix panic in zen.
+ // TODO: fix panic in zen
builtin.Os.freestanding, builtin.Os.zen => {
while (true) {}
},
+ builtin.Os.wasi => {
+ std.debug.warn("{}", msg);
+ _ = std.os.wasi.proc_raise(std.os.wasi.SIGABRT);
+ unreachable;
+ },
builtin.Os.uefi => {
// TODO look into using the debug info and logging helpful messages
std.os.abort();
diff --git a/std/special/test_runner.zig b/std/special/test_runner.zig
index 36b098bd61..db01293059 100644
--- a/std/special/test_runner.zig
+++ b/std/special/test_runner.zig
@@ -8,7 +8,7 @@ pub fn main() !void {
var ok_count: usize = 0;
var skip_count: usize = 0;
for (test_fn_list) |test_fn, i| {
- warn("Test {}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
+ warn("{}/{} {}...", i + 1, test_fn_list.len, test_fn.name);
if (test_fn.func()) |_| {
ok_count += 1;
diff --git a/std/std.zig b/std/std.zig
index f68edf6435..8ec042fdb8 100644
--- a/std/std.zig
+++ b/std/std.zig
@@ -9,6 +9,10 @@ pub const DynLib = @import("dynamic_library.zig").DynLib;
pub const HashMap = @import("hash_map.zig").HashMap;
pub const LinkedList = @import("linked_list.zig").LinkedList;
pub const Mutex = @import("mutex.zig").Mutex;
+pub const PackedIntArrayEndian = @import("packed_int_array.zig").PackedIntArrayEndian;
+pub const PackedIntArray = @import("packed_int_array.zig").PackedIntArray;
+pub const PackedIntSliceEndian = @import("packed_int_array.zig").PackedIntSliceEndian;
+pub const PackedIntSlice = @import("packed_int_array.zig").PackedIntSlice;
pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue;
pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex;
pub const SegmentedList = @import("segmented_list.zig").SegmentedList;
@@ -87,6 +91,7 @@ test "std" {
_ = @import("net.zig");
_ = @import("os.zig");
_ = @import("pdb.zig");
+ _ = @import("packed_int_array.zig");
_ = @import("priority_queue.zig");
_ = @import("rand.zig");
_ = @import("sort.zig");
@@ -94,4 +99,6 @@ test "std" {
_ = @import("unicode.zig");
_ = @import("valgrind.zig");
_ = @import("zig.zig");
+
+ _ = @import("debug/leb128.zig");
}
diff --git a/std/zig/ast.zig b/std/zig/ast.zig
index 77e487f1ef..75a811220f 100644
--- a/std/zig/ast.zig
+++ b/std/zig/ast.zig
@@ -18,7 +18,11 @@ pub const Tree = struct {
pub const ErrorList = SegmentedList(Error, 0);
pub fn deinit(self: *Tree) void {
- self.arena_allocator.deinit();
+ // Here we copy the arena allocator into stack memory, because
+ // otherwise it would destroy itself while it was still working.
+ var arena_allocator = self.arena_allocator;
+ arena_allocator.deinit();
+ // self is destroyed
}
pub fn renderError(self: *Tree, parse_error: *Error, stream: var) !void {
@@ -551,7 +555,6 @@ pub const Node = struct {
doc_comments: ?*DocComment,
decls: DeclList,
eof_token: TokenIndex,
- shebang: ?TokenIndex,
pub const DeclList = SegmentedList(*Node, 4);
@@ -563,7 +566,6 @@ pub const Node = struct {
}
pub fn firstToken(self: *const Root) TokenIndex {
- if (self.shebang) |shebang| return shebang;
return if (self.decls.len == 0) self.eof_token else (self.decls.at(0).*).firstToken();
}
@@ -2307,7 +2309,6 @@ test "iterate" {
.doc_comments = null,
.decls = Node.Root.DeclList.init(std.debug.global_allocator),
.eof_token = 0,
- .shebang = null,
};
var base = &root.base;
testing.expect(base.iterate(0) == null);
diff --git a/std/zig/parse.zig b/std/zig/parse.zig
index 96aec714ab..efe83a7dac 100644
--- a/std/zig/parse.zig
+++ b/std/zig/parse.zig
@@ -9,7 +9,7 @@ const Error = ast.Error;
/// Result should be freed with tree.deinit() when there are
/// no more references to any of the tokens or nodes.
-pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
+pub fn parse(allocator: *mem.Allocator, source: []const u8) !*ast.Tree {
var tree_arena = std.heap.ArenaAllocator.init(allocator);
errdefer tree_arena.deinit();
@@ -22,12 +22,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
.base = ast.Node{ .id = ast.Node.Id.Root },
.decls = ast.Node.Root.DeclList.init(arena),
.doc_comments = null,
- .shebang = null,
// initialized when we get the eof token
.eof_token = undefined,
};
- var tree = ast.Tree{
+ const tree = try arena.create(ast.Tree);
+ tree.* = ast.Tree{
.source = source,
.root_node = root_node,
.arena_allocator = tree_arena,
@@ -43,15 +43,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
var tok_it = tree.tokens.iterator(0);
- // skip over shebang line
- shebang: {
- const shebang_tok_index = tok_it.index;
- const shebang_tok_ptr = tok_it.peek() orelse break :shebang;
- if (shebang_tok_ptr.id != Token.Id.ShebangLine) break :shebang;
- root_node.shebang = shebang_tok_index;
- _ = tok_it.next();
- }
-
// skip over line comments at the top of the file
while (true) {
const next_tok = tok_it.peek() orelse break;
@@ -67,9 +58,9 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
switch (state) {
State.TopLevel => {
- const comments = try eatDocComments(arena, &tok_it, &tree);
+ const comments = try eatDocComments(arena, &tok_it, tree);
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -150,7 +141,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State.TopLevel) catch unreachable;
try stack.append(State{
.TopLevelExtern = TopLevelDeclCtx{
@@ -166,7 +157,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.TopLevelExtern => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -201,7 +192,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .TopLevelDecl = ctx }) catch unreachable;
continue;
},
@@ -209,11 +200,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.TopLevelLibname => |ctx| {
const lib_name = blk: {
- const lib_name_token = nextToken(&tok_it, &tree);
+ const lib_name_token = nextToken(&tok_it, tree);
const lib_name_token_index = lib_name_token.index;
const lib_name_token_ptr = lib_name_token.ptr;
- break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) orelse {
- prevToken(&tok_it, &tree);
+ break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, tree)) orelse {
+ prevToken(&tok_it, tree);
break :blk null;
};
};
@@ -230,7 +221,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ThreadLocal => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -256,7 +247,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.TopLevelDecl => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -397,7 +388,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.TopLevelExternOrField => |ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |identifier| {
+ if (eatToken(&tok_it, tree, Token.Id.Identifier)) |identifier| {
const node = try arena.create(ast.Node.StructField);
node.* = ast.Node.StructField{
.base = ast.Node{ .id = ast.Node.Id.StructField },
@@ -434,11 +425,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.FieldInitValue => |ctx| {
- const eq_tok = nextToken(&tok_it, &tree);
+ const eq_tok = nextToken(&tok_it, tree);
const eq_tok_index = eq_tok.index;
const eq_tok_ptr = eq_tok.ptr;
if (eq_tok_ptr.id != Token.Id.Equal) {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
stack.append(State{ .Expression = ctx }) catch unreachable;
@@ -446,7 +437,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerKind => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
const node = try arena.create(ast.Node.ContainerDecl);
@@ -479,7 +470,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerInitArgStart => |container_decl| {
- if (eatToken(&tok_it, &tree, Token.Id.LParen) == null) {
+ if (eatToken(&tok_it, tree, Token.Id.LParen) == null) {
continue;
}
@@ -489,24 +480,24 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerInitArg => |container_decl| {
- const init_arg_token = nextToken(&tok_it, &tree);
+ const init_arg_token = nextToken(&tok_it, tree);
const init_arg_token_index = init_arg_token.index;
const init_arg_token_ptr = init_arg_token.ptr;
switch (init_arg_token_ptr.id) {
Token.Id.Keyword_enum => {
container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg{ .Enum = null };
- const lparen_tok = nextToken(&tok_it, &tree);
+ const lparen_tok = nextToken(&tok_it, tree);
const lparen_tok_index = lparen_tok.index;
const lparen_tok_ptr = lparen_tok.ptr;
if (lparen_tok_ptr.id == Token.Id.LParen) {
try stack.append(State{ .ExpectToken = Token.Id.RParen });
try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &container_decl.init_arg_expr.Enum } });
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
}
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
container_decl.init_arg_expr = ast.Node.ContainerDecl.InitArg{ .Type = undefined };
stack.append(State{ .Expression = OptionalCtx{ .Required = &container_decl.init_arg_expr.Type } }) catch unreachable;
},
@@ -515,8 +506,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ContainerDecl => |container_decl| {
- const comments = try eatDocComments(arena, &tok_it, &tree);
- const token = nextToken(&tok_it, &tree);
+ const comments = try eatDocComments(arena, &tok_it, tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -629,6 +620,35 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
});
continue;
},
+ Token.Id.Keyword_comptime => {
+ const block = try arena.create(ast.Node.Block);
+ block.* = ast.Node.Block{
+ .base = ast.Node{ .id = ast.Node.Id.Block },
+ .label = null,
+ .lbrace = undefined,
+ .statements = ast.Node.Block.StatementList.init(arena),
+ .rbrace = undefined,
+ };
+
+ const node = try arena.create(ast.Node.Comptime);
+ node.* = ast.Node.Comptime{
+ .base = ast.Node{ .id = ast.Node.Id.Comptime },
+ .comptime_token = token_index,
+ .expr = &block.base,
+ .doc_comments = comments,
+ };
+ try container_decl.fields_and_decls.push(&node.base);
+
+ stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
+ try stack.append(State{ .Block = block });
+ try stack.append(State{
+ .ExpectTokenSave = ExpectTokenSave{
+ .id = Token.Id.LBrace,
+ .ptr = &block.lbrace,
+ },
+ });
+ continue;
+ },
Token.Id.RBrace => {
if (comments != null) {
((try tree.errors.addOne())).* = Error{ .UnattachedDocComment = Error.UnattachedDocComment{ .token = token_index } };
@@ -638,7 +658,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .ContainerDecl = container_decl }) catch unreachable;
try stack.append(State{
.TopLevelExtern = TopLevelDeclCtx{
@@ -690,7 +710,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.VarDeclAlign => |var_decl| {
try stack.append(State{ .VarDeclSection = var_decl });
- const next_token = nextToken(&tok_it, &tree);
+ const next_token = nextToken(&tok_it, tree);
const next_token_index = next_token.index;
const next_token_ptr = next_token.ptr;
if (next_token_ptr.id == Token.Id.Keyword_align) {
@@ -700,13 +720,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
State.VarDeclSection => |var_decl| {
try stack.append(State{ .VarDeclEq = var_decl });
- const next_token = nextToken(&tok_it, &tree);
+ const next_token = nextToken(&tok_it, tree);
const next_token_index = next_token.index;
const next_token_ptr = next_token.ptr;
if (next_token_ptr.id == Token.Id.Keyword_linksection) {
@@ -716,11 +736,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
State.VarDeclEq => |var_decl| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -742,7 +762,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.VarDeclSemiColon => |var_decl| {
- const semicolon_token = nextToken(&tok_it, &tree);
+ const semicolon_token = nextToken(&tok_it, tree);
if (semicolon_token.ptr.id != Token.Id.Semicolon) {
((try tree.errors.addOne())).* = Error{
@@ -756,18 +776,18 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
var_decl.semicolon_token = semicolon_token.index;
- if (eatToken(&tok_it, &tree, Token.Id.DocComment)) |doc_comment_token| {
+ if (eatToken(&tok_it, tree, Token.Id.DocComment)) |doc_comment_token| {
const loc = tree.tokenLocation(semicolon_token.ptr.end, doc_comment_token);
if (loc.line == 0) {
try pushDocComment(arena, doc_comment_token, &var_decl.doc_comments);
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
}
}
},
State.FnDef => |fn_proto| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -796,7 +816,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ .ParamDecl = fn_proto });
try stack.append(State{ .ExpectToken = Token.Id.LParen });
- if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |name_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Identifier)) |name_token| {
fn_proto.name_token = name_token;
}
continue;
@@ -804,7 +824,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.FnProtoAlign => |fn_proto| {
stack.append(State{ .FnProtoSection = fn_proto }) catch unreachable;
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_align)) |align_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_align)) |align_token| {
try stack.append(State{ .ExpectToken = Token.Id.RParen });
try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &fn_proto.align_expr } });
try stack.append(State{ .ExpectToken = Token.Id.LParen });
@@ -814,7 +834,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.FnProtoSection => |fn_proto| {
stack.append(State{ .FnProtoReturnType = fn_proto }) catch unreachable;
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_linksection)) |align_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_linksection)) |align_token| {
try stack.append(State{ .ExpectToken = Token.Id.RParen });
try stack.append(State{ .Expression = OptionalCtx{ .RequiredNull = &fn_proto.section_expr } });
try stack.append(State{ .ExpectToken = Token.Id.LParen });
@@ -822,7 +842,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.FnProtoReturnType => |fn_proto| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -845,7 +865,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
fn_proto.return_type = ast.Node.FnProto.ReturnType{ .Explicit = undefined };
stack.append(State{ .TypeExprBegin = OptionalCtx{ .Required = &fn_proto.return_type.Explicit } }) catch unreachable;
continue;
@@ -854,8 +874,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ParamDecl => |fn_proto| {
- const comments = try eatDocComments(arena, &tok_it, &tree);
- if (eatToken(&tok_it, &tree, Token.Id.RParen)) |_| {
+ const comments = try eatDocComments(arena, &tok_it, tree);
+ if (eatToken(&tok_it, tree, Token.Id.RParen)) |_| {
continue;
}
const param_decl = try arena.create(ast.Node.ParamDecl);
@@ -881,9 +901,9 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ParamDeclAliasOrComptime => |param_decl| {
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_comptime)) |comptime_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_comptime)) |comptime_token| {
param_decl.comptime_token = comptime_token;
- } else if (eatToken(&tok_it, &tree, Token.Id.Keyword_noalias)) |noalias_token| {
+ } else if (eatToken(&tok_it, tree, Token.Id.Keyword_noalias)) |noalias_token| {
param_decl.noalias_token = noalias_token;
}
continue;
@@ -891,20 +911,20 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.ParamDeclName => |param_decl| {
// TODO: Here, we eat two tokens in one state. This means that we can't have
// comments between these two tokens.
- if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| {
- if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
+ if (eatToken(&tok_it, tree, Token.Id.Identifier)) |ident_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Colon)) |_| {
param_decl.name_token = ident_token;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
}
}
continue;
},
State.ParamDeclEnd => |ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
+ if (eatToken(&tok_it, tree, Token.Id.Ellipsis3)) |ellipsis3| {
ctx.param_decl.var_args_token = ellipsis3;
- switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
+ switch (expectCommaOrEnd(&tok_it, tree, Token.Id.RParen)) {
ExpectCommaOrEndResult.end_token => |t| {
if (t == null) {
stack.append(State{ .ExpectToken = Token.Id.RParen }) catch unreachable;
@@ -924,7 +944,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ParamDeclComma => |fn_proto| {
- switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RParen)) {
+ switch (expectCommaOrEnd(&tok_it, tree, Token.Id.RParen)) {
ExpectCommaOrEndResult.end_token => |t| {
if (t == null) {
stack.append(State{ .ParamDecl = fn_proto }) catch unreachable;
@@ -939,7 +959,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.MaybeLabeledExpression => |ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.Colon)) |_| {
+ if (eatToken(&tok_it, tree, Token.Id.Colon)) |_| {
stack.append(State{
.LabeledExpression = LabelCtx{
.label = ctx.label,
@@ -953,7 +973,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.LabeledExpression => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1008,13 +1028,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
}
},
State.Inline => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1046,7 +1066,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
}
@@ -1103,7 +1123,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.Else => |dest| {
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_else)) |else_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_else)) |else_token| {
const node = try arena.create(ast.Node.Else);
node.* = ast.Node.Else{
.base = ast.Node{ .id = ast.Node.Id.Else },
@@ -1122,7 +1142,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.Block => |block| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1131,7 +1151,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .Block = block }) catch unreachable;
try stack.append(State{ .Statement = block });
@@ -1140,7 +1160,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.Statement => |block| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1197,7 +1217,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
const statement = try block.statements.addOne();
try stack.append(State{ .Semicolon = statement });
try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .Required = statement } });
@@ -1206,7 +1226,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.ComptimeStatement => |ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1226,8 +1246,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
+ prevToken(&tok_it, tree);
const statement = try ctx.block.statements.addOne();
try stack.append(State{ .Semicolon = statement });
try stack.append(State{ .Expression = OptionalCtx{ .Required = statement } });
@@ -1245,11 +1265,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.AsmOutputItems => |items| {
- const lbracket = nextToken(&tok_it, &tree);
+ const lbracket = nextToken(&tok_it, tree);
const lbracket_index = lbracket.index;
const lbracket_ptr = lbracket.ptr;
if (lbracket_ptr.id != Token.Id.LBracket) {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
@@ -1280,7 +1300,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.AsmOutputReturnOrType => |node| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1300,11 +1320,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.AsmInputItems => |items| {
- const lbracket = nextToken(&tok_it, &tree);
+ const lbracket = nextToken(&tok_it, tree);
const lbracket_index = lbracket.index;
const lbracket_ptr = lbracket.ptr;
if (lbracket_ptr.id != Token.Id.LBracket) {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
@@ -1335,16 +1355,16 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.AsmClobberItems => |items| {
- while (eatToken(&tok_it, &tree, Token.Id.StringLiteral)) |strlit| {
+ while (eatToken(&tok_it, tree, Token.Id.StringLiteral)) |strlit| {
try items.push(strlit);
- if (eatToken(&tok_it, &tree, Token.Id.Comma) == null)
+ if (eatToken(&tok_it, tree, Token.Id.Comma) == null)
break;
}
continue;
},
State.ExprListItemOrEnd => |list_state| {
- if (eatToken(&tok_it, &tree, list_state.end)) |token_index| {
+ if (eatToken(&tok_it, tree, list_state.end)) |token_index| {
(list_state.ptr).* = token_index;
continue;
}
@@ -1354,7 +1374,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ExprListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, &tree, list_state.end)) {
+ switch (expectCommaOrEnd(&tok_it, tree, list_state.end)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
(list_state.ptr).* = end;
continue;
@@ -1369,7 +1389,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.FieldInitListItemOrEnd => |list_state| {
- if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
+ if (eatToken(&tok_it, tree, Token.Id.RBrace)) |rbrace| {
(list_state.ptr).* = rbrace;
continue;
}
@@ -1401,7 +1421,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.FieldInitListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
+ switch (expectCommaOrEnd(&tok_it, tree, Token.Id.RBrace)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
(list_state.ptr).* = end;
continue;
@@ -1416,17 +1436,17 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.FieldListCommaOrEnd => |field_ctx| {
- const end_token = nextToken(&tok_it, &tree);
+ const end_token = nextToken(&tok_it, tree);
const end_token_index = end_token.index;
const end_token_ptr = end_token.ptr;
switch (end_token_ptr.id) {
Token.Id.Comma => {
- if (eatToken(&tok_it, &tree, Token.Id.DocComment)) |doc_comment_token| {
+ if (eatToken(&tok_it, tree, Token.Id.DocComment)) |doc_comment_token| {
const loc = tree.tokenLocation(end_token_ptr.end, doc_comment_token);
if (loc.line == 0) {
try pushDocComment(arena, doc_comment_token, field_ctx.doc_comments);
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
}
}
@@ -1449,7 +1469,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.ErrorTagListItemOrEnd => |list_state| {
- if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
+ if (eatToken(&tok_it, tree, Token.Id.RBrace)) |rbrace| {
(list_state.ptr).* = rbrace;
continue;
}
@@ -1461,7 +1481,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ErrorTagListCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
+ switch (expectCommaOrEnd(&tok_it, tree, Token.Id.RBrace)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
(list_state.ptr).* = end;
continue;
@@ -1476,12 +1496,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.SwitchCaseOrEnd => |list_state| {
- if (eatToken(&tok_it, &tree, Token.Id.RBrace)) |rbrace| {
+ if (eatToken(&tok_it, tree, Token.Id.RBrace)) |rbrace| {
(list_state.ptr).* = rbrace;
continue;
}
- const comments = try eatDocComments(arena, &tok_it, &tree);
+ const comments = try eatDocComments(arena, &tok_it, tree);
const node = try arena.create(ast.Node.SwitchCase);
node.* = ast.Node.SwitchCase{
.base = ast.Node{ .id = ast.Node.Id.SwitchCase },
@@ -1500,7 +1520,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.SwitchCaseCommaOrEnd => |list_state| {
- switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.RBrace)) {
+ switch (expectCommaOrEnd(&tok_it, tree, Token.Id.RBrace)) {
ExpectCommaOrEndResult.end_token => |maybe_end| if (maybe_end) |end| {
(list_state.ptr).* = end;
continue;
@@ -1516,7 +1536,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.SwitchCaseFirstItem => |switch_case| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (token_ptr.id == Token.Id.Keyword_else) {
@@ -1535,26 +1555,26 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
});
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .SwitchCaseItemCommaOrEnd = switch_case }) catch unreachable;
try stack.append(State{ .RangeExpressionBegin = OptionalCtx{ .Required = try switch_case.items.addOne() } });
continue;
}
},
State.SwitchCaseItemOrEnd => |switch_case| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
if (token.ptr.id == Token.Id.EqualAngleBracketRight) {
switch_case.arrow_token = token.index;
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .SwitchCaseItemCommaOrEnd = switch_case }) catch unreachable;
try stack.append(State{ .RangeExpressionBegin = OptionalCtx{ .Required = try switch_case.items.addOne() } });
continue;
}
},
State.SwitchCaseItemCommaOrEnd => |switch_case| {
- switch (expectCommaOrEnd(&tok_it, &tree, Token.Id.EqualAngleBracketRight)) {
+ switch (expectCommaOrEnd(&tok_it, tree, Token.Id.EqualAngleBracketRight)) {
ExpectCommaOrEndResult.end_token => |end_token| {
if (end_token) |t| {
switch_case.arrow_token = t;
@@ -1572,14 +1592,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.SuspendBody => |suspend_node| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
switch (token.ptr.id) {
Token.Id.Semicolon => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
Token.Id.LBrace => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } });
continue;
},
@@ -1589,7 +1609,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.AsyncAllocator => |async_node| {
- if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) {
+ if (eatToken(&tok_it, tree, Token.Id.AngleBracketLeft) == null) {
continue;
}
@@ -1630,7 +1650,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ExternType => |ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_fn)) |fn_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_fn)) |fn_token| {
const fn_proto = try arena.create(ast.Node.FnProto);
fn_proto.* = ast.Node.FnProto{
.base = ast.Node{ .id = ast.Node.Id.FnProto },
@@ -1663,7 +1683,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.SliceOrArrayAccess => |node| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1696,7 +1716,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
},
State.SliceOrArrayType => |node| {
- if (eatToken(&tok_it, &tree, Token.Id.RBracket)) |_| {
+ if (eatToken(&tok_it, tree, Token.Id.RBracket)) |_| {
node.op = ast.Node.PrefixOp.Op{
.SliceType = ast.Node.PrefixOp.PtrInfo{
.align_info = null,
@@ -1718,7 +1738,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.PtrTypeModifiers => |addr_of_info| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1768,14 +1788,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
}
},
State.AlignBitRange => |align_info| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
switch (token.ptr.id) {
Token.Id.Colon => {
align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined);
@@ -1798,7 +1818,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.Payload => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (token_ptr.id != Token.Id.Pipe) {
@@ -1812,7 +1832,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
@@ -1835,7 +1855,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.PointerPayload => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (token_ptr.id != Token.Id.Pipe) {
@@ -1849,7 +1869,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
@@ -1879,7 +1899,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.PointerIndexPayload => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (token_ptr.id != Token.Id.Pipe) {
@@ -1893,7 +1913,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
return tree;
}
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
@@ -1927,7 +1947,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.Expression => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -1981,7 +2001,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
if (!try parseBlockExpr(&stack, arena, opt_ctx, token_ptr.*, token_index)) {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .UnwrapExpressionBegin = opt_ctx }) catch unreachable;
}
continue;
@@ -1996,7 +2016,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.RangeExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
+ if (eatToken(&tok_it, tree, Token.Id.Ellipsis3)) |ellipsis3| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2019,7 +2039,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.AssignmentExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToAssignment(token_ptr.id)) |ass_id| {
@@ -2036,7 +2056,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ .Expression = OptionalCtx{ .Required = &node.rhs } });
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
},
@@ -2050,7 +2070,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.UnwrapExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToUnwrapExpr(token_ptr.id)) |unwrap_id| {
@@ -2072,7 +2092,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
},
@@ -2086,7 +2106,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.BoolOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_or)) |or_token| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2111,7 +2131,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.BoolAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_and)) |and_token| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2136,7 +2156,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.ComparisonExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToComparison(token_ptr.id)) |comp_id| {
@@ -2153,7 +2173,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ .BinaryOrExpressionBegin = OptionalCtx{ .Required = &node.rhs } });
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
},
@@ -2167,7 +2187,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.BinaryOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {
+ if (eatToken(&tok_it, tree, Token.Id.Pipe)) |pipe| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2192,7 +2212,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.BinaryXorExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {
+ if (eatToken(&tok_it, tree, Token.Id.Caret)) |caret| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2217,7 +2237,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.BinaryAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {
+ if (eatToken(&tok_it, tree, Token.Id.Ampersand)) |ampersand| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2242,7 +2262,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.BitShiftExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToBitShift(token_ptr.id)) |bitshift_id| {
@@ -2259,7 +2279,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ .AdditionExpressionBegin = OptionalCtx{ .Required = &node.rhs } });
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
},
@@ -2273,7 +2293,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.AdditionExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToAddition(token_ptr.id)) |add_id| {
@@ -2290,7 +2310,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ .MultiplyExpressionBegin = OptionalCtx{ .Required = &node.rhs } });
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
},
@@ -2304,7 +2324,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.MultiplyExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToMultiply(token_ptr.id)) |mult_id| {
@@ -2321,7 +2341,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ .CurlySuffixExpressionBegin = OptionalCtx{ .Required = &node.rhs } });
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
},
@@ -2386,7 +2406,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.TypeExprEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {
+ if (eatToken(&tok_it, tree, Token.Id.Bang)) |bang| {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2403,7 +2423,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.PrefixOpExpression => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (tokenIdToPrefixOp(token_ptr.id)) |prefix_id| {
@@ -2435,14 +2455,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}
continue;
} else {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{ .SuffixOpExpressionBegin = opt_ctx }) catch unreachable;
continue;
}
},
State.SuffixOpExpressionBegin => |opt_ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.Keyword_async)) |async_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Keyword_async)) |async_token| {
const async_node = try arena.create(ast.Node.AsyncAttribute);
async_node.* = ast.Node.AsyncAttribute{
.base = ast.Node{ .id = ast.Node.Id.AsyncAttribute },
@@ -2470,7 +2490,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
State.SuffixOpExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() orelse continue;
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
switch (token_ptr.id) {
@@ -2515,7 +2535,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Period => {
- if (eatToken(&tok_it, &tree, Token.Id.Asterisk)) |asterisk_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Asterisk)) |asterisk_token| {
const node = try arena.create(ast.Node.SuffixOp);
node.* = ast.Node.SuffixOp{
.base = ast.Node{ .id = ast.Node.Id.SuffixOp },
@@ -2527,7 +2547,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
continue;
}
- if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| {
+ if (eatToken(&tok_it, tree, Token.Id.QuestionMark)) |question_token| {
const node = try arena.create(ast.Node.SuffixOp);
node.* = ast.Node.SuffixOp{
.base = ast.Node{ .id = ast.Node.Id.SuffixOp },
@@ -2554,21 +2574,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
else => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
},
}
},
State.PrimaryExpression => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
switch (token.ptr.id) {
Token.Id.IntegerLiteral => {
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
continue;
},
Token.Id.Period => {
- const name_token = nextToken(&tok_it, &tree);
+ const name_token = nextToken(&tok_it, tree);
if (name_token.ptr.id != Token.Id.Identifier) {
((try tree.errors.addOne())).* = Error{
.ExpectedToken = Error.ExpectedToken{
@@ -2624,11 +2644,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
.result = null,
};
opt_ctx.store(&node.base);
- const next_token = nextToken(&tok_it, &tree);
+ const next_token = nextToken(&tok_it, tree);
const next_token_index = next_token.index;
const next_token_ptr = next_token.ptr;
if (next_token_ptr.id != Token.Id.Arrow) {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
continue;
}
node.result = ast.Node.PromiseType.Result{
@@ -2640,7 +2660,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
- opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) orelse unreachable);
+ opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, tree)) orelse unreachable);
continue;
},
Token.Id.LParen => {
@@ -2728,7 +2748,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
Token.Id.Keyword_struct, Token.Id.Keyword_union, Token.Id.Keyword_enum => {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
stack.append(State{
.ContainerKind = ContainerKindCtx{
.opt_ctx = opt_ctx,
@@ -2845,7 +2865,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
else => {
if (!try parseBlockExpr(&stack, arena, opt_ctx, token.ptr.*, token.index)) {
- prevToken(&tok_it, &tree);
+ prevToken(&tok_it, tree);
if (opt_ctx != OptionalCtx.Optional) {
((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token.index } };
return tree;
@@ -2857,7 +2877,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ErrorTypeOrSetDecl => |ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.LBrace) == null) {
+ if (eatToken(&tok_it, tree, Token.Id.LBrace) == null) {
const node = try arena.create(ast.Node.InfixOp);
node.* = ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp },
@@ -2895,11 +2915,11 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.StringLiteral => |opt_ctx| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
- opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) orelse {
- prevToken(&tok_it, &tree);
+ opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, tree)) orelse {
+ prevToken(&tok_it, tree);
if (opt_ctx != OptionalCtx.Optional) {
((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } };
return tree;
@@ -2910,13 +2930,13 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.Identifier => |opt_ctx| {
- if (eatToken(&tok_it, &tree, Token.Id.Identifier)) |ident_token| {
+ if (eatToken(&tok_it, tree, Token.Id.Identifier)) |ident_token| {
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.Identifier, ident_token);
continue;
}
if (opt_ctx != OptionalCtx.Optional) {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
((try tree.errors.addOne())).* = Error{
@@ -2930,8 +2950,8 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ErrorTag => |node_ptr| {
- const comments = try eatDocComments(arena, &tok_it, &tree);
- const ident_token = nextToken(&tok_it, &tree);
+ const comments = try eatDocComments(arena, &tok_it, tree);
+ const ident_token = nextToken(&tok_it, tree);
const ident_token_index = ident_token.index;
const ident_token_ptr = ident_token.ptr;
if (ident_token_ptr.id != Token.Id.Identifier) {
@@ -2955,7 +2975,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
},
State.ExpectToken => |token_id| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (token_ptr.id != token_id) {
@@ -2970,7 +2990,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.ExpectTokenSave => |expect_token_save| {
- const token = nextToken(&tok_it, &tree);
+ const token = nextToken(&tok_it, tree);
const token_index = token.index;
const token_ptr = token.ptr;
if (token_ptr.id != expect_token_save.id) {
@@ -2986,7 +3006,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.IfToken => |token_id| {
- if (eatToken(&tok_it, &tree, token_id)) |_| {
+ if (eatToken(&tok_it, tree, token_id)) |_| {
continue;
}
@@ -2994,7 +3014,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.IfTokenSave => |if_token_save| {
- if (eatToken(&tok_it, &tree, if_token_save.id)) |token_index| {
+ if (eatToken(&tok_it, tree, if_token_save.id)) |token_index| {
(if_token_save.ptr).* = token_index;
continue;
}
@@ -3003,7 +3023,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue;
},
State.OptionalTokenSave => |optional_token_save| {
- if (eatToken(&tok_it, &tree, optional_token_save.id)) |token_index| {
+ if (eatToken(&tok_it, tree, optional_token_save.id)) |token_index| {
(optional_token_save.ptr).* = token_index;
continue;
}
diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig
index 955e65eeed..6977e4e6b1 100644
--- a/std/zig/parser_test.zig
+++ b/std/zig/parser_test.zig
@@ -1,10 +1,3 @@
-test "temporary trivial example" {
- try testCanonical(
- \\const x = true;
- \\
- );
-}
-
test "zig fmt: allowzero pointer" {
try testCanonical(
\\const T = [*]allowzero const u8;
@@ -57,14 +50,6 @@ test "zig fmt: linksection" {
);
}
-test "zig fmt: shebang line" {
- try testCanonical(
- \\#!/usr/bin/env zig
- \\pub fn main() void {}
- \\
- );
-}
-
test "zig fmt: correctly move doc comments on struct fields" {
try testTransform(
\\pub const section_64 = extern struct {
@@ -2125,6 +2110,21 @@ test "zig fmt: error return" {
);
}
+test "zig fmt: comptime block in container" {
+ try testCanonical(
+ \\pub fn container() type {
+ \\ return struct {
+ \\ comptime {
+ \\ if (false) {
+ \\ unreachable;
+ \\ }
+ \\ }
+ \\ };
+ \\}
+ \\
+ );
+}
+
const std = @import("std");
const mem = std.mem;
const warn = std.debug.warn;
@@ -2137,7 +2137,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
var stderr_file = try io.getStdErr();
var stderr = &stderr_file.outStream().stream;
- var tree = try std.zig.parse2(allocator, source);
+ const tree = try std.zig.parse(allocator, source);
defer tree.deinit();
var error_it = tree.errors.iterator(0);
@@ -2170,7 +2170,7 @@ fn testParse(source: []const u8, allocator: *mem.Allocator, anything_changed: *b
errdefer buffer.deinit();
var buffer_out_stream = io.BufferOutStream.init(&buffer);
- anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, &tree);
+ anything_changed.* = try std.zig.render(allocator, &buffer_out_stream.stream, tree);
return buffer.toOwnedSlice();
}
@@ -2215,7 +2215,7 @@ fn testTransform(source: []const u8, expected_source: []const u8) !void {
needed_alloc_count,
failing_allocator.allocated_bytes,
failing_allocator.freed_bytes,
- failing_allocator.index,
+ failing_allocator.allocations,
failing_allocator.deallocations,
);
return error.MemoryLeakDetected;
diff --git a/std/zig/render.zig b/std/zig/render.zig
index f1fe23c2a8..cabc4ea9ef 100644
--- a/std/zig/render.zig
+++ b/std/zig/render.zig
@@ -73,11 +73,6 @@ fn renderRoot(
) (@typeOf(stream).Child.Error || Error)!void {
var tok_it = tree.tokens.iterator(0);
- // render the shebang line
- if (tree.root_node.shebang) |shebang| {
- try stream.write(tree.tokenSlice(shebang));
- }
-
// render all the line comments at the beginning of the file
while (tok_it.next()) |token| {
if (token.id != Token.Id.LineComment) break;
@@ -753,7 +748,7 @@ fn renderExpression(
counting_stream.bytes_written = 0;
var dummy_col: usize = 0;
try renderExpression(allocator, &counting_stream.stream, tree, 0, &dummy_col, expr.*, Space.None);
- const width = counting_stream.bytes_written;
+ const width = @intCast(usize, counting_stream.bytes_written);
const col = i % row_size;
column_widths[col] = std.math.max(column_widths[col], width);
expr_widths[i] = width;