From 67b4de33d2729fdb21337e5a0e05f6273bce23ba Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Mar 2019 10:38:27 -0500 Subject: compile error for import outside package path closes #2024 there's a new cli option `--main-pkg-path` which you can use to choose a different root package directory besides the one inferred from the root source file and a corresponding build.zig API: foo.setMainPkgPath(path) --- src/link.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/link.cpp') diff --git a/src/link.cpp b/src/link.cpp index 7eb8cb111f..2e30dc6230 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -41,7 +41,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) child_out_type = OutTypeObj; } - CodeGen *child_gen = codegen_create(full_path, parent_gen->zig_target, child_out_type, + CodeGen *child_gen = codegen_create(nullptr, full_path, parent_gen->zig_target, child_out_type, parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, parent_gen->libc); -- cgit v1.2.3 From e40245570422c137f7239f411128973cc217389e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 2 Mar 2019 16:46:04 -0500 Subject: rename std lib files to new convention --- .builds/freebsd.yml | 16 +- CMakeLists.txt | 40 +- build.zig | 4 +- src-self-hosted/compilation.zig | 2 +- src-self-hosted/introspect.zig | 2 +- src/codegen.cpp | 2 +- src/compiler.cpp | 6 +- src/link.cpp | 4 +- std/array_list.zig | 4 +- std/atomic.zig | 9 + std/atomic/index.zig | 9 - std/atomic/queue.zig | 2 +- std/atomic/stack.zig | 2 +- std/base64.zig | 2 +- std/buf_map.zig | 2 +- std/buf_set.zig | 2 +- std/buffer.zig | 2 +- std/build.zig | 2 +- std/build/fmt.zig | 2 +- std/c.zig | 68 + std/c/darwin.zig | 2 +- std/c/index.zig | 68 - std/coff.zig | 2 +- std/crypto.zig | 45 + std/crypto/blake2.zig | 4 +- std/crypto/chacha20.zig | 2 +- std/crypto/hmac.zig | 2 +- std/crypto/index.zig | 45 - std/crypto/md5.zig | 6 +- std/crypto/poly1305.zig | 2 +- std/crypto/sha1.zig | 4 +- std/crypto/sha2.zig | 4 +- std/crypto/sha3.zig | 4 +- std/crypto/test.zig | 2 +- std/crypto/throughput_test.zig | 2 +- std/crypto/x25519.zig | 2 +- std/cstr.zig | 2 +- std/debug.zig | 2205 +++++++++++++ std/debug/failing_allocator.zig | 2 +- std/debug/index.zig | 2205 ------------- std/dynamic_library.zig | 2 +- std/elf.zig | 2 +- std/event/channel.zig | 2 +- std/event/fs.zig | 13 +- std/event/future.zig | 2 +- std/event/group.zig | 2 +- std/event/io.zig | 2 +- std/event/lock.zig | 2 +- std/event/locked.zig | 2 +- std/event/loop.zig | 2 +- std/event/net.zig | 2 +- std/event/rwlock.zig | 2 +- std/event/rwlocked.zig | 2 +- std/fmt.zig | 1441 ++++++++ std/fmt/errol.zig | 704 ++++ std/fmt/errol/index.zig | 704 ---- std/fmt/index.zig | 1441 -------- std/fmt/parse_float.zig | 2 +- std/hash.zig | 22 + std/hash/adler.zig | 2 +- std/hash/crc.zig | 2 +- std/hash/fnv.zig | 2 +- std/hash/index.zig | 22 - std/hash/siphash.zig | 2 +- std/hash_map.zig | 2 +- std/heap.zig | 2 +- std/index.zig | 95 - std/io.zig | 2 +- std/io/seekable_stream.zig | 2 +- std/io_test.zig | 32 +- std/json.zig | 2 +- std/json_test.zig | 2 +- std/lazy_init.zig | 2 +- std/linked_list.zig | 2 +- std/math.zig | 797 +++++ std/math/acos.zig | 2 +- std/math/acosh.zig | 2 +- std/math/asin.zig | 2 +- std/math/asinh.zig | 2 +- std/math/atan.zig | 2 +- std/math/atan2.zig | 2 +- std/math/atanh.zig | 2 +- std/math/big.zig | 5 + std/math/big/index.zig | 5 - std/math/big/int.zig | 2 +- std/math/cbrt.zig | 2 +- std/math/ceil.zig | 2 +- std/math/complex.zig | 171 + std/math/complex/abs.zig | 2 +- std/math/complex/acos.zig | 2 +- std/math/complex/acosh.zig | 2 +- std/math/complex/arg.zig | 2 +- std/math/complex/asin.zig | 2 +- std/math/complex/asinh.zig | 2 +- std/math/complex/atan.zig | 2 +- std/math/complex/atanh.zig | 2 +- std/math/complex/conj.zig | 2 +- std/math/complex/cos.zig | 2 +- std/math/complex/cosh.zig | 2 +- std/math/complex/exp.zig | 2 +- std/math/complex/index.zig | 171 - std/math/complex/ldexp.zig | 2 +- std/math/complex/log.zig | 2 +- std/math/complex/pow.zig | 2 +- std/math/complex/proj.zig | 2 +- std/math/complex/sin.zig | 2 +- std/math/complex/sinh.zig | 2 +- std/math/complex/sqrt.zig | 2 +- std/math/complex/tan.zig | 2 +- std/math/complex/tanh.zig | 2 +- std/math/copysign.zig | 2 +- std/math/cos.zig | 2 +- std/math/cosh.zig | 2 +- std/math/exp.zig | 2 +- std/math/exp2.zig | 2 +- std/math/expm1.zig | 2 +- std/math/expo2.zig | 2 +- std/math/fabs.zig | 2 +- std/math/floor.zig | 2 +- std/math/fma.zig | 2 +- std/math/frexp.zig | 2 +- std/math/hypot.zig | 2 +- std/math/ilogb.zig | 2 +- std/math/index.zig | 797 ----- std/math/inf.zig | 2 +- std/math/isfinite.zig | 2 +- std/math/isinf.zig | 2 +- std/math/isnan.zig | 2 +- std/math/isnormal.zig | 2 +- std/math/ln.zig | 2 +- std/math/log.zig | 2 +- std/math/log10.zig | 2 +- std/math/log1p.zig | 2 +- std/math/log2.zig | 2 +- std/math/modf.zig | 2 +- std/math/nan.zig | 2 +- std/math/pow.zig | 2 +- std/math/powi.zig | 2 +- std/math/round.zig | 2 +- std/math/scalbn.zig | 2 +- std/math/signbit.zig | 2 +- std/math/sin.zig | 2 +- std/math/sinh.zig | 2 +- std/math/sqrt.zig | 2 +- std/math/tan.zig | 2 +- std/math/tanh.zig | 2 +- std/math/trunc.zig | 2 +- std/mem.zig | 2 +- std/meta.zig | 569 ++++ std/meta/index.zig | 569 ---- std/meta/trait.zig | 4 +- std/mutex.zig | 2 +- std/net.zig | 2 +- std/os.zig | 3432 ++++++++++++++++++++ std/os/child_process.zig | 2 +- std/os/darwin.zig | 2 +- std/os/file.zig | 2 +- std/os/freebsd.zig | 845 +++++ std/os/freebsd/index.zig | 846 ----- std/os/get_app_data_dir.zig | 2 +- std/os/get_user_id.zig | 2 +- std/os/index.zig | 3432 -------------------- std/os/linux.zig | 1541 +++++++++ std/os/linux/arm64.zig | 2 +- std/os/linux/index.zig | 1541 --------- std/os/linux/test.zig | 2 +- std/os/linux/vdso.zig | 2 +- std/os/linux/x86_64.zig | 2 +- std/os/netbsd.zig | 724 +++++ std/os/netbsd/index.zig | 725 ----- std/os/path.zig | 2 +- std/os/test.zig | 2 +- std/os/time.zig | 2 +- std/os/windows.zig | 399 +++ std/os/windows/advapi32.zig | 2 +- std/os/windows/index.zig | 399 --- std/os/windows/kernel32.zig | 8 +- std/os/windows/ntdll.zig | 2 +- std/os/windows/ole32.zig | 2 +- std/os/windows/shell32.zig | 2 +- std/os/windows/util.zig | 2 +- std/os/zen.zig | 2 +- std/pdb.zig | 2 +- std/priority_queue.zig | 2 +- std/rand.zig | 1005 ++++++ std/rand/index.zig | 1005 ------ std/rand/ziggurat.zig | 2 +- std/rb.zig | 2 +- std/segmented_list.zig | 2 +- std/sort.zig | 2 +- std/special/compiler_rt.zig | 1050 ++++++ std/special/compiler_rt/addXf3.zig | 2 +- std/special/compiler_rt/divti3.zig | 2 +- std/special/compiler_rt/index.zig | 1050 ------ std/special/compiler_rt/muloti4.zig | 2 +- std/special/compiler_rt/multi3.zig | 2 +- std/special/compiler_rt/udivmodti4.zig | 2 +- std/special/compiler_rt/umodti3.zig | 2 +- std/spinlock.zig | 2 +- std/statically_initialized_mutex.zig | 13 +- std/std.zig | 95 + std/testing.zig | 2 +- std/unicode.zig | 2 +- std/zig.zig | 16 + std/zig/ast.zig | 2 +- std/zig/bench.zig | 2 +- std/zig/index.zig | 16 - std/zig/parse.zig | 2 +- std/zig/parse_string_literal.zig | 2 +- std/zig/render.zig | 2 +- std/zig/tokenizer.zig | 2 +- test/stage1/behavior.zig | 4 +- .../behavior/namespace_depends_on_compile_var.zig | 14 + .../namespace_depends_on_compile_var/index.zig | 14 - test/stage1/behavior/pub_enum.zig | 13 + test/stage1/behavior/pub_enum/index.zig | 13 - 216 files changed, 15402 insertions(+), 15420 deletions(-) create mode 100644 std/atomic.zig delete mode 100644 std/atomic/index.zig create mode 100644 std/c.zig delete mode 100644 std/c/index.zig create mode 100644 std/crypto.zig delete mode 100644 std/crypto/index.zig create mode 100644 std/debug.zig delete mode 100644 std/debug/index.zig create mode 100644 std/fmt.zig create mode 100644 std/fmt/errol.zig delete mode 100644 std/fmt/errol/index.zig delete mode 100644 std/fmt/index.zig create mode 100644 std/hash.zig delete mode 100644 std/hash/index.zig delete mode 100644 std/index.zig create mode 100644 std/math.zig create mode 100644 std/math/big.zig delete mode 100644 std/math/big/index.zig create mode 100644 std/math/complex.zig delete mode 100644 std/math/complex/index.zig delete mode 100644 std/math/index.zig create mode 100644 std/meta.zig delete mode 100644 std/meta/index.zig create mode 100644 std/os.zig create mode 100644 std/os/freebsd.zig delete mode 100644 std/os/freebsd/index.zig delete mode 100644 std/os/index.zig create mode 100644 std/os/linux.zig delete mode 100644 std/os/linux/index.zig create mode 100644 std/os/netbsd.zig delete mode 100644 std/os/netbsd/index.zig create mode 100644 std/os/windows.zig delete mode 100644 std/os/windows/index.zig create mode 100644 std/rand.zig delete mode 100644 std/rand/index.zig create mode 100644 std/special/compiler_rt.zig delete mode 100644 std/special/compiler_rt/index.zig create mode 100644 std/std.zig create mode 100644 std/zig.zig delete mode 100644 std/zig/index.zig create mode 100644 test/stage1/behavior/namespace_depends_on_compile_var.zig delete mode 100644 test/stage1/behavior/namespace_depends_on_compile_var/index.zig create mode 100644 test/stage1/behavior/pub_enum.zig delete mode 100644 test/stage1/behavior/pub_enum/index.zig (limited to 'src/link.cpp') diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 5e94a83a3a..79dfab50c8 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -13,28 +13,28 @@ tasks: - test: | cd zig/build bin/zig test ../test/stage1/behavior.zig - bin/zig test ../std/special/compiler_rt/index.zig + bin/zig test ../std/special/compiler_rt.zig bin/zig test ../test/stage1/behavior.zig --library c - bin/zig test ../std/special/compiler_rt/index.zig --library c + bin/zig test ../std/special/compiler_rt.zig --library c bin/zig test ../test/stage1/behavior.zig --release-fast - bin/zig test ../std/special/compiler_rt/index.zig --release-fast + bin/zig test ../std/special/compiler_rt.zig --release-fast bin/zig test ../test/stage1/behavior.zig --release-fast --library c - bin/zig test ../std/special/compiler_rt/index.zig --release-fast --library c + bin/zig test ../std/special/compiler_rt.zig --release-fast --library c bin/zig test ../test/stage1/behavior.zig --release-small --library c - bin/zig test ../std/special/compiler_rt/index.zig --release-small --library c + bin/zig test ../std/special/compiler_rt.zig --release-small --library c bin/zig test ../test/stage1/behavior.zig --release-small - bin/zig test ../std/special/compiler_rt/index.zig --release-small + bin/zig test ../std/special/compiler_rt.zig --release-small bin/zig test ../test/stage1/behavior.zig --release-safe - bin/zig test ../std/special/compiler_rt/index.zig --release-safe + bin/zig test ../std/special/compiler_rt.zig --release-safe bin/zig test ../test/stage1/behavior.zig --release-safe --library c - bin/zig test ../std/special/compiler_rt/index.zig --release-safe --library c + bin/zig test ../std/special/compiler_rt.zig --release-safe --library c # TODO enable all tests #bin/zig build --build-file ../build.zig test # TODO integrate with the download page updater and make a diff --git a/CMakeLists.txt b/CMakeLists.txt index f2d9f392ef..8c8defa5ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -439,7 +439,7 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" - "atomic/index.zig" + "atomic.zig" "atomic/int.zig" "atomic/queue.zig" "atomic/stack.zig" @@ -449,17 +449,17 @@ set(ZIG_STD_FILES "buffer.zig" "build.zig" "build/fmt.zig" + "c.zig" "c/darwin.zig" "c/freebsd.zig" - "c/index.zig" "c/linux.zig" "c/netbsd.zig" "c/windows.zig" "coff.zig" + "crypto.zig" "crypto/blake2.zig" "crypto/chacha20.zig" "crypto/hmac.zig" - "crypto/index.zig" "crypto/md5.zig" "crypto/poly1305.zig" "crypto/sha1.zig" @@ -467,8 +467,8 @@ set(ZIG_STD_FILES "crypto/sha3.zig" "crypto/x25519.zig" "cstr.zig" + "debug.zig" "debug/failing_allocator.zig" - "debug/index.zig" "dwarf.zig" "dynamic_library.zig" "elf.zig" @@ -485,25 +485,25 @@ set(ZIG_STD_FILES "event/net.zig" "event/rwlock.zig" "event/rwlocked.zig" + "fmt.zig" + "fmt/errol.zig" "fmt/errol/enum3.zig" - "fmt/errol/index.zig" "fmt/errol/lookup.zig" - "fmt/index.zig" "fmt/parse_float.zig" + "hash.zig" "hash/adler.zig" "hash/crc.zig" "hash/fnv.zig" - "hash/index.zig" "hash/siphash.zig" "hash_map.zig" "heap.zig" - "index.zig" "io.zig" "io/seekable_stream.zig" "json.zig" "lazy_init.zig" "linked_list.zig" "macho.zig" + "math.zig" "math/acos.zig" "math/acosh.zig" "math/asin.zig" @@ -511,10 +511,11 @@ set(ZIG_STD_FILES "math/atan.zig" "math/atan2.zig" "math/atanh.zig" - "math/big/index.zig" + "math/big.zig" "math/big/int.zig" "math/cbrt.zig" "math/ceil.zig" + "math/complex.zig" "math/complex/abs.zig" "math/complex/acos.zig" "math/complex/acosh.zig" @@ -527,7 +528,6 @@ set(ZIG_STD_FILES "math/complex/cos.zig" "math/complex/cosh.zig" "math/complex/exp.zig" - "math/complex/index.zig" "math/complex/ldexp.zig" "math/complex/log.zig" "math/complex/pow.zig" @@ -550,7 +550,6 @@ set(ZIG_STD_FILES "math/frexp.zig" "math/hypot.zig" "math/ilogb.zig" - "math/index.zig" "math/inf.zig" "math/isfinite.zig" "math/isinf.zig" @@ -575,33 +574,33 @@ set(ZIG_STD_FILES "math/tanh.zig" "math/trunc.zig" "mem.zig" - "meta/index.zig" + "meta.zig" "meta/trait.zig" "mutex.zig" "net.zig" + "os.zig" "os/child_process.zig" "os/darwin.zig" "os/darwin/errno.zig" "os/epoch.zig" "os/file.zig" + "os/freebsd.zig" "os/freebsd/errno.zig" - "os/freebsd/index.zig" "os/get_app_data_dir.zig" "os/get_user_id.zig" - "os/index.zig" + "os/linux.zig" "os/linux/arm64.zig" "os/linux/errno.zig" - "os/linux/index.zig" "os/linux/vdso.zig" "os/linux/x86_64.zig" + "os/netbsd.zig" "os/netbsd/errno.zig" - "os/netbsd/index.zig" "os/path.zig" "os/time.zig" "os/uefi.zig" + "os/windows.zig" "os/windows/advapi32.zig" "os/windows/error.zig" - "os/windows/index.zig" "os/windows/kernel32.zig" "os/windows/ntdll.zig" "os/windows/ole32.zig" @@ -610,7 +609,7 @@ set(ZIG_STD_FILES "os/zen.zig" "pdb.zig" "priority_queue.zig" - "rand/index.zig" + "rand.zig" "rand/ziggurat.zig" "segmented_list.zig" "sort.zig" @@ -619,6 +618,7 @@ set(ZIG_STD_FILES "special/bootstrap_windows_tls.zig" "special/build_runner.zig" "special/builtin.zig" + "special/compiler_rt.zig" "special/compiler_rt/addXf3.zig" "special/compiler_rt/aulldiv.zig" "special/compiler_rt/aullrem.zig" @@ -653,7 +653,6 @@ set(ZIG_STD_FILES "special/compiler_rt/floatuntidf.zig" "special/compiler_rt/floatuntisf.zig" "special/compiler_rt/floatuntitf.zig" - "special/compiler_rt/index.zig" "special/compiler_rt/muloti4.zig" "special/compiler_rt/multi3.zig" "special/compiler_rt/truncXfYf2.zig" @@ -671,10 +670,11 @@ set(ZIG_STD_FILES "special/test_runner.zig" "spinlock.zig" "statically_initialized_mutex.zig" + "std.zig" "testing.zig" "unicode.zig" + "zig.zig" "zig/ast.zig" - "zig/index.zig" "zig/parse.zig" "zig/parse_string_literal.zig" "zig/render.zig" diff --git a/build.zig b/build.zig index 756ec53708..6acf08a0e5 100644 --- a/build.zig +++ b/build.zig @@ -115,9 +115,9 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); - test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "std/std.zig", "std", "Run the standard library tests", modes)); - test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); + test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes)); test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes)); diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index a59588353c..478edce020 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -533,7 +533,7 @@ pub const Compilation = struct { const basename = std.os.path.basename(root_src); comp.root_package = try Package.create(comp.arena(), dirname, basename); - comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig"); + comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "std.zig"); try comp.root_package.add("std", comp.std_package); } else { comp.root_package = try Package.create(comp.arena(), ".", ""); diff --git a/src-self-hosted/introspect.zig b/src-self-hosted/introspect.zig index 0a7f63b4f1..8f859a82ce 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -11,7 +11,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![ const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" }); errdefer allocator.free(test_zig_dir); - const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "index.zig" }); + const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "std.zig" }); defer allocator.free(test_index_file); var file = try os.File.openRead(test_index_file); diff --git a/src/codegen.cpp b/src/codegen.cpp index 071dd1fde4..89e370329f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -162,7 +162,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget } g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); - g->std_package = new_package(buf_ptr(g->zig_std_dir), "index.zig", "std"); + g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); } else { g->root_package = new_package(".", "", ""); diff --git a/src/compiler.cpp b/src/compiler.cpp index 0d4dff5d50..8c2f9cdf60 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -78,8 +78,8 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { Buf std_buf = BUF_INIT; buf_init_from_str(&std_buf, "std"); - Buf index_zig_buf = BUF_INIT; - buf_init_from_str(&index_zig_buf, "index.zig"); + Buf std_zig_buf = BUF_INIT; + buf_init_from_str(&std_zig_buf, "std.zig"); Buf test_lib_dir = BUF_INIT; Buf test_zig_dir = BUF_INIT; @@ -89,7 +89,7 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { os_path_join(test_path, &lib_buf, &test_lib_dir); os_path_join(&test_lib_dir, &zig_buf, &test_zig_dir); os_path_join(&test_zig_dir, &std_buf, &test_std_dir); - os_path_join(&test_std_dir, &index_zig_buf, &test_index_file); + os_path_join(&test_std_dir, &std_zig_buf, &test_index_file); int err; bool exists; diff --git a/src/link.cpp b/src/link.cpp index 2e30dc6230..bd1879da1a 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -87,10 +87,8 @@ static Buf *build_a(CodeGen *parent_gen, const char *aname) { } static Buf *build_compiler_rt(CodeGen *parent_gen) { - Buf *dir_path = buf_alloc(); - os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt"), dir_path); Buf *full_path = buf_alloc(); - os_path_join(dir_path, buf_create_from_str("index.zig"), full_path); + os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path); return build_a_raw(parent_gen, "compiler_rt", full_path); } diff --git a/std/array_list.zig b/std/array_list.zig index e2535d6393..2c9da50972 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const assert = debug.assert; const testing = std.testing; @@ -406,6 +406,6 @@ const Item = struct { test "std.ArrayList: ArrayList(T) of struct T" { var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(debug.global_allocator) }; - try root.sub_items.append( Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) } ); + try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) }); testing.expect(root.sub_items.items[0].integer == 42); } diff --git a/std/atomic.zig b/std/atomic.zig new file mode 100644 index 0000000000..6f7b4fe75d --- /dev/null +++ b/std/atomic.zig @@ -0,0 +1,9 @@ +pub const Stack = @import("atomic/stack.zig").Stack; +pub const Queue = @import("atomic/queue.zig").Queue; +pub const Int = @import("atomic/int.zig").Int; + +test "std.atomic" { + _ = @import("atomic/stack.zig"); + _ = @import("atomic/queue.zig"); + _ = @import("atomic/int.zig"); +} diff --git a/std/atomic/index.zig b/std/atomic/index.zig deleted file mode 100644 index a94cff1973..0000000000 --- a/std/atomic/index.zig +++ /dev/null @@ -1,9 +0,0 @@ -pub const Stack = @import("stack.zig").Stack; -pub const Queue = @import("queue.zig").Queue; -pub const Int = @import("int.zig").Int; - -test "std.atomic" { - _ = @import("stack.zig"); - _ = @import("queue.zig"); - _ = @import("int.zig"); -} diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig index bdc86c0981..431a96e64b 100644 --- a/std/atomic/queue.zig +++ b/std/atomic/queue.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; const AtomicRmwOp = builtin.AtomicRmwOp; diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig index 4d0d5075e0..8ae6c997aa 100644 --- a/std/atomic/stack.zig +++ b/std/atomic/stack.zig @@ -68,7 +68,7 @@ pub fn Stack(comptime T: type) type { }; } -const std = @import("../index.zig"); +const std = @import("../std.zig"); const Context = struct { allocator: *std.mem.Allocator, stack: *Stack(i32), diff --git a/std/base64.zig b/std/base64.zig index cfe8ea95f8..0e44f9d4fe 100644 --- a/std/base64.zig +++ b/std/base64.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; diff --git a/std/buf_map.zig b/std/buf_map.zig index f8d3d5e04d..be0666d972 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const HashMap = std.HashMap; const mem = std.mem; const Allocator = mem.Allocator; diff --git a/std/buf_set.zig b/std/buf_set.zig index 484b4953b9..807b9db35d 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const HashMap = @import("hash_map.zig").HashMap; const mem = @import("mem.zig"); const Allocator = mem.Allocator; diff --git a/std/buffer.zig b/std/buffer.zig index 371655f1e5..8706dfc320 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const mem = std.mem; const Allocator = mem.Allocator; diff --git a/std/build.zig b/std/build.zig index b7f76a53af..f2e53cce10 100644 --- a/std/build.zig +++ b/std/build.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const io = std.io; const mem = std.mem; diff --git a/std/build/fmt.zig b/std/build/fmt.zig index ec0536dd34..f3b8e08180 100644 --- a/std/build/fmt.zig +++ b/std/build/fmt.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const build = @import("../build.zig"); const Step = build.Step; const Builder = build.Builder; diff --git a/std/c.zig b/std/c.zig new file mode 100644 index 0000000000..c7c8a9c550 --- /dev/null +++ b/std/c.zig @@ -0,0 +1,68 @@ +const builtin = @import("builtin"); +const Os = builtin.Os; + +pub use switch (builtin.os) { + Os.linux => @import("c/linux.zig"), + Os.windows => @import("c/windows.zig"), + Os.macosx, Os.ios => @import("c/darwin.zig"), + Os.freebsd => @import("c/freebsd.zig"), + Os.netbsd => @import("c/netbsd.zig"), + else => empty_import, +}; +const empty_import = @import("empty.zig"); + +// TODO https://github.com/ziglang/zig/issues/265 on this whole file + +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; +pub extern "c" fn close(fd: c_int) c_int; +pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; +pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; +pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; +pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; +pub extern "c" fn raise(sig: c_int) c_int; +pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; +pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize; +pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; +pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; +pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize; +pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; +pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; +pub extern "c" fn unlink(path: [*]const u8) c_int; +pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; +pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; +pub extern "c" fn fork() c_int; +pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn pipe(fds: *[2]c_int) c_int; +pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; +pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; +pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; +pub extern "c" fn chdir(path: [*]const u8) c_int; +pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; +pub extern "c" fn dup(fd: c_int) c_int; +pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; +pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; +pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; +pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; +pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; +pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; +pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; +pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; +pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; +pub extern "c" fn rmdir(path: [*]const u8) c_int; + +pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; +pub extern "c" fn malloc(usize) ?*c_void; +pub extern "c" fn realloc(*c_void, usize) ?*c_void; +pub extern "c" fn free(*c_void) void; +pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; + +pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; +pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; +pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; +pub extern "pthread" fn pthread_self() pthread_t; +pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; + +pub const pthread_t = *@OpaqueType(); diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 940d949985..0e92e8d411 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -163,7 +163,7 @@ pub const Kevent = extern struct { // sys/types.h on macos uses #pragma pack(4) so these checks are // to make sure the struct is laid out the same. These values were // produced from C code using the offsetof macro. -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; comptime { diff --git a/std/c/index.zig b/std/c/index.zig deleted file mode 100644 index 74cdd884ca..0000000000 --- a/std/c/index.zig +++ /dev/null @@ -1,68 +0,0 @@ -const builtin = @import("builtin"); -const Os = builtin.Os; - -pub use switch (builtin.os) { - Os.linux => @import("linux.zig"), - Os.windows => @import("windows.zig"), - Os.macosx, Os.ios => @import("darwin.zig"), - Os.freebsd => @import("freebsd.zig"), - Os.netbsd => @import("netbsd.zig"), - else => empty_import, -}; -const empty_import = @import("../empty.zig"); - -// TODO https://github.com/ziglang/zig/issues/265 on this whole file - -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; -pub extern "c" fn close(fd: c_int) c_int; -pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; -pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; -pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; -pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; -pub extern "c" fn raise(sig: c_int) c_int; -pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; -pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize; -pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; -pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; -pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize; -pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; -pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; -pub extern "c" fn unlink(path: [*]const u8) c_int; -pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; -pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; -pub extern "c" fn fork() c_int; -pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; -pub extern "c" fn pipe(fds: *[2]c_int) c_int; -pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; -pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; -pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; -pub extern "c" fn chdir(path: [*]const u8) c_int; -pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; -pub extern "c" fn dup(fd: c_int) c_int; -pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; -pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; -pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; -pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; -pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; -pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; -pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; -pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; -pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; -pub extern "c" fn rmdir(path: [*]const u8) c_int; - -pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; -pub extern "c" fn malloc(usize) ?*c_void; -pub extern "c" fn realloc(*c_void, usize) ?*c_void; -pub extern "c" fn free(*c_void) void; -pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; - -pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; -pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; -pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; -pub extern "pthread" fn pthread_self() pthread_t; -pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; - -pub const pthread_t = *@OpaqueType(); diff --git a/std/coff.zig b/std/coff.zig index 53bd2a4b7a..c31d2d8233 100644 --- a/std/coff.zig +++ b/std/coff.zig @@ -1,5 +1,5 @@ const builtin = @import("builtin"); -const std = @import("index.zig"); +const std = @import("std.zig"); const io = std.io; const mem = std.mem; const os = std.os; diff --git a/std/crypto.zig b/std/crypto.zig new file mode 100644 index 0000000000..2429441fc7 --- /dev/null +++ b/std/crypto.zig @@ -0,0 +1,45 @@ +pub const Md5 = @import("crypto/md5.zig").Md5; +pub const Sha1 = @import("crypto/sha1.zig").Sha1; + +const sha2 = @import("crypto/sha2.zig"); +pub const Sha224 = sha2.Sha224; +pub const Sha256 = sha2.Sha256; +pub const Sha384 = sha2.Sha384; +pub const Sha512 = sha2.Sha512; + +const sha3 = @import("crypto/sha3.zig"); +pub const Sha3_224 = sha3.Sha3_224; +pub const Sha3_256 = sha3.Sha3_256; +pub const Sha3_384 = sha3.Sha3_384; +pub const Sha3_512 = sha3.Sha3_512; + +const blake2 = @import("crypto/blake2.zig"); +pub const Blake2s224 = blake2.Blake2s224; +pub const Blake2s256 = blake2.Blake2s256; +pub const Blake2b384 = blake2.Blake2b384; +pub const Blake2b512 = blake2.Blake2b512; + +const hmac = @import("crypto/hmac.zig"); +pub const HmacMd5 = hmac.HmacMd5; +pub const HmacSha1 = hmac.HmacSha1; +pub const HmacSha256 = hmac.HmacSha256; +pub const HmacBlake2s256 = hmac.HmacBlake2s256; + +const import_chaCha20 = @import("crypto/chacha20.zig"); +pub const chaCha20IETF = import_chaCha20.chaCha20IETF; +pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; + +pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; +pub const X25519 = @import("crypto/x25519.zig").X25519; + +test "crypto" { + _ = @import("crypto/blake2.zig"); + _ = @import("crypto/chacha20.zig"); + _ = @import("crypto/hmac.zig"); + _ = @import("crypto/md5.zig"); + _ = @import("crypto/poly1305.zig"); + _ = @import("crypto/sha1.zig"); + _ = @import("crypto/sha2.zig"); + _ = @import("crypto/sha3.zig"); + _ = @import("crypto/x25519.zig"); +} diff --git a/std/crypto/blake2.zig b/std/crypto/blake2.zig index 6eebaf7a96..e9f617261f 100644 --- a/std/crypto/blake2.zig +++ b/std/crypto/blake2.zig @@ -1,7 +1,7 @@ const mem = @import("../mem.zig"); -const math = @import("../math/index.zig"); +const math = @import("../math.zig"); const endian = @import("../endian.zig"); -const debug = @import("../debug/index.zig"); +const debug = @import("../debug.zig"); const builtin = @import("builtin"); const htest = @import("test.zig"); diff --git a/std/crypto/chacha20.zig b/std/crypto/chacha20.zig index d964f4c03d..78ef700bdf 100644 --- a/std/crypto/chacha20.zig +++ b/std/crypto/chacha20.zig @@ -1,6 +1,6 @@ // Based on public domain Supercop by Daniel J. Bernstein -const std = @import("../index.zig"); +const std = @import("../std.zig"); const mem = std.mem; const endian = std.endian; const assert = std.debug.assert; diff --git a/std/crypto/hmac.zig b/std/crypto/hmac.zig index 51dda1b1ea..69c1b86386 100644 --- a/std/crypto/hmac.zig +++ b/std/crypto/hmac.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const crypto = std.crypto; const debug = std.debug; const mem = std.mem; diff --git a/std/crypto/index.zig b/std/crypto/index.zig deleted file mode 100644 index 3855bd8dd3..0000000000 --- a/std/crypto/index.zig +++ /dev/null @@ -1,45 +0,0 @@ -pub const Md5 = @import("md5.zig").Md5; -pub const Sha1 = @import("sha1.zig").Sha1; - -const sha2 = @import("sha2.zig"); -pub const Sha224 = sha2.Sha224; -pub const Sha256 = sha2.Sha256; -pub const Sha384 = sha2.Sha384; -pub const Sha512 = sha2.Sha512; - -const sha3 = @import("sha3.zig"); -pub const Sha3_224 = sha3.Sha3_224; -pub const Sha3_256 = sha3.Sha3_256; -pub const Sha3_384 = sha3.Sha3_384; -pub const Sha3_512 = sha3.Sha3_512; - -const blake2 = @import("blake2.zig"); -pub const Blake2s224 = blake2.Blake2s224; -pub const Blake2s256 = blake2.Blake2s256; -pub const Blake2b384 = blake2.Blake2b384; -pub const Blake2b512 = blake2.Blake2b512; - -const hmac = @import("hmac.zig"); -pub const HmacMd5 = hmac.HmacMd5; -pub const HmacSha1 = hmac.HmacSha1; -pub const HmacSha256 = hmac.HmacSha256; -pub const HmacBlake2s256 = hmac.HmacBlake2s256; - -const import_chaCha20 = @import("chacha20.zig"); -pub const chaCha20IETF = import_chaCha20.chaCha20IETF; -pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; - -pub const Poly1305 = @import("poly1305.zig").Poly1305; -pub const X25519 = @import("x25519.zig").X25519; - -test "crypto" { - _ = @import("blake2.zig"); - _ = @import("chacha20.zig"); - _ = @import("hmac.zig"); - _ = @import("md5.zig"); - _ = @import("poly1305.zig"); - _ = @import("sha1.zig"); - _ = @import("sha2.zig"); - _ = @import("sha3.zig"); - _ = @import("x25519.zig"); -} diff --git a/std/crypto/md5.zig b/std/crypto/md5.zig index fe6877a58f..d27a3c1c33 100644 --- a/std/crypto/md5.zig +++ b/std/crypto/md5.zig @@ -1,9 +1,9 @@ const mem = @import("../mem.zig"); -const math = @import("../math/index.zig"); +const math = @import("../math.zig"); const endian = @import("../endian.zig"); const builtin = @import("builtin"); -const debug = @import("../debug/index.zig"); -const fmt = @import("../fmt/index.zig"); +const debug = @import("../debug.zig"); +const fmt = @import("../fmt.zig"); const RoundParam = struct { a: usize, diff --git a/std/crypto/poly1305.zig b/std/crypto/poly1305.zig index 64adb17c45..bd0b33e586 100644 --- a/std/crypto/poly1305.zig +++ b/std/crypto/poly1305.zig @@ -2,7 +2,7 @@ // // https://monocypher.org/ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const Endian = builtin.Endian; diff --git a/std/crypto/sha1.zig b/std/crypto/sha1.zig index 42beba6205..ee5c118b13 100644 --- a/std/crypto/sha1.zig +++ b/std/crypto/sha1.zig @@ -1,7 +1,7 @@ const mem = @import("../mem.zig"); -const math = @import("../math/index.zig"); +const math = @import("../math.zig"); const endian = @import("../endian.zig"); -const debug = @import("../debug/index.zig"); +const debug = @import("../debug.zig"); const builtin = @import("builtin"); const RoundParam = struct { diff --git a/std/crypto/sha2.zig b/std/crypto/sha2.zig index 615dd4a221..37b0b65dc6 100644 --- a/std/crypto/sha2.zig +++ b/std/crypto/sha2.zig @@ -1,7 +1,7 @@ const mem = @import("../mem.zig"); -const math = @import("../math/index.zig"); +const math = @import("../math.zig"); const endian = @import("../endian.zig"); -const debug = @import("../debug/index.zig"); +const debug = @import("../debug.zig"); const builtin = @import("builtin"); const htest = @import("test.zig"); diff --git a/std/crypto/sha3.zig b/std/crypto/sha3.zig index 11d8e4be50..beebb3e65d 100644 --- a/std/crypto/sha3.zig +++ b/std/crypto/sha3.zig @@ -1,7 +1,7 @@ const mem = @import("../mem.zig"); -const math = @import("../math/index.zig"); +const math = @import("../math.zig"); const endian = @import("../endian.zig"); -const debug = @import("../debug/index.zig"); +const debug = @import("../debug.zig"); const builtin = @import("builtin"); const htest = @import("test.zig"); diff --git a/std/crypto/test.zig b/std/crypto/test.zig index 258cdf7320..a0ddad6c83 100644 --- a/std/crypto/test.zig +++ b/std/crypto/test.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const testing = std.testing; const mem = std.mem; const fmt = std.fmt; diff --git a/std/crypto/throughput_test.zig b/std/crypto/throughput_test.zig index 10ef1bcd48..73a2a86124 100644 --- a/std/crypto/throughput_test.zig +++ b/std/crypto/throughput_test.zig @@ -2,7 +2,7 @@ const builtin = @import("builtin"); const std = @import("std"); const time = std.os.time; const Timer = time.Timer; -const crypto = @import("index.zig"); +const crypto = @import("../crypto.zig"); const KiB = 1024; const MiB = 1024 * KiB; diff --git a/std/crypto/x25519.zig b/std/crypto/x25519.zig index 9349569f97..5d5d31267d 100644 --- a/std/crypto/x25519.zig +++ b/std/crypto/x25519.zig @@ -2,7 +2,7 @@ // // https://monocypher.org/ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const fmt = std.fmt; diff --git a/std/cstr.zig b/std/cstr.zig index abd2eac48f..49d6373732 100644 --- a/std/cstr.zig +++ b/std/cstr.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const debug = std.debug; const mem = std.mem; diff --git a/std/debug.zig b/std/debug.zig new file mode 100644 index 0000000000..df35a8e773 --- /dev/null +++ b/std/debug.zig @@ -0,0 +1,2205 @@ +const std = @import("std.zig"); +const math = std.math; +const mem = std.mem; +const io = std.io; +const os = std.os; +const elf = std.elf; +const DW = std.dwarf; +const macho = std.macho; +const coff = std.coff; +const pdb = std.pdb; +const windows = os.windows; +const ArrayList = std.ArrayList; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; + +pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAllocator; +pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator; + +pub const runtime_safety = switch (builtin.mode) { + builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, + builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, +}; + +const Module = struct { + mod_info: pdb.ModInfo, + module_name: []u8, + obj_file_name: []u8, + + populated: bool, + symbols: []u8, + subsect_info: []u8, + checksum_offset: ?usize, +}; + +/// Tries to write to stderr, unbuffered, and ignores any error returned. +/// Does not append a newline. +var stderr_file: os.File = undefined; +var stderr_file_out_stream: os.File.OutStream = undefined; + +var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; +var stderr_mutex = std.Mutex.init(); +pub fn warn(comptime fmt: []const u8, args: ...) void { + const held = stderr_mutex.acquire(); + defer held.release(); + const stderr = getStderrStream() catch return; + stderr.print(fmt, args) catch return; +} + +pub fn getStderrStream() !*io.OutStream(os.File.WriteError) { + if (stderr_stream) |st| { + return st; + } else { + stderr_file = try io.getStdErr(); + stderr_file_out_stream = stderr_file.outStream(); + const st = &stderr_file_out_stream.stream; + stderr_stream = st; + return st; + } +} + +/// TODO multithreaded awareness +var self_debug_info: ?DebugInfo = null; + +pub fn getSelfDebugInfo() !*DebugInfo { + if (self_debug_info) |*info| { + return info; + } else { + self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator()); + return &self_debug_info.?; + } +} + +fn wantTtyColor() bool { + var bytes: [128]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); +} + +/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. +/// TODO multithreaded awareness +pub fn dumpCurrentStackTrace(start_addr: ?usize) void { + const stderr = getStderrStream() catch return; + const debug_info = getSelfDebugInfo() catch |err| { + stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { + stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + return; + }; +} + +/// Returns a slice with the same pointer as addresses, with a potentially smaller len. +/// On Windows, when first_address is not null, we ask for at least 32 stack frames, +/// and then try to find the first address. If addresses.len is more than 32, we +/// capture that many stack frames exactly, and then look for the first address, +/// chopping off the irrelevant frames and shifting so that the returned addresses pointer +/// equals the passed in addresses pointer. +pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { + switch (builtin.os) { + builtin.Os.windows => { + const addrs = stack_trace.instruction_addresses; + const u32_addrs_len = @intCast(u32, addrs.len); + const first_addr = first_address orelse { + stack_trace.index = windows.RtlCaptureStackBackTrace( + 0, + u32_addrs_len, + @ptrCast(**c_void, addrs.ptr), + null, + ); + return; + }; + var addr_buf_stack: [32]usize = undefined; + const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; + const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); + const first_index = for (addr_buf[0..n]) |addr, i| { + if (addr == first_addr) { + break i; + } + } else { + stack_trace.index = 0; + return; + }; + const slice = addr_buf[first_index..n]; + // We use a for loop here because slice and addrs may alias. + for (slice) |addr, i| { + addrs[i] = addr; + } + stack_trace.index = slice.len; + }, + else => { + var it = StackIterator.init(first_address); + for (stack_trace.instruction_addresses) |*addr, i| { + addr.* = it.next() orelse { + stack_trace.index = i; + return; + }; + } + stack_trace.index = stack_trace.instruction_addresses.len; + }, + } +} + +/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. +/// TODO multithreaded awareness +pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { + const stderr = getStderrStream() catch return; + const debug_info = getSelfDebugInfo() catch |err| { + stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; + return; + }; + writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { + stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; + return; + }; +} + +/// This function invokes undefined behavior when `ok` is `false`. +/// In Debug and ReleaseSafe modes, calls to this function are always +/// generated, and the `unreachable` statement triggers a panic. +/// In ReleaseFast and ReleaseSmall modes, calls to this function are +/// optimized away, and in fact the optimizer is able to use the assertion +/// in its heuristics. +/// Inside a test block, it is best to use the `std.testing` module rather +/// than this function, because this function may not detect a test failure +/// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert +/// function is the correct function to use. +pub fn assert(ok: bool) void { + if (!ok) unreachable; // assertion failure +} + +pub fn panic(comptime format: []const u8, args: ...) noreturn { + @setCold(true); + const first_trace_addr = @returnAddress(); + panicExtra(null, first_trace_addr, format, args); +} + +/// TODO multithreaded awareness +var panicking: u8 = 0; // TODO make this a bool + +pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { + @setCold(true); + + if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { + // Panicked during a panic. + + // TODO detect if a different thread caused the panic, because in that case + // we would want to return here instead of calling abort, so that the thread + // which first called panic can finish printing a stack trace. + os.abort(); + } + const stderr = getStderrStream() catch os.abort(); + stderr.print(format ++ "\n", args) catch os.abort(); + if (trace) |t| { + dumpStackTrace(t.*); + } + dumpCurrentStackTrace(first_trace_addr); + + os.abort(); +} + +const RED = "\x1b[31;1m"; +const GREEN = "\x1b[32;1m"; +const CYAN = "\x1b[36;1m"; +const WHITE = "\x1b[37;1m"; +const DIM = "\x1b[2m"; +const RESET = "\x1b[0m"; + +pub fn writeStackTrace( + stack_trace: builtin.StackTrace, + out_stream: var, + allocator: *mem.Allocator, + debug_info: *DebugInfo, + tty_color: bool, +) !void { + var frame_index: usize = 0; + var frames_left: usize = stack_trace.index; + + 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); + } +} + +pub const StackIterator = struct { + first_addr: ?usize, + fp: usize, + + pub fn init(first_addr: ?usize) StackIterator { + return StackIterator{ + .first_addr = first_addr, + .fp = @frameAddress(), + }; + } + + fn next(self: *StackIterator) ?usize { + if (self.fp == 0) return null; + self.fp = @intToPtr(*const usize, self.fp).*; + if (self.fp == 0) return null; + + if (self.first_addr) |addr| { + while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { + const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; + if (addr == return_address) { + self.first_addr = null; + return return_address; + } + } + } + + const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; + return return_address; + } +}; + +pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { + switch (builtin.os) { + builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), + else => {}, + } + var it = StackIterator.init(start_addr); + while (it.next()) |return_address| { + try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); + } +} + +pub fn writeCurrentStackTraceWindows( + out_stream: var, + debug_info: *DebugInfo, + tty_color: bool, + start_addr: ?usize, +) !void { + var addr_buf: [1024]usize = undefined; + const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); + const addrs = addr_buf[0..n]; + var start_i: usize = if (start_addr) |saddr| blk: { + for (addrs) |addr, i| { + if (addr == saddr) break :blk i; + } + return; + } else 0; + for (addrs[start_i..]) |addr| { + try printSourceAtAddress(debug_info, out_stream, addr, tty_color); + } +} + +pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { + switch (builtin.os) { + builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), + builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), + else => return error.UnsupportedOperatingSystem, + } +} + +fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { + const allocator = getDebugInfoAllocator(); + const base_address = os.getBaseAddress(); + const relative_address = relocated_address - base_address; + + var coff_section: *coff.Section = undefined; + const mod_index = for (di.sect_contribs) |sect_contrib| { + if (sect_contrib.Section > di.coff.sections.len) continue; + // Remember that SectionContribEntry.Section is 1-based. + coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; + + const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; + const vaddr_end = vaddr_start + sect_contrib.Size; + if (relative_address >= vaddr_start and relative_address < vaddr_end) { + break sect_contrib.ModuleIndex; + } + } else { + // we have no information to add to the address + if (tty_color) { + try out_stream.print("???:?:?: "); + setTtyColor(TtyColor.Dim); + try out_stream.print("0x{x} in ??? (???)", relocated_address); + setTtyColor(TtyColor.Reset); + try out_stream.print("\n\n\n"); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); + } + return; + }; + + const mod = &di.modules[mod_index]; + try populateModule(di, mod); + const obj_basename = os.path.basename(mod.obj_file_name); + + var symbol_i: usize = 0; + const symbol_name = while (symbol_i != mod.symbols.len) { + const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); + if (prefix.RecordLen < 2) + return error.InvalidDebugInfo; + switch (prefix.RecordKind) { + pdb.SymbolKind.S_LPROC32 => { + const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); + const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; + const vaddr_end = vaddr_start + proc_sym.CodeSize; + if (relative_address >= vaddr_start and relative_address < vaddr_end) { + break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); + } + }, + else => {}, + } + symbol_i += prefix.RecordLen + @sizeOf(u16); + if (symbol_i > mod.symbols.len) + return error.InvalidDebugInfo; + } else "???"; + + const subsect_info = mod.subsect_info; + + var sect_offset: usize = 0; + var skip_len: usize = undefined; + const opt_line_info = subsections: { + const checksum_offset = mod.checksum_offset orelse break :subsections null; + while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { + const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); + skip_len = subsect_hdr.Length; + sect_offset += @sizeOf(pdb.DebugSubsectionHeader); + + switch (subsect_hdr.Kind) { + pdb.DebugSubsectionKind.Lines => { + var line_index = sect_offset; + + const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); + if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; + line_index += @sizeOf(pdb.LineFragmentHeader); + const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; + const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; + + // 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) { + const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineBlockFragmentHeader); + const start_line_index = line_index; + + const has_column = line_hdr.Flags.LF_HaveColumns; + + if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { + // All line entries are stored inside their line block by ascending start address. + // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address. + // This is done with a simple linear search. + var line_i: u32 = 0; + while (line_i < block_hdr.NumLines) : (line_i += 1) { + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); + line_index += @sizeOf(pdb.LineNumberEntry); + + const vaddr_start = frag_vaddr_start + line_num_entry.Offset; + if (relative_address <= vaddr_start) { + break; + } + } + + // line_i == 0 would mean that no matching LineNumberEntry was found. + if (line_i > 0) { + const subsect_index = checksum_offset + block_hdr.NameIndex; + const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); + const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; + try di.pdb.string_table.seekTo(strtab_offset); + const source_file_name = try di.pdb.string_table.readNullTermString(allocator); + + const line_entry_idx = line_i - 1; + + const column = if (has_column) blk: { + const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; + const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; + const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); + break :blk col_num_entry.StartColumn; + } else 0; + + const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); + const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); + const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); + + break :subsections LineInfo{ + .allocator = allocator, + .file_name = source_file_name, + .line = flags.Start, + .column = column, + }; + } + } + } + + // Checking that we are not reading garbage after the (possibly) multiple block fragments. + if (line_index != subsection_end_index) { + return error.InvalidDebugInfo; + } + }, + else => {}, + } + + if (sect_offset > subsect_info.len) + return error.InvalidDebugInfo; + } else { + break :subsections null; + } + }; + + if (tty_color) { + setTtyColor(TtyColor.White); + if (opt_line_info) |li| { + try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column); + } else { + try out_stream.print("???:?:?"); + } + setTtyColor(TtyColor.Reset); + try out_stream.print(": "); + setTtyColor(TtyColor.Dim); + try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); + setTtyColor(TtyColor.Reset); + + if (opt_line_info) |line_info| { + try out_stream.print("\n"); + if (printLineFromFileAnyOs(out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } + } + setTtyColor(TtyColor.Green); + try out_stream.write("^"); + setTtyColor(TtyColor.Reset); + try out_stream.write("\n"); + } + } else |err| switch (err) { + error.EndOfFile => {}, + else => return err, + } + } else { + try out_stream.print("\n\n\n"); + } + } else { + if (opt_line_info) |li| { + try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); + } else { + try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); + } + } +} + +const TtyColor = enum { + Red, + Green, + Cyan, + White, + Dim, + Bold, + Reset, +}; + +/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt +fn setTtyColor(tty_color: TtyColor) void { + if (os.supportsAnsiEscapeCodes(stderr_file.handle)) { + switch (tty_color) { + TtyColor.Red => { + stderr_file.write(RED) catch return; + }, + TtyColor.Green => { + stderr_file.write(GREEN) catch return; + }, + TtyColor.Cyan => { + stderr_file.write(CYAN) catch return; + }, + TtyColor.White, TtyColor.Bold => { + stderr_file.write(WHITE) catch return; + }, + TtyColor.Dim => { + stderr_file.write(DIM) catch return; + }, + TtyColor.Reset => { + stderr_file.write(RESET) catch return; + }, + } + } else { + const S = struct { + var attrs: windows.WORD = undefined; + var init_attrs = false; + }; + if (!S.init_attrs) { + S.init_attrs = true; + var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; + // TODO handle error + _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); + S.attrs = info.wAttributes; + } + + // TODO handle errors + switch (tty_color) { + TtyColor.Red => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY); + }, + TtyColor.Green => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY); + }, + TtyColor.Cyan => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); + }, + TtyColor.White, TtyColor.Bold => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); + }, + TtyColor.Dim => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); + }, + TtyColor.Reset => { + _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); + }, + } + } +} + +fn populateModule(di: *DebugInfo, mod: *Module) !void { + if (mod.populated) + return; + const allocator = getDebugInfoAllocator(); + + if (mod.mod_info.C11ByteSize != 0) + return error.InvalidDebugInfo; + + if (mod.mod_info.C13ByteSize == 0) + return error.MissingDebugInfo; + + const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; + + const signature = try modi.stream.readIntLittle(u32); + if (signature != 4) + return error.InvalidDebugInfo; + + mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); + try modi.stream.readNoEof(mod.symbols); + + mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); + try modi.stream.readNoEof(mod.subsect_info); + + var sect_offset: usize = 0; + var skip_len: usize = undefined; + while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { + const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]); + skip_len = subsect_hdr.Length; + sect_offset += @sizeOf(pdb.DebugSubsectionHeader); + + switch (subsect_hdr.Kind) { + pdb.DebugSubsectionKind.FileChecksums => { + mod.checksum_offset = sect_offset; + break; + }, + else => {}, + } + + if (sect_offset > mod.subsect_info.len) + return error.InvalidDebugInfo; + } + + mod.populated = true; +} + +fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { + var min: usize = 0; + var max: usize = symbols.len - 1; // Exclude sentinel. + while (min < max) { + const mid = min + (max - min) / 2; + const curr = &symbols[mid]; + const next = &symbols[mid + 1]; + if (address >= next.address()) { + min = mid + 1; + } else if (address < curr.address()) { + max = mid; + } else { + return curr; + } + } + return null; +} + +fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { + const base_addr = std.os.getBaseAddress(); + const adjusted_addr = 0x100000000 + (address - base_addr); + + const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); + } + return; + }; + + const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx); + const compile_unit_name = if (symbol.ofile) |ofile| blk: { + const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); + break :blk os.path.basename(ofile_path); + } else "???"; + if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { + defer line_info.deinit(); + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFileAnyOs, + ); + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); + } else { + try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name); + } + }, + else => return err, + } +} + +/// This function works in freestanding mode. +/// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void +pub fn printSourceAtAddressDwarf( + debug_info: *DwarfInfo, + out_stream: var, + address: usize, + tty_color: bool, + comptime printLineFromFile: var, +) !void { + const compile_unit = findCompileUnit(debug_info, address) catch { + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); + } + 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| { + defer line_info.deinit(); + const symbol_name = "???"; + try printLineInfo( + out_stream, + line_info, + address, + symbol_name, + compile_unit_name, + tty_color, + printLineFromFile, + ); + } else |err| switch (err) { + error.MissingDebugInfo, error.InvalidDebugInfo => { + if (tty_color) { + try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); + } else { + try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); + } + }, + else => return err, + } +} + +pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { + return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); +} + +fn printLineInfo( + out_stream: var, + line_info: LineInfo, + address: usize, + symbol_name: []const u8, + compile_unit_name: []const u8, + tty_color: bool, + comptime printLineFromFile: var, +) !void { + if (tty_color) { + try out_stream.print( + WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", + line_info.file_name, + line_info.line, + line_info.column, + address, + symbol_name, + compile_unit_name, + ); + if (printLineFromFile(out_stream, line_info)) { + if (line_info.column == 0) { + try out_stream.write("\n"); + } else { + { + var col_i: usize = 1; + while (col_i < line_info.column) : (col_i += 1) { + try out_stream.writeByte(' '); + } + } + try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); + } + } else |err| switch (err) { + error.EndOfFile => {}, + else => return err, + } + } else { + try out_stream.print( + "{}:{}:{}: 0x{x} in {} ({})\n", + line_info.file_name, + line_info.line, + line_info.column, + address, + symbol_name, + compile_unit_name, + ); + } +} + +// TODO use this +pub const OpenSelfDebugInfoError = error{ + MissingDebugInfo, + OutOfMemory, + UnsupportedOperatingSystem, +}; + +pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { + switch (builtin.os) { + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator), + builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), + builtin.Os.windows => return openSelfDebugInfoWindows(allocator), + else => return error.UnsupportedOperatingSystem, + } +} + +fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { + const self_file = try os.openSelfExe(); + defer self_file.close(); + + const coff_obj = try allocator.create(coff.Coff); + coff_obj.* = coff.Coff{ + .in_file = self_file, + .allocator = allocator, + .coff_header = undefined, + .pe_header = undefined, + .sections = undefined, + .guid = undefined, + .age = undefined, + }; + + var di = DebugInfo{ + .coff = coff_obj, + .pdb = undefined, + .sect_contribs = undefined, + .modules = undefined, + }; + + try di.coff.loadHeader(); + + var path_buf: [windows.MAX_PATH]u8 = undefined; + const len = try di.coff.getPdbPath(path_buf[0..]); + const raw_path = path_buf[0..len]; + + const path = try os.path.resolve(allocator, [][]const u8{raw_path}); + + try di.pdb.openFile(di.coff, path); + + var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; + const version = try pdb_stream.stream.readIntLittle(u32); + const signature = try pdb_stream.stream.readIntLittle(u32); + const age = try pdb_stream.stream.readIntLittle(u32); + var guid: [16]u8 = undefined; + try pdb_stream.stream.readNoEof(guid[0..]); + if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) + return error.InvalidDebugInfo; + // We validated the executable and pdb match. + + const string_table_index = str_tab_index: { + const name_bytes_len = try pdb_stream.stream.readIntLittle(u32); + const name_bytes = try allocator.alloc(u8, name_bytes_len); + try pdb_stream.stream.readNoEof(name_bytes); + + const HashTableHeader = packed struct { + Size: u32, + Capacity: u32, + + fn maxLoad(cap: u32) u32 { + return cap * 2 / 3 + 1; + } + }; + const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader); + if (hash_tbl_hdr.Capacity == 0) + return error.InvalidDebugInfo; + + if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) + return error.InvalidDebugInfo; + + const present = try readSparseBitVector(&pdb_stream.stream, allocator); + if (present.len != hash_tbl_hdr.Size) + return error.InvalidDebugInfo; + const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); + + const Bucket = struct { + first: u32, + second: u32, + }; + const bucket_list = try allocator.alloc(Bucket, present.len); + for (present) |_| { + const name_offset = try pdb_stream.stream.readIntLittle(u32); + const name_index = try pdb_stream.stream.readIntLittle(u32); + const name = mem.toSlice(u8, name_bytes.ptr + name_offset); + if (mem.eql(u8, name, "/names")) { + break :str_tab_index name_index; + } + } + return error.MissingDebugInfo; + }; + + di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; + di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; + + const dbi = di.pdb.dbi; + + // Dbi Header + const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); + const mod_info_size = dbi_stream_header.ModInfoSize; + const section_contrib_size = dbi_stream_header.SectionContributionSize; + + var modules = ArrayList(Module).init(allocator); + + // Module Info Substream + var mod_info_offset: usize = 0; + while (mod_info_offset != mod_info_size) { + const mod_info = try dbi.stream.readStruct(pdb.ModInfo); + var this_record_len: usize = @sizeOf(pdb.ModInfo); + + const module_name = try dbi.readNullTermString(allocator); + this_record_len += module_name.len + 1; + + const obj_file_name = try dbi.readNullTermString(allocator); + this_record_len += obj_file_name.len + 1; + + const march_forward_bytes = this_record_len % 4; + if (march_forward_bytes != 0) { + try dbi.seekForward(march_forward_bytes); + this_record_len += march_forward_bytes; + } + + try modules.append(Module{ + .mod_info = mod_info, + .module_name = module_name, + .obj_file_name = obj_file_name, + + .populated = false, + .symbols = undefined, + .subsect_info = undefined, + .checksum_offset = null, + }); + + mod_info_offset += this_record_len; + if (mod_info_offset > mod_info_size) + return error.InvalidDebugInfo; + } + + di.modules = modules.toOwnedSlice(); + + // Section Contribution Substream + var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); + var sect_cont_offset: usize = 0; + if (section_contrib_size != 0) { + const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32)); + if (ver != pdb.SectionContrSubstreamVersion.Ver60) + return error.InvalidDebugInfo; + sect_cont_offset += @sizeOf(u32); + } + while (sect_cont_offset != section_contrib_size) { + const entry = try sect_contribs.addOne(); + entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry); + sect_cont_offset += @sizeOf(pdb.SectionContribEntry); + + if (sect_cont_offset > section_contrib_size) + return error.InvalidDebugInfo; + } + + di.sect_contribs = sect_contribs.toOwnedSlice(); + + return di; +} + +fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { + const num_words = try stream.readIntLittle(u32); + var word_i: usize = 0; + var list = ArrayList(usize).init(allocator); + while (word_i != num_words) : (word_i += 1) { + const word = try stream.readIntLittle(u32); + var bit_i: u5 = 0; + while (true) : (bit_i += 1) { + if (word & (u32(1) << bit_i) != 0) { + try list.append(word_i * 32 + bit_i); + } + if (bit_i == maxInt(u5)) break; + } + } + return list.toOwnedSlice(); +} + +fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { + const elf_header = (try elf_file.findSection(name)) orelse return null; + return DwarfInfo.Section{ + .offset = elf_header.offset, + .size = elf_header.size, + }; +} + +/// Initialize DWARF info. The caller has the responsibility to initialize most +/// the DwarfInfo fields before calling. These fields can be left undefined: +/// * abbrev_table_list +/// * compile_unit_list +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); + try scanAllCompileUnits(di); +} + +pub fn openElfDebugInfo( + allocator: *mem.Allocator, + elf_seekable_stream: *DwarfSeekableStream, + elf_in_stream: *DwarfInStream, +) !DwarfInfo { + var efile: elf.Elf = undefined; + try efile.openStream(allocator, elf_seekable_stream, elf_in_stream); + errdefer efile.close(); + + var di = DwarfInfo{ + .dwarf_seekable_stream = elf_seekable_stream, + .dwarf_in_stream = elf_in_stream, + .endian = efile.endian, + .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, + .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, + .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, + .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, + .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), + .abbrev_table_list = undefined, + .compile_unit_list = undefined, + }; + try openDwarfDebugInfo(&di, allocator); + return di; +} + +fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { + const S = struct { + var self_exe_file: os.File = undefined; + var self_exe_seekable_stream: os.File.SeekableStream = undefined; + var self_exe_in_stream: os.File.InStream = undefined; + }; + S.self_exe_file = try os.openSelfExe(); + errdefer S.self_exe_file.close(); + + S.self_exe_seekable_stream = S.self_exe_file.seekableStream(); + S.self_exe_in_stream = S.self_exe_file.inStream(); + + return openElfDebugInfo( + allocator, + // TODO https://github.com/ziglang/zig/issues/764 + @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream), + // TODO https://github.com/ziglang/zig/issues/764 + @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream), + ); +} + +fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { + const hdr = &std.c._mh_execute_header; + assert(hdr.magic == std.macho.MH_MAGIC_64); + + const hdr_base = @ptrCast([*]u8, hdr); + var ptr = hdr_base + @sizeOf(macho.mach_header_64); + var ncmd: u32 = hdr.ncmds; + const symtab = while (ncmd != 0) : (ncmd -= 1) { + const lc = @ptrCast(*std.macho.load_command, ptr); + switch (lc.cmd) { + std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), + else => {}, + } + ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 + } else { + return error.MissingDebugInfo; + }; + const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; + const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; + + const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); + + var ofile: ?*macho.nlist_64 = null; + var reloc: u64 = 0; + var symbol_index: usize = 0; + var last_len: u64 = 0; + for (syms) |*sym| { + if (sym.n_type & std.macho.N_STAB != 0) { + switch (sym.n_type) { + std.macho.N_OSO => { + ofile = sym; + reloc = 0; + }, + std.macho.N_FUN => { + if (sym.n_sect == 0) { + last_len = sym.n_value; + } else { + symbols_buf[symbol_index] = MachoSymbol{ + .nlist = sym, + .ofile = ofile, + .reloc = reloc, + }; + symbol_index += 1; + } + }, + std.macho.N_BNSYM => { + if (reloc == 0) { + reloc = sym.n_value; + } + }, + else => continue, + } + } + } + const sentinel = try allocator.create(macho.nlist_64); + sentinel.* = macho.nlist_64{ + .n_strx = 0, + .n_type = 36, + .n_sect = 0, + .n_desc = 0, + .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, + }; + + const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index); + + // Even though lld emits symbols in ascending order, this debug code + // should work for programs linked in any valid way. + // This sort is so that we can binary search later. + std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); + + return DebugInfo{ + .ofiles = DebugInfo.OFileTable.init(allocator), + .symbols = symbols, + .strings = strings, + }; +} + +fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { + var f = try os.File.openRead(line_info.file_name); + defer f.close(); + // TODO fstat and make sure that the file has the correct size + + var buf: [os.page_size]u8 = undefined; + var line: usize = 1; + var column: usize = 1; + var abs_index: usize = 0; + while (true) { + const amt_read = try f.read(buf[0..]); + const slice = buf[0..amt_read]; + + for (slice) |byte| { + if (line == line_info.line) { + try out_stream.writeByte(byte); + if (byte == '\n') { + return; + } + } + if (byte == '\n') { + line += 1; + column = 1; + } else { + column += 1; + } + } + + if (amt_read < buf.len) return error.EndOfFile; + } +} + +const MachoSymbol = struct { + nlist: *macho.nlist_64, + ofile: ?*macho.nlist_64, + reloc: u64, + + /// Returns the address from the macho file + fn address(self: MachoSymbol) u64 { + return self.nlist.n_value; + } + + fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool { + return lhs.address() < rhs.address(); + } +}; + +const MachOFile = struct { + bytes: []align(@alignOf(macho.mach_header_64)) const u8, + sect_debug_info: ?*const macho.section_64, + sect_debug_line: ?*const macho.section_64, +}; + +pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); +pub const DwarfInStream = io.InStream(anyerror); + +pub const DwarfInfo = struct { + dwarf_seekable_stream: *DwarfSeekableStream, + dwarf_in_stream: *DwarfInStream, + endian: builtin.Endian, + debug_info: Section, + debug_abbrev: Section, + debug_str: Section, + debug_line: Section, + debug_ranges: ?Section, + abbrev_table_list: ArrayList(AbbrevTableHeader), + compile_unit_list: ArrayList(CompileUnit), + + pub const Section = struct { + offset: usize, + size: usize, + }; + + pub fn allocator(self: DwarfInfo) *mem.Allocator { + return self.abbrev_table_list.allocator; + } + + pub fn readString(self: *DwarfInfo) ![]u8 { + return readStringRaw(self.allocator(), self.dwarf_in_stream); + } +}; + +pub const DebugInfo = switch (builtin.os) { + builtin.Os.macosx => struct { + symbols: []const MachoSymbol, + strings: []const u8, + ofiles: OFileTable, + + const OFileTable = std.HashMap( + *macho.nlist_64, + MachOFile, + std.hash_map.getHashPtrAddrFn(*macho.nlist_64), + std.hash_map.getTrivialEqlFn(*macho.nlist_64), + ); + + pub fn allocator(self: DebugInfo) *mem.Allocator { + return self.ofiles.allocator; + } + }, + builtin.Os.uefi, builtin.Os.windows => struct { + pdb: pdb.Pdb, + coff: *coff.Coff, + sect_contribs: []pdb.SectionContribEntry, + modules: []Module, + }, + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo, + else => @compileError("Unsupported OS"), +}; + +const PcRange = struct { + start: u64, + end: u64, +}; + +const CompileUnit = struct { + version: u16, + is_64: bool, + die: *Die, + index: usize, + pc_range: ?PcRange, +}; + +const AbbrevTable = ArrayList(AbbrevTableEntry); + +const AbbrevTableHeader = struct { + // offset from .debug_abbrev + offset: u64, + table: AbbrevTable, +}; + +const AbbrevTableEntry = struct { + has_children: bool, + abbrev_code: u64, + tag_id: u64, + attrs: ArrayList(AbbrevAttr), +}; + +const AbbrevAttr = struct { + attr_id: u64, + form_id: u64, +}; + +const FormValue = union(enum) { + Address: u64, + Block: []u8, + Const: Constant, + ExprLoc: []u8, + Flag: bool, + SecOffset: u64, + Ref: []u8, + RefAddr: u64, + RefSig8: u64, + String: []u8, + StrPtr: u64, +}; + +const Constant = struct { + payload: []u8, + 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); + } +}; + +const Die = struct { + tag_id: u64, + has_children: bool, + attrs: ArrayList(Attr), + + const Attr = struct { + id: u64, + value: FormValue, + }; + + fn getAttr(self: *const Die, id: u64) ?*const FormValue { + for (self.attrs.toSliceConst()) |*attr| { + if (attr.id == id) return &attr.value; + } + return null; + } + + fn getAttrAddr(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Address => |value| value, + else => error.InvalidDebugInfo, + }; + } + + fn getAttrSecOffset(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Const => |value| value.asUnsignedLe(), + FormValue.SecOffset => |value| value, + else => error.InvalidDebugInfo, + }; + } + + fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { + const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; + return switch (form_value.*) { + FormValue.Const => |value| value.asUnsignedLe(), + 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.*) { + FormValue.String => |value| value, + FormValue.StrPtr => |offset| getString(di, offset), + else => error.InvalidDebugInfo, + }; + } +}; + +const FileEntry = struct { + file_name: []const u8, + dir_index: usize, + mtime: usize, + len_bytes: usize, +}; + +pub const LineInfo = struct { + line: usize, + column: usize, + file_name: []const u8, + allocator: ?*mem.Allocator, + + fn deinit(self: LineInfo) void { + const allocator = self.allocator orelse return; + allocator.free(self.file_name); + } +}; + +const LineNumberProgram = struct { + address: usize, + file: usize, + line: isize, + column: usize, + is_stmt: bool, + basic_block: bool, + end_sequence: bool, + + target_address: usize, + include_dirs: []const []const u8, + file_entries: *ArrayList(FileEntry), + + prev_address: usize, + prev_file: usize, + prev_line: isize, + prev_column: usize, + prev_is_stmt: bool, + prev_basic_block: bool, + prev_end_sequence: bool, + + pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { + return LineNumberProgram{ + .address = 0, + .file = 1, + .line = 1, + .column = 0, + .is_stmt = is_stmt, + .basic_block = false, + .end_sequence = false, + .include_dirs = include_dirs, + .file_entries = file_entries, + .target_address = target_address, + .prev_address = 0, + .prev_file = undefined, + .prev_line = undefined, + .prev_column = undefined, + .prev_is_stmt = undefined, + .prev_basic_block = undefined, + .prev_end_sequence = undefined, + }; + } + + pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { + if (self.target_address >= self.prev_address and self.target_address < self.address) { + const file_entry = if (self.prev_file == 0) { + return error.MissingDebugInfo; + } else if (self.prev_file - 1 >= self.file_entries.len) { + return error.InvalidDebugInfo; + } else + &self.file_entries.items[self.prev_file - 1]; + + const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { + return error.InvalidDebugInfo; + } else + self.include_dirs[file_entry.dir_index]; + const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); + errdefer self.file_entries.allocator.free(file_name); + return LineInfo{ + .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, + .column = self.prev_column, + .file_name = file_name, + .allocator = self.file_entries.allocator, + }; + } + + self.prev_address = self.address; + self.prev_file = self.file; + self.prev_line = self.line; + self.prev_column = self.column; + self.prev_is_stmt = self.is_stmt; + self.prev_basic_block = self.basic_block; + self.prev_end_sequence = self.end_sequence; + return null; + } +}; + +fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { + var buf = ArrayList(u8).init(allocator); + while (true) { + const byte = try in_stream.readByte(); + if (byte == 0) break; + try buf.append(byte); + } + return buf.toSlice(); +} + +fn getString(di: *DwarfInfo, offset: u64) ![]u8 { + const pos = di.debug_str.offset + offset; + try di.dwarf_seekable_stream.seekTo(pos); + return di.readString(); +} + +fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { + const buf = try allocator.alloc(u8, size); + errdefer allocator.free(buf); + if ((try in_stream.read(buf)) < size) return error.EndOfFile; + return buf; +} + +fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { + const buf = try readAllocBytes(allocator, in_stream, size); + return FormValue{ .Block = buf }; +} + +fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { + const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size); + return parseFormValueBlockLen(allocator, in_stream, block_len); +} + +fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { + return FormValue{ + .Const = Constant{ + .signed = signed, + .payload = try readAllocBytes(allocator, in_stream, size), + }, + }; +} + +fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { + return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32)); +} + +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 parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { + return switch (form_id) { + DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, + DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), + 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); + return parseFormValueBlockLen(allocator, in_stream, block_len); + }, + DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), + DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), + 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); + }, + DW.FORM_exprloc => { + const size = try readULeb128(in_stream); + const buf = try readAllocBytes(allocator, in_stream, size); + return FormValue{ .ExprLoc = buf }; + }, + DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 }, + 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_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, + DW.FORM_ref_sig8 => FormValue{ .RefSig8 = 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); + return parseFormValue(allocator, in_stream, child_form_id, is_64); + }, + else => error.InvalidDebugInfo, + }; +} + +fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { + var result = AbbrevTable.init(di.allocator()); + while (true) { + const abbrev_code = try readULeb128(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), + .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); + if (attr_id == 0 and form_id == 0) break; + try attrs.append(AbbrevAttr{ + .attr_id = attr_id, + .form_id = form_id, + }); + } + } +} + +/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, +/// seeks in the stream and parses it. +fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { + for (di.abbrev_table_list.toSlice()) |*header| { + if (header.offset == abbrev_offset) { + return &header.table; + } + } + try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); + try di.abbrev_table_list.append(AbbrevTableHeader{ + .offset = abbrev_offset, + .table = try parseAbbrevTable(di), + }); + return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; +} + +fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { + for (abbrev_table.toSliceConst()) |*table_entry| { + if (table_entry.abbrev_code == abbrev_code) return table_entry; + } + return null; +} + +fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { + const abbrev_code = try readULeb128(di.dwarf_in_stream); + 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 getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { + const ofile = symbol.ofile orelse return error.MissingDebugInfo; + const gop = try di.ofiles.getOrPut(ofile); + const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { + errdefer _ = di.ofiles.remove(ofile); + const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); + + gop.kv.value = MachOFile{ + .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)), + .sect_debug_info = null, + .sect_debug_line = null, + }; + const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr); + if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; + + const hdr_base = @ptrCast([*]const u8, hdr); + var ptr = hdr_base + @sizeOf(macho.mach_header_64); + var ncmd: u32 = hdr.ncmds; + const segcmd = while (ncmd != 0) : (ncmd -= 1) { + const lc = @ptrCast(*const std.macho.load_command, ptr); + switch (lc.cmd) { + std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), + else => {}, + } + ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 + } else { + return error.MissingDebugInfo; + }; + const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; + for (sections) |*sect| { + if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and + (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) + { + const sect_name = mem.toSliceConst(u8, §.sectname); + if (mem.eql(u8, sect_name, "__debug_line")) { + gop.kv.value.sect_debug_line = sect; + } else if (mem.eql(u8, sect_name, "__debug_info")) { + gop.kv.value.sect_debug_info = sect; + } + } + } + + break :blk &gop.kv.value; + }; + + const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; + var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; + + var is_64: bool = undefined; + const unit_length = try readInitialLengthMem(&ptr, &is_64); + if (unit_length == 0) return error.MissingDebugInfo; + + const version = readIntMem(&ptr, u16, builtin.Endian.Little); + // TODO support 3 and 5 + if (version != 2 and version != 4) return error.InvalidDebugInfo; + + const prologue_length = if (is_64) + readIntMem(&ptr, u64, builtin.Endian.Little) + else + readIntMem(&ptr, u32, builtin.Endian.Little); + const prog_start = ptr + prologue_length; + + const minimum_instruction_length = readByteMem(&ptr); + if (minimum_instruction_length == 0) return error.InvalidDebugInfo; + + if (version >= 4) { + // maximum_operations_per_instruction + ptr += 1; + } + + const default_is_stmt = readByteMem(&ptr) != 0; + const line_base = readByteSignedMem(&ptr); + + const line_range = readByteMem(&ptr); + if (line_range == 0) return error.InvalidDebugInfo; + + const opcode_base = readByteMem(&ptr); + + const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; + ptr += opcode_base - 1; + + var include_directories = ArrayList([]const u8).init(di.allocator()); + try include_directories.append(""); + while (true) { + const dir = readStringMem(&ptr); + 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); + + 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); + try file_entries.append(FileEntry{ + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + } + + ptr = prog_start; + while (true) { + const opcode = readByteMem(&ptr); + + if (opcode == DW.LNS_extended_op) { + const op_size = try readULeb128Mem(&ptr); + if (op_size < 1) return error.InvalidDebugInfo; + var sub_op = readByteMem(&ptr); + 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 = readIntMem(&ptr, usize, builtin.Endian.Little); + prog.address = symbol.reloc + addr; + }, + 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); + try file_entries.append(FileEntry{ + .file_name = file_name, + .dir_index = dir_index, + .mtime = mtime, + .len_bytes = len_bytes, + }); + }, + else => { + ptr += op_size - 1; + }, + } + } 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 readULeb128Mem(&ptr); + prog.address += arg * minimum_instruction_length; + }, + DW.LNS_advance_line => { + const arg = try readILeb128Mem(&ptr); + prog.line += arg; + }, + DW.LNS_set_file => { + const arg = try readULeb128Mem(&ptr); + prog.file = arg; + }, + DW.LNS_set_column => { + const arg = try readULeb128Mem(&ptr); + 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 = readIntMem(&ptr, u16, builtin.Endian.Little); + 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]; + ptr += len_bytes; + }, + } + } + } + + return error.MissingDebugInfo; +} + +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 debug_line_end = di.debug_line.offset + di.debug_line.size; + var this_offset = di.debug_line.offset; + var this_index: usize = 0; + + while (this_offset < debug_line_end) : (this_index += 1) { + try di.dwarf_seekable_stream.seekTo(this_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)); + + 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 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 opcode_base = try di.dwarf_in_stream.readByte(); + + const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); + + { + var i: usize = 0; + while (i < opcode_base - 1) : (i += 1) { + standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); + } + } + + 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); + + 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, + }); + } + + try di.dwarf_seekable_stream.seekTo(prog_start_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); + }, + } + } + } + + this_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); + + 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 compile_unit_die = try di.allocator().create(Die); + compile_unit_die.* = try parseDie(di, abbrev_table, is_64); + + if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; + + const pc_range = x: { + if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { + if (compile_unit_die.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.compile_unit_list.append(CompileUnit{ + .version = version, + .is_64 = is_64, + .pc_range = pc_range, + .die = compile_unit_die, + .index = cu_index, + }); + + this_unit_offset += next_offset; + cu_index += 1; + } +} + +fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { + for (di.compile_unit_list.toSlice()) |*compile_unit| { + if (compile_unit.pc_range) |range| { + if (target_address >= range.start and target_address < range.end) return compile_unit; + } + if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { + var base_address: usize = 0; + if (di.debug_ranges) |debug_ranges| { + try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); + while (true) { + const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); + const end_addr = try di.dwarf_in_stream.readIntLittle(usize); + if (begin_addr == 0 and end_addr == 0) { + break; + } + if (begin_addr == maxInt(usize)) { + base_address = begin_addr; + continue; + } + if (target_address >= begin_addr and target_address < end_addr) { + return compile_unit; + } + } + } + } else |err| { + if (err != error.MissingDebugInfo) return err; + continue; + } + } + return error.MissingDebugInfo; +} + +fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { + // TODO https://github.com/ziglang/zig/issues/863 + const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian); + ptr.* += @sizeOf(T); + return result; +} + +fn readByteMem(ptr: *[*]const u8) u8 { + const result = ptr.*[0]; + ptr.* += 1; + return result; +} + +fn readByteSignedMem(ptr: *[*]const u8) i8 { + return @bitCast(i8, readByteMem(ptr)); +} + +fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { + // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 + const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); + is_64.* = (first_32_bits == 0xffffffff); + if (is_64.*) { + ptr.* += 4; + const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); + ptr.* += 8; + return result; + } else { + if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; + ptr.* += 4; + return u64(first_32_bits); + } +} + +fn readStringMem(ptr: *[*]const u8) []const u8 { + const result = mem.toSliceConst(u8, ptr.*); + ptr.* += result.len + 1; + 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); + if (is_64.*) { + return in_stream.readIntLittle(u64); + } else { + if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; + return u64(first_32_bits); + } +} + +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..]); +var global_allocator_mem: [100 * 1024]u8 = undefined; + +/// TODO multithreaded awareness +var debug_info_allocator: ?*mem.Allocator = null; +var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; +var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; +fn getDebugInfoAllocator() *mem.Allocator { + if (debug_info_allocator) |a| return a; + + debug_info_direct_allocator = std.heap.DirectAllocator.init(); + debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator); + debug_info_allocator = &debug_info_arena_allocator.allocator; + return &debug_info_arena_allocator.allocator; +} diff --git a/std/debug/failing_allocator.zig b/std/debug/failing_allocator.zig index e16dd21db4..82093dffda 100644 --- a/std/debug/failing_allocator.zig +++ b/std/debug/failing_allocator.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const mem = std.mem; /// Allocator that fails after N allocations, useful for making sure out of diff --git a/std/debug/index.zig b/std/debug/index.zig deleted file mode 100644 index 1523eea57c..0000000000 --- a/std/debug/index.zig +++ /dev/null @@ -1,2205 +0,0 @@ -const std = @import("../index.zig"); -const math = std.math; -const mem = std.mem; -const io = std.io; -const os = std.os; -const elf = std.elf; -const DW = std.dwarf; -const macho = std.macho; -const coff = std.coff; -const pdb = std.pdb; -const windows = os.windows; -const ArrayList = std.ArrayList; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; - -pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; -pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator; - -pub const runtime_safety = switch (builtin.mode) { - builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, - builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, -}; - -const Module = struct { - mod_info: pdb.ModInfo, - module_name: []u8, - obj_file_name: []u8, - - populated: bool, - symbols: []u8, - subsect_info: []u8, - checksum_offset: ?usize, -}; - -/// Tries to write to stderr, unbuffered, and ignores any error returned. -/// Does not append a newline. -var stderr_file: os.File = undefined; -var stderr_file_out_stream: os.File.OutStream = undefined; - -var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; -var stderr_mutex = std.Mutex.init(); -pub fn warn(comptime fmt: []const u8, args: ...) void { - const held = stderr_mutex.acquire(); - defer held.release(); - const stderr = getStderrStream() catch return; - stderr.print(fmt, args) catch return; -} - -pub fn getStderrStream() !*io.OutStream(os.File.WriteError) { - if (stderr_stream) |st| { - return st; - } else { - stderr_file = try io.getStdErr(); - stderr_file_out_stream = stderr_file.outStream(); - const st = &stderr_file_out_stream.stream; - stderr_stream = st; - return st; - } -} - -/// TODO multithreaded awareness -var self_debug_info: ?DebugInfo = null; - -pub fn getSelfDebugInfo() !*DebugInfo { - if (self_debug_info) |*info| { - return info; - } else { - self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator()); - return &self_debug_info.?; - } -} - -fn wantTtyColor() bool { - var bytes: [128]u8 = undefined; - const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; - return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); -} - -/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. -/// TODO multithreaded awareness -pub fn dumpCurrentStackTrace(start_addr: ?usize) void { - const stderr = getStderrStream() catch return; - const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; - return; - }; - writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; - return; - }; -} - -/// Returns a slice with the same pointer as addresses, with a potentially smaller len. -/// On Windows, when first_address is not null, we ask for at least 32 stack frames, -/// and then try to find the first address. If addresses.len is more than 32, we -/// capture that many stack frames exactly, and then look for the first address, -/// chopping off the irrelevant frames and shifting so that the returned addresses pointer -/// equals the passed in addresses pointer. -pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { - switch (builtin.os) { - builtin.Os.windows => { - const addrs = stack_trace.instruction_addresses; - const u32_addrs_len = @intCast(u32, addrs.len); - const first_addr = first_address orelse { - stack_trace.index = windows.RtlCaptureStackBackTrace( - 0, - u32_addrs_len, - @ptrCast(**c_void, addrs.ptr), - null, - ); - return; - }; - var addr_buf_stack: [32]usize = undefined; - const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; - const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); - const first_index = for (addr_buf[0..n]) |addr, i| { - if (addr == first_addr) { - break i; - } - } else { - stack_trace.index = 0; - return; - }; - const slice = addr_buf[first_index..n]; - // We use a for loop here because slice and addrs may alias. - for (slice) |addr, i| { - addrs[i] = addr; - } - stack_trace.index = slice.len; - }, - else => { - var it = StackIterator.init(first_address); - for (stack_trace.instruction_addresses) |*addr, i| { - addr.* = it.next() orelse { - stack_trace.index = i; - return; - }; - } - stack_trace.index = stack_trace.instruction_addresses.len; - }, - } -} - -/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. -/// TODO multithreaded awareness -pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { - const stderr = getStderrStream() catch return; - const debug_info = getSelfDebugInfo() catch |err| { - stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; - return; - }; - writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { - stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; - return; - }; -} - -/// This function invokes undefined behavior when `ok` is `false`. -/// In Debug and ReleaseSafe modes, calls to this function are always -/// generated, and the `unreachable` statement triggers a panic. -/// In ReleaseFast and ReleaseSmall modes, calls to this function are -/// optimized away, and in fact the optimizer is able to use the assertion -/// in its heuristics. -/// Inside a test block, it is best to use the `std.testing` module rather -/// than this function, because this function may not detect a test failure -/// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert -/// function is the correct function to use. -pub fn assert(ok: bool) void { - if (!ok) unreachable; // assertion failure -} - -pub fn panic(comptime format: []const u8, args: ...) noreturn { - @setCold(true); - const first_trace_addr = @returnAddress(); - panicExtra(null, first_trace_addr, format, args); -} - -/// TODO multithreaded awareness -var panicking: u8 = 0; // TODO make this a bool - -pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { - @setCold(true); - - if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { - // Panicked during a panic. - - // TODO detect if a different thread caused the panic, because in that case - // we would want to return here instead of calling abort, so that the thread - // which first called panic can finish printing a stack trace. - os.abort(); - } - const stderr = getStderrStream() catch os.abort(); - stderr.print(format ++ "\n", args) catch os.abort(); - if (trace) |t| { - dumpStackTrace(t.*); - } - dumpCurrentStackTrace(first_trace_addr); - - os.abort(); -} - -const RED = "\x1b[31;1m"; -const GREEN = "\x1b[32;1m"; -const CYAN = "\x1b[36;1m"; -const WHITE = "\x1b[37;1m"; -const DIM = "\x1b[2m"; -const RESET = "\x1b[0m"; - -pub fn writeStackTrace( - stack_trace: builtin.StackTrace, - out_stream: var, - allocator: *mem.Allocator, - debug_info: *DebugInfo, - tty_color: bool, -) !void { - var frame_index: usize = 0; - var frames_left: usize = stack_trace.index; - - 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); - } -} - -pub const StackIterator = struct { - first_addr: ?usize, - fp: usize, - - pub fn init(first_addr: ?usize) StackIterator { - return StackIterator{ - .first_addr = first_addr, - .fp = @frameAddress(), - }; - } - - fn next(self: *StackIterator) ?usize { - if (self.fp == 0) return null; - self.fp = @intToPtr(*const usize, self.fp).*; - if (self.fp == 0) return null; - - if (self.first_addr) |addr| { - while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { - const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; - if (addr == return_address) { - self.first_addr = null; - return return_address; - } - } - } - - const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; - return return_address; - } -}; - -pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { - switch (builtin.os) { - builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), - else => {}, - } - var it = StackIterator.init(start_addr); - while (it.next()) |return_address| { - try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); - } -} - -pub fn writeCurrentStackTraceWindows( - out_stream: var, - debug_info: *DebugInfo, - tty_color: bool, - start_addr: ?usize, -) !void { - var addr_buf: [1024]usize = undefined; - const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); - const addrs = addr_buf[0..n]; - var start_i: usize = if (start_addr) |saddr| blk: { - for (addrs) |addr, i| { - if (addr == saddr) break :blk i; - } - return; - } else 0; - for (addrs[start_i..]) |addr| { - try printSourceAtAddress(debug_info, out_stream, addr, tty_color); - } -} - -pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - switch (builtin.os) { - builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), - builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), - else => return error.UnsupportedOperatingSystem, - } -} - -fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { - const allocator = getDebugInfoAllocator(); - const base_address = os.getBaseAddress(); - const relative_address = relocated_address - base_address; - - var coff_section: *coff.Section = undefined; - const mod_index = for (di.sect_contribs) |sect_contrib| { - if (sect_contrib.Section > di.coff.sections.len) continue; - // Remember that SectionContribEntry.Section is 1-based. - coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; - - const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; - const vaddr_end = vaddr_start + sect_contrib.Size; - if (relative_address >= vaddr_start and relative_address < vaddr_end) { - break sect_contrib.ModuleIndex; - } - } else { - // we have no information to add to the address - if (tty_color) { - try out_stream.print("???:?:?: "); - setTtyColor(TtyColor.Dim); - try out_stream.print("0x{x} in ??? (???)", relocated_address); - setTtyColor(TtyColor.Reset); - try out_stream.print("\n\n\n"); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); - } - return; - }; - - const mod = &di.modules[mod_index]; - try populateModule(di, mod); - const obj_basename = os.path.basename(mod.obj_file_name); - - var symbol_i: usize = 0; - const symbol_name = while (symbol_i != mod.symbols.len) { - const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); - if (prefix.RecordLen < 2) - return error.InvalidDebugInfo; - switch (prefix.RecordKind) { - pdb.SymbolKind.S_LPROC32 => { - const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); - const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; - const vaddr_end = vaddr_start + proc_sym.CodeSize; - if (relative_address >= vaddr_start and relative_address < vaddr_end) { - break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); - } - }, - else => {}, - } - symbol_i += prefix.RecordLen + @sizeOf(u16); - if (symbol_i > mod.symbols.len) - return error.InvalidDebugInfo; - } else "???"; - - const subsect_info = mod.subsect_info; - - var sect_offset: usize = 0; - var skip_len: usize = undefined; - const opt_line_info = subsections: { - const checksum_offset = mod.checksum_offset orelse break :subsections null; - while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { - const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); - skip_len = subsect_hdr.Length; - sect_offset += @sizeOf(pdb.DebugSubsectionHeader); - - switch (subsect_hdr.Kind) { - pdb.DebugSubsectionKind.Lines => { - var line_index = sect_offset; - - const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); - if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; - line_index += @sizeOf(pdb.LineFragmentHeader); - const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; - const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; - - // 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) { - const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); - line_index += @sizeOf(pdb.LineBlockFragmentHeader); - const start_line_index = line_index; - - const has_column = line_hdr.Flags.LF_HaveColumns; - - if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { - // All line entries are stored inside their line block by ascending start address. - // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address. - // This is done with a simple linear search. - var line_i: u32 = 0; - while (line_i < block_hdr.NumLines) : (line_i += 1) { - const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); - line_index += @sizeOf(pdb.LineNumberEntry); - - const vaddr_start = frag_vaddr_start + line_num_entry.Offset; - if (relative_address <= vaddr_start) { - break; - } - } - - // line_i == 0 would mean that no matching LineNumberEntry was found. - if (line_i > 0) { - const subsect_index = checksum_offset + block_hdr.NameIndex; - const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); - const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; - try di.pdb.string_table.seekTo(strtab_offset); - const source_file_name = try di.pdb.string_table.readNullTermString(allocator); - - const line_entry_idx = line_i - 1; - - const column = if (has_column) blk: { - const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; - const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; - const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); - break :blk col_num_entry.StartColumn; - } else 0; - - const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); - const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); - const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); - - break :subsections LineInfo{ - .allocator = allocator, - .file_name = source_file_name, - .line = flags.Start, - .column = column, - }; - } - } - } - - // Checking that we are not reading garbage after the (possibly) multiple block fragments. - if (line_index != subsection_end_index) { - return error.InvalidDebugInfo; - } - }, - else => {}, - } - - if (sect_offset > subsect_info.len) - return error.InvalidDebugInfo; - } else { - break :subsections null; - } - }; - - if (tty_color) { - setTtyColor(TtyColor.White); - if (opt_line_info) |li| { - try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column); - } else { - try out_stream.print("???:?:?"); - } - setTtyColor(TtyColor.Reset); - try out_stream.print(": "); - setTtyColor(TtyColor.Dim); - try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); - setTtyColor(TtyColor.Reset); - - if (opt_line_info) |line_info| { - try out_stream.print("\n"); - if (printLineFromFileAnyOs(out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - { - var col_i: usize = 1; - while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - } - } - setTtyColor(TtyColor.Green); - try out_stream.write("^"); - setTtyColor(TtyColor.Reset); - try out_stream.write("\n"); - } - } else |err| switch (err) { - error.EndOfFile => {}, - else => return err, - } - } else { - try out_stream.print("\n\n\n"); - } - } else { - if (opt_line_info) |li| { - try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); - } else { - try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); - } - } -} - -const TtyColor = enum { - Red, - Green, - Cyan, - White, - Dim, - Bold, - Reset, -}; - -/// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt -fn setTtyColor(tty_color: TtyColor) void { - if (os.supportsAnsiEscapeCodes(stderr_file.handle)) { - switch (tty_color) { - TtyColor.Red => { - stderr_file.write(RED) catch return; - }, - TtyColor.Green => { - stderr_file.write(GREEN) catch return; - }, - TtyColor.Cyan => { - stderr_file.write(CYAN) catch return; - }, - TtyColor.White, TtyColor.Bold => { - stderr_file.write(WHITE) catch return; - }, - TtyColor.Dim => { - stderr_file.write(DIM) catch return; - }, - TtyColor.Reset => { - stderr_file.write(RESET) catch return; - }, - } - } else { - const S = struct { - var attrs: windows.WORD = undefined; - var init_attrs = false; - }; - if (!S.init_attrs) { - S.init_attrs = true; - var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; - // TODO handle error - _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); - S.attrs = info.wAttributes; - } - - // TODO handle errors - switch (tty_color) { - TtyColor.Red => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY); - }, - TtyColor.Green => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY); - }, - TtyColor.Cyan => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); - }, - TtyColor.White, TtyColor.Bold => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); - }, - TtyColor.Dim => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); - }, - TtyColor.Reset => { - _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); - }, - } - } -} - -fn populateModule(di: *DebugInfo, mod: *Module) !void { - if (mod.populated) - return; - const allocator = getDebugInfoAllocator(); - - if (mod.mod_info.C11ByteSize != 0) - return error.InvalidDebugInfo; - - if (mod.mod_info.C13ByteSize == 0) - return error.MissingDebugInfo; - - const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; - - const signature = try modi.stream.readIntLittle(u32); - if (signature != 4) - return error.InvalidDebugInfo; - - mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); - try modi.stream.readNoEof(mod.symbols); - - mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); - try modi.stream.readNoEof(mod.subsect_info); - - var sect_offset: usize = 0; - var skip_len: usize = undefined; - while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { - const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]); - skip_len = subsect_hdr.Length; - sect_offset += @sizeOf(pdb.DebugSubsectionHeader); - - switch (subsect_hdr.Kind) { - pdb.DebugSubsectionKind.FileChecksums => { - mod.checksum_offset = sect_offset; - break; - }, - else => {}, - } - - if (sect_offset > mod.subsect_info.len) - return error.InvalidDebugInfo; - } - - mod.populated = true; -} - -fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { - var min: usize = 0; - var max: usize = symbols.len - 1; // Exclude sentinel. - while (min < max) { - const mid = min + (max - min) / 2; - const curr = &symbols[mid]; - const next = &symbols[mid + 1]; - if (address >= next.address()) { - min = mid + 1; - } else if (address < curr.address()) { - max = mid; - } else { - return curr; - } - } - return null; -} - -fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - const base_addr = std.os.getBaseAddress(); - const adjusted_addr = 0x100000000 + (address - base_addr); - - const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); - } - return; - }; - - const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx); - const compile_unit_name = if (symbol.ofile) |ofile| blk: { - const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); - break :blk os.path.basename(ofile_path); - } else "???"; - if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { - defer line_info.deinit(); - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_color, - printLineFromFileAnyOs, - ); - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); - } else { - try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name); - } - }, - else => return err, - } -} - -/// This function works in freestanding mode. -/// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void -pub fn printSourceAtAddressDwarf( - debug_info: *DwarfInfo, - out_stream: var, - address: usize, - tty_color: bool, - comptime printLineFromFile: var, -) !void { - const compile_unit = findCompileUnit(debug_info, address) catch { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); - } - 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| { - defer line_info.deinit(); - const symbol_name = "???"; - try printLineInfo( - out_stream, - line_info, - address, - symbol_name, - compile_unit_name, - tty_color, - printLineFromFile, - ); - } else |err| switch (err) { - error.MissingDebugInfo, error.InvalidDebugInfo => { - if (tty_color) { - try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); - } else { - try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); - } - }, - else => return err, - } -} - -pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { - return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); -} - -fn printLineInfo( - out_stream: var, - line_info: LineInfo, - address: usize, - symbol_name: []const u8, - compile_unit_name: []const u8, - tty_color: bool, - comptime printLineFromFile: var, -) !void { - if (tty_color) { - try out_stream.print( - WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", - line_info.file_name, - line_info.line, - line_info.column, - address, - symbol_name, - compile_unit_name, - ); - if (printLineFromFile(out_stream, line_info)) { - if (line_info.column == 0) { - try out_stream.write("\n"); - } else { - { - var col_i: usize = 1; - while (col_i < line_info.column) : (col_i += 1) { - try out_stream.writeByte(' '); - } - } - try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); - } - } else |err| switch (err) { - error.EndOfFile => {}, - else => return err, - } - } else { - try out_stream.print( - "{}:{}:{}: 0x{x} in {} ({})\n", - line_info.file_name, - line_info.line, - line_info.column, - address, - symbol_name, - compile_unit_name, - ); - } -} - -// TODO use this -pub const OpenSelfDebugInfoError = error{ - MissingDebugInfo, - OutOfMemory, - UnsupportedOperatingSystem, -}; - -pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { - switch (builtin.os) { - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator), - builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), - builtin.Os.windows => return openSelfDebugInfoWindows(allocator), - else => return error.UnsupportedOperatingSystem, - } -} - -fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { - const self_file = try os.openSelfExe(); - defer self_file.close(); - - const coff_obj = try allocator.create(coff.Coff); - coff_obj.* = coff.Coff{ - .in_file = self_file, - .allocator = allocator, - .coff_header = undefined, - .pe_header = undefined, - .sections = undefined, - .guid = undefined, - .age = undefined, - }; - - var di = DebugInfo{ - .coff = coff_obj, - .pdb = undefined, - .sect_contribs = undefined, - .modules = undefined, - }; - - try di.coff.loadHeader(); - - var path_buf: [windows.MAX_PATH]u8 = undefined; - const len = try di.coff.getPdbPath(path_buf[0..]); - const raw_path = path_buf[0..len]; - - const path = try os.path.resolve(allocator, [][]const u8{raw_path}); - - try di.pdb.openFile(di.coff, path); - - var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; - const version = try pdb_stream.stream.readIntLittle(u32); - const signature = try pdb_stream.stream.readIntLittle(u32); - const age = try pdb_stream.stream.readIntLittle(u32); - var guid: [16]u8 = undefined; - try pdb_stream.stream.readNoEof(guid[0..]); - if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) - return error.InvalidDebugInfo; - // We validated the executable and pdb match. - - const string_table_index = str_tab_index: { - const name_bytes_len = try pdb_stream.stream.readIntLittle(u32); - const name_bytes = try allocator.alloc(u8, name_bytes_len); - try pdb_stream.stream.readNoEof(name_bytes); - - const HashTableHeader = packed struct { - Size: u32, - Capacity: u32, - - fn maxLoad(cap: u32) u32 { - return cap * 2 / 3 + 1; - } - }; - const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader); - if (hash_tbl_hdr.Capacity == 0) - return error.InvalidDebugInfo; - - if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) - return error.InvalidDebugInfo; - - const present = try readSparseBitVector(&pdb_stream.stream, allocator); - if (present.len != hash_tbl_hdr.Size) - return error.InvalidDebugInfo; - const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); - - const Bucket = struct { - first: u32, - second: u32, - }; - const bucket_list = try allocator.alloc(Bucket, present.len); - for (present) |_| { - const name_offset = try pdb_stream.stream.readIntLittle(u32); - const name_index = try pdb_stream.stream.readIntLittle(u32); - const name = mem.toSlice(u8, name_bytes.ptr + name_offset); - if (mem.eql(u8, name, "/names")) { - break :str_tab_index name_index; - } - } - return error.MissingDebugInfo; - }; - - di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; - di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; - - const dbi = di.pdb.dbi; - - // Dbi Header - const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); - const mod_info_size = dbi_stream_header.ModInfoSize; - const section_contrib_size = dbi_stream_header.SectionContributionSize; - - var modules = ArrayList(Module).init(allocator); - - // Module Info Substream - var mod_info_offset: usize = 0; - while (mod_info_offset != mod_info_size) { - const mod_info = try dbi.stream.readStruct(pdb.ModInfo); - var this_record_len: usize = @sizeOf(pdb.ModInfo); - - const module_name = try dbi.readNullTermString(allocator); - this_record_len += module_name.len + 1; - - const obj_file_name = try dbi.readNullTermString(allocator); - this_record_len += obj_file_name.len + 1; - - const march_forward_bytes = this_record_len % 4; - if (march_forward_bytes != 0) { - try dbi.seekForward(march_forward_bytes); - this_record_len += march_forward_bytes; - } - - try modules.append(Module{ - .mod_info = mod_info, - .module_name = module_name, - .obj_file_name = obj_file_name, - - .populated = false, - .symbols = undefined, - .subsect_info = undefined, - .checksum_offset = null, - }); - - mod_info_offset += this_record_len; - if (mod_info_offset > mod_info_size) - return error.InvalidDebugInfo; - } - - di.modules = modules.toOwnedSlice(); - - // Section Contribution Substream - var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); - var sect_cont_offset: usize = 0; - if (section_contrib_size != 0) { - const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32)); - if (ver != pdb.SectionContrSubstreamVersion.Ver60) - return error.InvalidDebugInfo; - sect_cont_offset += @sizeOf(u32); - } - while (sect_cont_offset != section_contrib_size) { - const entry = try sect_contribs.addOne(); - entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry); - sect_cont_offset += @sizeOf(pdb.SectionContribEntry); - - if (sect_cont_offset > section_contrib_size) - return error.InvalidDebugInfo; - } - - di.sect_contribs = sect_contribs.toOwnedSlice(); - - return di; -} - -fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { - const num_words = try stream.readIntLittle(u32); - var word_i: usize = 0; - var list = ArrayList(usize).init(allocator); - while (word_i != num_words) : (word_i += 1) { - const word = try stream.readIntLittle(u32); - var bit_i: u5 = 0; - while (true) : (bit_i += 1) { - if (word & (u32(1) << bit_i) != 0) { - try list.append(word_i * 32 + bit_i); - } - if (bit_i == maxInt(u5)) break; - } - } - return list.toOwnedSlice(); -} - -fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { - const elf_header = (try elf_file.findSection(name)) orelse return null; - return DwarfInfo.Section{ - .offset = elf_header.offset, - .size = elf_header.size, - }; -} - -/// Initialize DWARF info. The caller has the responsibility to initialize most -/// the DwarfInfo fields before calling. These fields can be left undefined: -/// * abbrev_table_list -/// * compile_unit_list -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); - try scanAllCompileUnits(di); -} - -pub fn openElfDebugInfo( - allocator: *mem.Allocator, - elf_seekable_stream: *DwarfSeekableStream, - elf_in_stream: *DwarfInStream, -) !DwarfInfo { - var efile: elf.Elf = undefined; - try efile.openStream(allocator, elf_seekable_stream, elf_in_stream); - errdefer efile.close(); - - var di = DwarfInfo{ - .dwarf_seekable_stream = elf_seekable_stream, - .dwarf_in_stream = elf_in_stream, - .endian = efile.endian, - .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, - .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, - .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, - .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, - .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), - .abbrev_table_list = undefined, - .compile_unit_list = undefined, - }; - try openDwarfDebugInfo(&di, allocator); - return di; -} - -fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { - const S = struct { - var self_exe_file: os.File = undefined; - var self_exe_seekable_stream: os.File.SeekableStream = undefined; - var self_exe_in_stream: os.File.InStream = undefined; - }; - S.self_exe_file = try os.openSelfExe(); - errdefer S.self_exe_file.close(); - - S.self_exe_seekable_stream = S.self_exe_file.seekableStream(); - S.self_exe_in_stream = S.self_exe_file.inStream(); - - return openElfDebugInfo( - allocator, - // TODO https://github.com/ziglang/zig/issues/764 - @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream), - // TODO https://github.com/ziglang/zig/issues/764 - @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream), - ); -} - -fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { - const hdr = &std.c._mh_execute_header; - assert(hdr.magic == std.macho.MH_MAGIC_64); - - const hdr_base = @ptrCast([*]u8, hdr); - var ptr = hdr_base + @sizeOf(macho.mach_header_64); - var ncmd: u32 = hdr.ncmds; - const symtab = while (ncmd != 0) : (ncmd -= 1) { - const lc = @ptrCast(*std.macho.load_command, ptr); - switch (lc.cmd) { - std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), - else => {}, - } - ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 - } else { - return error.MissingDebugInfo; - }; - const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; - const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; - - const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); - - var ofile: ?*macho.nlist_64 = null; - var reloc: u64 = 0; - var symbol_index: usize = 0; - var last_len: u64 = 0; - for (syms) |*sym| { - if (sym.n_type & std.macho.N_STAB != 0) { - switch (sym.n_type) { - std.macho.N_OSO => { - ofile = sym; - reloc = 0; - }, - std.macho.N_FUN => { - if (sym.n_sect == 0) { - last_len = sym.n_value; - } else { - symbols_buf[symbol_index] = MachoSymbol{ - .nlist = sym, - .ofile = ofile, - .reloc = reloc, - }; - symbol_index += 1; - } - }, - std.macho.N_BNSYM => { - if (reloc == 0) { - reloc = sym.n_value; - } - }, - else => continue, - } - } - } - const sentinel = try allocator.create(macho.nlist_64); - sentinel.* = macho.nlist_64{ - .n_strx = 0, - .n_type = 36, - .n_sect = 0, - .n_desc = 0, - .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, - }; - - const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index); - - // Even though lld emits symbols in ascending order, this debug code - // should work for programs linked in any valid way. - // This sort is so that we can binary search later. - std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); - - return DebugInfo{ - .ofiles = DebugInfo.OFileTable.init(allocator), - .symbols = symbols, - .strings = strings, - }; -} - -fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { - var f = try os.File.openRead(line_info.file_name); - defer f.close(); - // TODO fstat and make sure that the file has the correct size - - var buf: [os.page_size]u8 = undefined; - var line: usize = 1; - var column: usize = 1; - var abs_index: usize = 0; - while (true) { - const amt_read = try f.read(buf[0..]); - const slice = buf[0..amt_read]; - - for (slice) |byte| { - if (line == line_info.line) { - try out_stream.writeByte(byte); - if (byte == '\n') { - return; - } - } - if (byte == '\n') { - line += 1; - column = 1; - } else { - column += 1; - } - } - - if (amt_read < buf.len) return error.EndOfFile; - } -} - -const MachoSymbol = struct { - nlist: *macho.nlist_64, - ofile: ?*macho.nlist_64, - reloc: u64, - - /// Returns the address from the macho file - fn address(self: MachoSymbol) u64 { - return self.nlist.n_value; - } - - fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool { - return lhs.address() < rhs.address(); - } -}; - -const MachOFile = struct { - bytes: []align(@alignOf(macho.mach_header_64)) const u8, - sect_debug_info: ?*const macho.section_64, - sect_debug_line: ?*const macho.section_64, -}; - -pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); -pub const DwarfInStream = io.InStream(anyerror); - -pub const DwarfInfo = struct { - dwarf_seekable_stream: *DwarfSeekableStream, - dwarf_in_stream: *DwarfInStream, - endian: builtin.Endian, - debug_info: Section, - debug_abbrev: Section, - debug_str: Section, - debug_line: Section, - debug_ranges: ?Section, - abbrev_table_list: ArrayList(AbbrevTableHeader), - compile_unit_list: ArrayList(CompileUnit), - - pub const Section = struct { - offset: usize, - size: usize, - }; - - pub fn allocator(self: DwarfInfo) *mem.Allocator { - return self.abbrev_table_list.allocator; - } - - pub fn readString(self: *DwarfInfo) ![]u8 { - return readStringRaw(self.allocator(), self.dwarf_in_stream); - } -}; - -pub const DebugInfo = switch (builtin.os) { - builtin.Os.macosx => struct { - symbols: []const MachoSymbol, - strings: []const u8, - ofiles: OFileTable, - - const OFileTable = std.HashMap( - *macho.nlist_64, - MachOFile, - std.hash_map.getHashPtrAddrFn(*macho.nlist_64), - std.hash_map.getTrivialEqlFn(*macho.nlist_64), - ); - - pub fn allocator(self: DebugInfo) *mem.Allocator { - return self.ofiles.allocator; - } - }, - builtin.Os.uefi, builtin.Os.windows => struct { - pdb: pdb.Pdb, - coff: *coff.Coff, - sect_contribs: []pdb.SectionContribEntry, - modules: []Module, - }, - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo, - else => @compileError("Unsupported OS"), -}; - -const PcRange = struct { - start: u64, - end: u64, -}; - -const CompileUnit = struct { - version: u16, - is_64: bool, - die: *Die, - index: usize, - pc_range: ?PcRange, -}; - -const AbbrevTable = ArrayList(AbbrevTableEntry); - -const AbbrevTableHeader = struct { - // offset from .debug_abbrev - offset: u64, - table: AbbrevTable, -}; - -const AbbrevTableEntry = struct { - has_children: bool, - abbrev_code: u64, - tag_id: u64, - attrs: ArrayList(AbbrevAttr), -}; - -const AbbrevAttr = struct { - attr_id: u64, - form_id: u64, -}; - -const FormValue = union(enum) { - Address: u64, - Block: []u8, - Const: Constant, - ExprLoc: []u8, - Flag: bool, - SecOffset: u64, - Ref: []u8, - RefAddr: u64, - RefSig8: u64, - String: []u8, - StrPtr: u64, -}; - -const Constant = struct { - payload: []u8, - 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); - } -}; - -const Die = struct { - tag_id: u64, - has_children: bool, - attrs: ArrayList(Attr), - - const Attr = struct { - id: u64, - value: FormValue, - }; - - fn getAttr(self: *const Die, id: u64) ?*const FormValue { - for (self.attrs.toSliceConst()) |*attr| { - if (attr.id == id) return &attr.value; - } - return null; - } - - fn getAttrAddr(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Address => |value| value, - else => error.InvalidDebugInfo, - }; - } - - fn getAttrSecOffset(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Const => |value| value.asUnsignedLe(), - FormValue.SecOffset => |value| value, - else => error.InvalidDebugInfo, - }; - } - - fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { - const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; - return switch (form_value.*) { - FormValue.Const => |value| value.asUnsignedLe(), - 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.*) { - FormValue.String => |value| value, - FormValue.StrPtr => |offset| getString(di, offset), - else => error.InvalidDebugInfo, - }; - } -}; - -const FileEntry = struct { - file_name: []const u8, - dir_index: usize, - mtime: usize, - len_bytes: usize, -}; - -pub const LineInfo = struct { - line: usize, - column: usize, - file_name: []const u8, - allocator: ?*mem.Allocator, - - fn deinit(self: LineInfo) void { - const allocator = self.allocator orelse return; - allocator.free(self.file_name); - } -}; - -const LineNumberProgram = struct { - address: usize, - file: usize, - line: isize, - column: usize, - is_stmt: bool, - basic_block: bool, - end_sequence: bool, - - target_address: usize, - include_dirs: []const []const u8, - file_entries: *ArrayList(FileEntry), - - prev_address: usize, - prev_file: usize, - prev_line: isize, - prev_column: usize, - prev_is_stmt: bool, - prev_basic_block: bool, - prev_end_sequence: bool, - - pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { - return LineNumberProgram{ - .address = 0, - .file = 1, - .line = 1, - .column = 0, - .is_stmt = is_stmt, - .basic_block = false, - .end_sequence = false, - .include_dirs = include_dirs, - .file_entries = file_entries, - .target_address = target_address, - .prev_address = 0, - .prev_file = undefined, - .prev_line = undefined, - .prev_column = undefined, - .prev_is_stmt = undefined, - .prev_basic_block = undefined, - .prev_end_sequence = undefined, - }; - } - - pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { - if (self.target_address >= self.prev_address and self.target_address < self.address) { - const file_entry = if (self.prev_file == 0) { - return error.MissingDebugInfo; - } else if (self.prev_file - 1 >= self.file_entries.len) { - return error.InvalidDebugInfo; - } else - &self.file_entries.items[self.prev_file - 1]; - - const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { - return error.InvalidDebugInfo; - } else - self.include_dirs[file_entry.dir_index]; - const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); - errdefer self.file_entries.allocator.free(file_name); - return LineInfo{ - .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, - .column = self.prev_column, - .file_name = file_name, - .allocator = self.file_entries.allocator, - }; - } - - self.prev_address = self.address; - self.prev_file = self.file; - self.prev_line = self.line; - self.prev_column = self.column; - self.prev_is_stmt = self.is_stmt; - self.prev_basic_block = self.basic_block; - self.prev_end_sequence = self.end_sequence; - return null; - } -}; - -fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { - var buf = ArrayList(u8).init(allocator); - while (true) { - const byte = try in_stream.readByte(); - if (byte == 0) break; - try buf.append(byte); - } - return buf.toSlice(); -} - -fn getString(di: *DwarfInfo, offset: u64) ![]u8 { - const pos = di.debug_str.offset + offset; - try di.dwarf_seekable_stream.seekTo(pos); - return di.readString(); -} - -fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { - const buf = try allocator.alloc(u8, size); - errdefer allocator.free(buf); - if ((try in_stream.read(buf)) < size) return error.EndOfFile; - return buf; -} - -fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { - const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue{ .Block = buf }; -} - -fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { - const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size); - return parseFormValueBlockLen(allocator, in_stream, block_len); -} - -fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { - return FormValue{ - .Const = Constant{ - .signed = signed, - .payload = try readAllocBytes(allocator, in_stream, size), - }, - }; -} - -fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { - return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32)); -} - -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 parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { - return switch (form_id) { - DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, - DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), - 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); - return parseFormValueBlockLen(allocator, in_stream, block_len); - }, - DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), - DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), - 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); - }, - DW.FORM_exprloc => { - const size = try readULeb128(in_stream); - const buf = try readAllocBytes(allocator, in_stream, size); - return FormValue{ .ExprLoc = buf }; - }, - DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 }, - 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_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, - DW.FORM_ref_sig8 => FormValue{ .RefSig8 = 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); - return parseFormValue(allocator, in_stream, child_form_id, is_64); - }, - else => error.InvalidDebugInfo, - }; -} - -fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { - var result = AbbrevTable.init(di.allocator()); - while (true) { - const abbrev_code = try readULeb128(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), - .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); - if (attr_id == 0 and form_id == 0) break; - try attrs.append(AbbrevAttr{ - .attr_id = attr_id, - .form_id = form_id, - }); - } - } -} - -/// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, -/// seeks in the stream and parses it. -fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { - for (di.abbrev_table_list.toSlice()) |*header| { - if (header.offset == abbrev_offset) { - return &header.table; - } - } - try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); - try di.abbrev_table_list.append(AbbrevTableHeader{ - .offset = abbrev_offset, - .table = try parseAbbrevTable(di), - }); - return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; -} - -fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { - for (abbrev_table.toSliceConst()) |*table_entry| { - if (table_entry.abbrev_code == abbrev_code) return table_entry; - } - return null; -} - -fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { - const abbrev_code = try readULeb128(di.dwarf_in_stream); - 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 getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { - const ofile = symbol.ofile orelse return error.MissingDebugInfo; - const gop = try di.ofiles.getOrPut(ofile); - const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { - errdefer _ = di.ofiles.remove(ofile); - const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); - - gop.kv.value = MachOFile{ - .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)), - .sect_debug_info = null, - .sect_debug_line = null, - }; - const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr); - if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; - - const hdr_base = @ptrCast([*]const u8, hdr); - var ptr = hdr_base + @sizeOf(macho.mach_header_64); - var ncmd: u32 = hdr.ncmds; - const segcmd = while (ncmd != 0) : (ncmd -= 1) { - const lc = @ptrCast(*const std.macho.load_command, ptr); - switch (lc.cmd) { - std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), - else => {}, - } - ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 - } else { - return error.MissingDebugInfo; - }; - const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; - for (sections) |*sect| { - if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and - (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) - { - const sect_name = mem.toSliceConst(u8, §.sectname); - if (mem.eql(u8, sect_name, "__debug_line")) { - gop.kv.value.sect_debug_line = sect; - } else if (mem.eql(u8, sect_name, "__debug_info")) { - gop.kv.value.sect_debug_info = sect; - } - } - } - - break :blk &gop.kv.value; - }; - - const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; - var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; - - var is_64: bool = undefined; - const unit_length = try readInitialLengthMem(&ptr, &is_64); - if (unit_length == 0) return error.MissingDebugInfo; - - const version = readIntMem(&ptr, u16, builtin.Endian.Little); - // TODO support 3 and 5 - if (version != 2 and version != 4) return error.InvalidDebugInfo; - - const prologue_length = if (is_64) - readIntMem(&ptr, u64, builtin.Endian.Little) - else - readIntMem(&ptr, u32, builtin.Endian.Little); - const prog_start = ptr + prologue_length; - - const minimum_instruction_length = readByteMem(&ptr); - if (minimum_instruction_length == 0) return error.InvalidDebugInfo; - - if (version >= 4) { - // maximum_operations_per_instruction - ptr += 1; - } - - const default_is_stmt = readByteMem(&ptr) != 0; - const line_base = readByteSignedMem(&ptr); - - const line_range = readByteMem(&ptr); - if (line_range == 0) return error.InvalidDebugInfo; - - const opcode_base = readByteMem(&ptr); - - const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; - ptr += opcode_base - 1; - - var include_directories = ArrayList([]const u8).init(di.allocator()); - try include_directories.append(""); - while (true) { - const dir = readStringMem(&ptr); - 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); - - 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); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - } - - ptr = prog_start; - while (true) { - const opcode = readByteMem(&ptr); - - if (opcode == DW.LNS_extended_op) { - const op_size = try readULeb128Mem(&ptr); - if (op_size < 1) return error.InvalidDebugInfo; - var sub_op = readByteMem(&ptr); - 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 = readIntMem(&ptr, usize, builtin.Endian.Little); - prog.address = symbol.reloc + addr; - }, - 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); - try file_entries.append(FileEntry{ - .file_name = file_name, - .dir_index = dir_index, - .mtime = mtime, - .len_bytes = len_bytes, - }); - }, - else => { - ptr += op_size - 1; - }, - } - } 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 readULeb128Mem(&ptr); - prog.address += arg * minimum_instruction_length; - }, - DW.LNS_advance_line => { - const arg = try readILeb128Mem(&ptr); - prog.line += arg; - }, - DW.LNS_set_file => { - const arg = try readULeb128Mem(&ptr); - prog.file = arg; - }, - DW.LNS_set_column => { - const arg = try readULeb128Mem(&ptr); - 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 = readIntMem(&ptr, u16, builtin.Endian.Little); - 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]; - ptr += len_bytes; - }, - } - } - } - - return error.MissingDebugInfo; -} - -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 debug_line_end = di.debug_line.offset + di.debug_line.size; - var this_offset = di.debug_line.offset; - var this_index: usize = 0; - - while (this_offset < debug_line_end) : (this_index += 1) { - try di.dwarf_seekable_stream.seekTo(this_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)); - - 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 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 opcode_base = try di.dwarf_in_stream.readByte(); - - const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); - - { - var i: usize = 0; - while (i < opcode_base - 1) : (i += 1) { - standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); - } - } - - 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); - - 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, - }); - } - - try di.dwarf_seekable_stream.seekTo(prog_start_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); - }, - } - } - } - - this_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); - - 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 compile_unit_die = try di.allocator().create(Die); - compile_unit_die.* = try parseDie(di, abbrev_table, is_64); - - if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; - - const pc_range = x: { - if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { - if (compile_unit_die.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.compile_unit_list.append(CompileUnit{ - .version = version, - .is_64 = is_64, - .pc_range = pc_range, - .die = compile_unit_die, - .index = cu_index, - }); - - this_unit_offset += next_offset; - cu_index += 1; - } -} - -fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { - for (di.compile_unit_list.toSlice()) |*compile_unit| { - if (compile_unit.pc_range) |range| { - if (target_address >= range.start and target_address < range.end) return compile_unit; - } - if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { - var base_address: usize = 0; - if (di.debug_ranges) |debug_ranges| { - try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); - while (true) { - const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); - const end_addr = try di.dwarf_in_stream.readIntLittle(usize); - if (begin_addr == 0 and end_addr == 0) { - break; - } - if (begin_addr == maxInt(usize)) { - base_address = begin_addr; - continue; - } - if (target_address >= begin_addr and target_address < end_addr) { - return compile_unit; - } - } - } - } else |err| { - if (err != error.MissingDebugInfo) return err; - continue; - } - } - return error.MissingDebugInfo; -} - -fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { - // TODO https://github.com/ziglang/zig/issues/863 - const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian); - ptr.* += @sizeOf(T); - return result; -} - -fn readByteMem(ptr: *[*]const u8) u8 { - const result = ptr.*[0]; - ptr.* += 1; - return result; -} - -fn readByteSignedMem(ptr: *[*]const u8) i8 { - return @bitCast(i8, readByteMem(ptr)); -} - -fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { - // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 - const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); - is_64.* = (first_32_bits == 0xffffffff); - if (is_64.*) { - ptr.* += 4; - const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); - ptr.* += 8; - return result; - } else { - if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; - ptr.* += 4; - return u64(first_32_bits); - } -} - -fn readStringMem(ptr: *[*]const u8) []const u8 { - const result = mem.toSliceConst(u8, ptr.*); - ptr.* += result.len + 1; - 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); - if (is_64.*) { - return in_stream.readIntLittle(u64); - } else { - if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; - return u64(first_32_bits); - } -} - -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..]); -var global_allocator_mem: [100 * 1024]u8 = undefined; - -/// TODO multithreaded awareness -var debug_info_allocator: ?*mem.Allocator = null; -var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; -var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; -fn getDebugInfoAllocator() *mem.Allocator { - if (debug_info_allocator) |a| return a; - - debug_info_direct_allocator = std.heap.DirectAllocator.init(); - debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator); - debug_info_allocator = &debug_info_arena_allocator.allocator; - return &debug_info_arena_allocator.allocator; -} diff --git a/std/dynamic_library.zig b/std/dynamic_library.zig index 66026b1f29..698b0eb216 100644 --- a/std/dynamic_library.zig +++ b/std/dynamic_library.zig @@ -1,7 +1,7 @@ const builtin = @import("builtin"); const Os = builtin.Os; -const std = @import("index.zig"); +const std = @import("std.zig"); const mem = std.mem; const cstr = std.cstr; const os = std.os; diff --git a/std/elf.zig b/std/elf.zig index 6a564c3283..d0ec5fb2ae 100644 --- a/std/elf.zig +++ b/std/elf.zig @@ -1,5 +1,5 @@ const builtin = @import("builtin"); -const std = @import("index.zig"); +const std = @import("std.zig"); const io = std.io; const os = std.os; const math = std.math; diff --git a/std/event/channel.zig b/std/event/channel.zig index 4af9bf612b..be5af49774 100644 --- a/std/event/channel.zig +++ b/std/event/channel.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; diff --git a/std/event/fs.zig b/std/event/fs.zig index 6535945573..b923651b1b 100644 --- a/std/event/fs.zig +++ b/std/event/fs.zig @@ -1,5 +1,5 @@ const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const event = std.event; const assert = std.debug.assert; const testing = std.testing; @@ -404,11 +404,7 @@ pub async fn openPosix( pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { switch (builtin.os) { - builtin.Os.macosx, - builtin.Os.linux, - builtin.Os.freebsd, - builtin.Os.netbsd - => { + builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC; return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); }, @@ -876,10 +872,7 @@ pub fn Watch(comptime V: type) type { pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { switch (builtin.os) { - builtin.Os.macosx, - builtin.Os.freebsd, - builtin.Os.netbsd - => return await (async addFileKEvent(self, file_path, value) catch unreachable), + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => return await (async addFileKEvent(self, file_path, value) catch unreachable), builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable), else => @compileError("Unsupported OS"), diff --git a/std/event/future.zig b/std/event/future.zig index 66acac5ad7..0e1b928898 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; const testing = std.testing; const builtin = @import("builtin"); diff --git a/std/event/group.zig b/std/event/group.zig index 25e79640cb..455d1bd60c 100644 --- a/std/event/group.zig +++ b/std/event/group.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const Lock = std.event.Lock; const Loop = std.event.Loop; diff --git a/std/event/io.zig b/std/event/io.zig index b11550f7aa..29419a792e 100644 --- a/std/event/io.zig +++ b/std/event/io.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; diff --git a/std/event/lock.zig b/std/event/lock.zig index d6a246cee5..031b2adf19 100644 --- a/std/event/lock.zig +++ b/std/event/lock.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; diff --git a/std/event/locked.zig b/std/event/locked.zig index 6718b1bf9c..1ceaa33530 100644 --- a/std/event/locked.zig +++ b/std/event/locked.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const Lock = std.event.Lock; const Loop = std.event.Loop; diff --git a/std/event/loop.zig b/std/event/loop.zig index 55a1538f0c..a327d03792 100644 --- a/std/event/loop.zig +++ b/std/event/loop.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; diff --git a/std/event/net.zig b/std/event/net.zig index 48461c3d81..687c119920 100644 --- a/std/event/net.zig +++ b/std/event/net.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const testing = std.testing; const event = std.event; diff --git a/std/event/rwlock.zig b/std/event/rwlock.zig index 26ccd12b73..25b0f0e74b 100644 --- a/std/event/rwlock.zig +++ b/std/event/rwlock.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; diff --git a/std/event/rwlocked.zig b/std/event/rwlocked.zig index d305b1791e..0448e0298e 100644 --- a/std/event/rwlocked.zig +++ b/std/event/rwlocked.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const RwLock = std.event.RwLock; const Loop = std.event.Loop; diff --git a/std/fmt.zig b/std/fmt.zig new file mode 100644 index 0000000000..e01ba2033b --- /dev/null +++ b/std/fmt.zig @@ -0,0 +1,1441 @@ +const std = @import("std.zig"); +const math = std.math; +const debug = std.debug; +const assert = debug.assert; +const testing = std.testing; +const mem = std.mem; +const builtin = @import("builtin"); +const errol = @import("fmt/errol.zig"); +const lossyCast = std.math.lossyCast; + +const max_int_digits = 65; + +/// Renders fmt string with args, calling output with slices of bytes. +/// If `output` returns an error, the error is returned from `format` and +/// `output` is not called again. +pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { + const State = enum { + Start, + OpenBrace, + CloseBrace, + FormatString, + Pointer, + }; + + comptime var start_index = 0; + comptime var state = State.Start; + comptime var next_arg = 0; + + inline for (fmt) |c, i| { + switch (state) { + State.Start => switch (c) { + '{' => { + if (start_index < i) { + try output(context, fmt[start_index..i]); + } + start_index = i; + state = State.OpenBrace; + }, + + '}' => { + if (start_index < i) { + try output(context, fmt[start_index..i]); + } + state = State.CloseBrace; + }, + else => {}, + }, + State.OpenBrace => switch (c) { + '{' => { + state = State.Start; + start_index = i; + }, + '}' => { + try formatType(args[next_arg], fmt[0..0], context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '*' => state = State.Pointer, + else => { + state = State.FormatString; + }, + }, + State.CloseBrace => switch (c) { + '}' => { + state = State.Start; + start_index = i; + }, + else => @compileError("Single '}' encountered in format string"), + }, + State.FormatString => switch (c) { + '}' => { + const s = start_index + 1; + try formatType(args[next_arg], fmt[s..i], context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + else => {}, + }, + State.Pointer => switch (c) { + '}' => { + try output(context, @typeName(@typeOf(args[next_arg]).Child)); + try output(context, "@"); + try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + else => @compileError("Unexpected format character after '*'"), + }, + } + } + comptime { + if (args.len != next_arg) { + @compileError("Unused arguments"); + } + if (state != State.Start) { + @compileError("Incomplete format string: " ++ fmt); + } + } + if (start_index < fmt.len) { + try output(context, fmt[start_index..]); + } +} + +pub fn formatType( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + const T = @typeOf(value); + if (T == anyerror) { + try output(context, "error."); + return output(context, @errorName(value)); + } + switch (@typeInfo(T)) { + builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { + return formatValue(value, fmt, context, Errors, output); + }, + builtin.TypeId.Void => { + return output(context, "void"); + }, + builtin.TypeId.Bool => { + return output(context, if (value) "true" else "false"); + }, + builtin.TypeId.Optional => { + if (value) |payload| { + return formatType(payload, fmt, context, Errors, output); + } else { + return output(context, "null"); + } + }, + builtin.TypeId.ErrorUnion => { + if (value) |payload| { + return formatType(payload, fmt, context, Errors, output); + } else |err| { + return formatType(err, fmt, context, Errors, output); + } + }, + builtin.TypeId.ErrorSet => { + try output(context, "error."); + return output(context, @errorName(value)); + }, + builtin.TypeId.Promise => { + return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); + }, + builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { + const has_cust_fmt = comptime cf: { + const info = @typeInfo(T); + const defs = switch (info) { + builtin.TypeId.Struct => |s| s.defs, + builtin.TypeId.Union => |u| u.defs, + builtin.TypeId.Enum => |e| e.defs, + else => unreachable, + }; + + for (defs) |def| { + if (mem.eql(u8, def.name, "format")) { + break :cf true; + } + } + break :cf false; + }; + if (has_cust_fmt) return value.format(fmt, context, Errors, output); + + try output(context, @typeName(T)); + switch (comptime @typeId(T)) { + builtin.TypeId.Enum => { + try output(context, "."); + try formatType(@tagName(value), "", context, Errors, output); + return; + }, + builtin.TypeId.Struct => { + comptime var field_i = 0; + inline while (field_i < @memberCount(T)) : (field_i += 1) { + if (field_i == 0) { + try output(context, "{ ."); + } else { + try output(context, ", ."); + } + try output(context, @memberName(T, field_i)); + try output(context, " = "); + try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output); + } + try output(context, " }"); + }, + builtin.TypeId.Union => { + const info = @typeInfo(T).Union; + if (info.tag_type) |UnionTagType| { + try output(context, "{ ."); + try output(context, @tagName(UnionTagType(value))); + try output(context, " = "); + inline for (info.fields) |u_field| { + if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { + try formatType(@field(value, u_field.name), "", context, Errors, output); + } + } + try output(context, " }"); + } else { + try format(context, Errors, output, "@{x}", @ptrToInt(&value)); + } + }, + else => unreachable, + } + return; + }, + builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { + builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { + builtin.TypeId.Array => |info| { + if (info.child == u8) { + return formatText(value, fmt, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, + builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { + return formatType(value.*, fmt, context, Errors, output); + }, + else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), + }, + builtin.TypeInfo.Pointer.Size.Many => { + if (ptr_info.child == u8) { + if (fmt.len > 0 and fmt[0] == 's') { + const len = std.cstr.len(value); + return formatText(value[0..len], fmt, context, Errors, output); + } + } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, + builtin.TypeInfo.Pointer.Size.Slice => { + if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { + return formatText(value, fmt, context, Errors, output); + } + const casted_value = ([]const u8)(value); + return output(context, casted_value); + }, + builtin.TypeInfo.Pointer.Size.C => { + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); + }, + }, + builtin.TypeId.Array => |info| { + if (info.child == u8) { + return formatText(value, fmt, context, Errors, output); + } + return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); + }, + builtin.TypeId.Fn => { + return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); + }, + else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), + } +} + +fn formatValue( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + if (fmt.len > 0) { + if (fmt[0] == 'B') { + comptime var width: ?usize = null; + if (fmt.len > 1) { + if (fmt[1] == 'i') { + if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); + return formatBytes(value, width, 1024, context, Errors, output); + } + width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + return formatBytes(value, width, 1000, context, Errors, output); + } + } + + const T = @typeOf(value); + switch (@typeId(T)) { + builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), + builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), + builtin.TypeId.ComptimeInt => { + const Int = math.IntFittingRange(value, value); + return formatIntValue(Int(value), fmt, context, Errors, output); + }, + else => comptime unreachable, + } +} + +pub fn formatIntValue( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + comptime var radix = 10; + comptime var uppercase = false; + comptime var width = 0; + if (fmt.len > 0) { + switch (fmt[0]) { + 'c' => { + if (@typeOf(value).bit_count <= 8) { + if (fmt.len > 1) + @compileError("Unknown format character: " ++ []u8{fmt[1]}); + return formatAsciiChar(u8(value), context, Errors, output); + } + }, + 'b' => { + radix = 2; + uppercase = false; + width = 0; + }, + 'd' => { + radix = 10; + uppercase = false; + width = 0; + }, + 'x' => { + radix = 16; + uppercase = false; + width = 0; + }, + 'X' => { + radix = 16; + uppercase = true; + width = 0; + }, + else => @compileError("Unknown format character: " ++ []u8{fmt[0]}), + } + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + return formatInt(value, radix, uppercase, width, context, Errors, output); +} + +fn formatFloatValue( + value: var, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + comptime var width: ?usize = null; + comptime var float_fmt = 'e'; + if (fmt.len > 0) { + float_fmt = fmt[0]; + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + } + + switch (float_fmt) { + 'e' => try formatFloatScientific(value, width, context, Errors, output), + '.' => try formatFloatDecimal(value, width, context, Errors, output), + else => @compileError("Unknown format character: " ++ []u8{float_fmt}), + } +} + +pub fn formatText( + bytes: []const u8, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + if (fmt.len > 0) { + if (fmt[0] == 's') { + comptime var width = 0; + if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); + return formatBuf(bytes, width, context, Errors, output); + } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { + for (bytes) |c| { + try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); + } + return; + } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); + } + return output(context, bytes); +} + +pub fn formatAsciiChar( + c: u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + return output(context, (*[1]u8)(&c)[0..]); +} + +pub fn formatBuf( + buf: []const u8, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + try output(context, buf); + + var leftover_padding = if (width > buf.len) (width - buf.len) else return; + const pad_byte: u8 = ' '; + while (leftover_padding > 0) : (leftover_padding -= 1) { + try output(context, (*[1]u8)(&pad_byte)[0..1]); + } +} + +// Print a float in scientific notation to the specified precision. Null uses full precision. +// It should be the case that every full precision, printed value can be re-parsed back to the +// same type unambiguously. +pub fn formatFloatScientific( + value: var, + maybe_precision: ?usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + var x = @floatCast(f64, value); + + // Errol doesn't handle these special cases. + if (math.signbit(x)) { + try output(context, "-"); + x = -x; + } + + if (math.isNan(x)) { + return output(context, "nan"); + } + if (math.isPositiveInf(x)) { + return output(context, "inf"); + } + if (x == 0.0) { + try output(context, "0"); + + if (maybe_precision) |precision| { + if (precision != 0) { + try output(context, "."); + var i: usize = 0; + while (i < precision) : (i += 1) { + try output(context, "0"); + } + } + } else { + try output(context, ".0"); + } + + try output(context, "e+00"); + return; + } + + var buffer: [32]u8 = undefined; + var float_decimal = errol.errol3(x, buffer[0..]); + + if (maybe_precision) |precision| { + errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); + + try output(context, float_decimal.digits[0..1]); + + // {e0} case prints no `.` + if (precision != 0) { + try output(context, "."); + + var printed: usize = 0; + if (float_decimal.digits.len > 1) { + const num_digits = math.min(float_decimal.digits.len, precision + 1); + try output(context, float_decimal.digits[1..num_digits]); + printed += num_digits - 1; + } + + while (printed < precision) : (printed += 1) { + try output(context, "0"); + } + } + } else { + try output(context, float_decimal.digits[0..1]); + try output(context, "."); + if (float_decimal.digits.len > 1) { + const num_digits = if (@typeOf(value) == f32) math.min(usize(9), float_decimal.digits.len) else float_decimal.digits.len; + + try output(context, float_decimal.digits[1..num_digits]); + } else { + try output(context, "0"); + } + } + + try output(context, "e"); + const exp = float_decimal.exp - 1; + + if (exp >= 0) { + try output(context, "+"); + if (exp > -10 and exp < 10) { + try output(context, "0"); + } + try formatInt(exp, 10, false, 0, context, Errors, output); + } else { + try output(context, "-"); + if (exp > -10 and exp < 10) { + try output(context, "0"); + } + try formatInt(-exp, 10, false, 0, context, Errors, output); + } +} + +// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. +// By default floats are printed at full precision (no rounding). +pub fn formatFloatDecimal( + value: var, + maybe_precision: ?usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + var x = f64(value); + + // Errol doesn't handle these special cases. + if (math.signbit(x)) { + try output(context, "-"); + x = -x; + } + + if (math.isNan(x)) { + return output(context, "nan"); + } + if (math.isPositiveInf(x)) { + return output(context, "inf"); + } + if (x == 0.0) { + try output(context, "0"); + + if (maybe_precision) |precision| { + if (precision != 0) { + try output(context, "."); + var i: usize = 0; + while (i < precision) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, ".0"); + } + } else { + try output(context, "0"); + } + + return; + } + + // non-special case, use errol3 + var buffer: [32]u8 = undefined; + var float_decimal = errol.errol3(x, buffer[0..]); + + if (maybe_precision) |precision| { + errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); + + // exp < 0 means the leading is always 0 as errol result is normalized. + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; + + // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. + var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); + + if (num_digits_whole > 0) { + // We may have to zero pad, for instance 1e4 requires zero padding. + try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + + var i = num_digits_whole_no_pad; + while (i < num_digits_whole) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, "0"); + } + + // {.0} special case doesn't want a trailing '.' + if (precision == 0) { + return; + } + + try output(context, "."); + + // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. + var printed: usize = 0; + + // Zero-fill until we reach significant digits or run out of precision. + if (float_decimal.exp <= 0) { + const zero_digit_count = @intCast(usize, -float_decimal.exp); + const zeros_to_print = math.min(zero_digit_count, precision); + + var i: usize = 0; + while (i < zeros_to_print) : (i += 1) { + try output(context, "0"); + printed += 1; + } + + if (printed >= precision) { + return; + } + } + + // Remaining fractional portion, zero-padding if insufficient. + assert(precision >= printed); + if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { + try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); + return; + } else { + try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + printed += float_decimal.digits.len - num_digits_whole_no_pad; + + while (printed < precision) : (printed += 1) { + try output(context, "0"); + } + } + } else { + // exp < 0 means the leading is always 0 as errol result is normalized. + var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; + + // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. + var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); + + if (num_digits_whole > 0) { + // We may have to zero pad, for instance 1e4 requires zero padding. + try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); + + var i = num_digits_whole_no_pad; + while (i < num_digits_whole) : (i += 1) { + try output(context, "0"); + } + } else { + try output(context, "0"); + } + + // Omit `.` if no fractional portion + if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { + return; + } + + try output(context, "."); + + // Zero-fill until we reach significant digits or run out of precision. + if (float_decimal.exp < 0) { + const zero_digit_count = @intCast(usize, -float_decimal.exp); + + var i: usize = 0; + while (i < zero_digit_count) : (i += 1) { + try output(context, "0"); + } + } + + try output(context, float_decimal.digits[num_digits_whole_no_pad..]); + } +} + +pub fn formatBytes( + value: var, + width: ?usize, + comptime radix: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + if (value == 0) { + return output(context, "0B"); + } + + const mags_si = " kMGTPEZY"; + const mags_iec = " KMGTPEZY"; + const magnitude = switch (radix) { + 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), + 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), + else => unreachable, + }; + const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); + const suffix = switch (radix) { + 1000 => mags_si[magnitude], + 1024 => mags_iec[magnitude], + else => unreachable, + }; + + try formatFloatDecimal(new_value, width, context, Errors, output); + + if (suffix == ' ') { + return output(context, "B"); + } + + const buf = switch (radix) { + 1000 => []u8{ suffix, 'B' }, + 1024 => []u8{ suffix, 'i', 'B' }, + else => unreachable, + }; + return output(context, buf); +} + +pub fn formatInt( + value: var, + base: u8, + uppercase: bool, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + if (@typeOf(value).is_signed) { + return formatIntSigned(value, base, uppercase, width, context, Errors, output); + } else { + return formatIntUnsigned(value, base, uppercase, width, context, Errors, output); + } +} + +fn formatIntSigned( + value: var, + base: u8, + uppercase: bool, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + const uint = @IntType(false, @typeOf(value).bit_count); + if (value < 0) { + const minus_sign: u8 = '-'; + try output(context, (*[1]u8)(&minus_sign)[0..]); + const new_value = @intCast(uint, -(value + 1)) + 1; + const new_width = if (width == 0) 0 else (width - 1); + return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); + } else if (width == 0) { + return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); + } else { + const plus_sign: u8 = '+'; + try output(context, (*[1]u8)(&plus_sign)[0..]); + const new_value = @intCast(uint, value); + const new_width = if (width == 0) 0 else (width - 1); + return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); + } +} + +fn formatIntUnsigned( + value: var, + base: u8, + uppercase: bool, + width: usize, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, +) Errors!void { + // max_int_digits accounts for the minus sign. when printing an unsigned + // number we don't need to do that. + var buf: [max_int_digits - 1]u8 = undefined; + const min_int_bits = comptime math.max(@typeOf(value).bit_count, @typeOf(base).bit_count); + const MinInt = @IntType(@typeOf(value).is_signed, min_int_bits); + var a: MinInt = value; + var index: usize = buf.len; + + while (true) { + const digit = a % base; + index -= 1; + buf[index] = digitToChar(@intCast(u8, digit), uppercase); + a /= base; + if (a == 0) break; + } + + const digits_buf = buf[index..]; + const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; + + if (padding > index) { + const zero_byte: u8 = '0'; + var leftover_padding = padding - index; + while (true) { + try output(context, (*[1]u8)(&zero_byte)[0..]); + leftover_padding -= 1; + if (leftover_padding == 0) break; + } + mem.set(u8, buf[0..index], '0'); + return output(context, buf); + } else { + const padded_buf = buf[index - padding ..]; + mem.set(u8, padded_buf[0..padding], '0'); + return output(context, padded_buf); + } +} + +pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { + var context = FormatIntBuf{ + .out_buf = out_buf, + .index = 0, + }; + formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable; + return context.index; +} +const FormatIntBuf = struct { + out_buf: []u8, + index: usize, +}; +fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { + mem.copy(u8, context.out_buf[context.index..], bytes); + context.index += bytes.len; +} + +pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { + if (!T.is_signed) return parseUnsigned(T, buf, radix); + if (buf.len == 0) return T(0); + if (buf[0] == '-') { + return math.negate(try parseUnsigned(T, buf[1..], radix)); + } else if (buf[0] == '+') { + return parseUnsigned(T, buf[1..], radix); + } else { + return parseUnsigned(T, buf, radix); + } +} + +test "fmt.parseInt" { + testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); + testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); + testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); + testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); + testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); + testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); + testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); +} + +const ParseUnsignedError = error{ + /// The result cannot fit in the type specified + Overflow, + + /// The input had a byte that was not a digit + InvalidCharacter, +}; + +pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { + var x: T = 0; + + for (buf) |c| { + const digit = try charToDigit(c, radix); + + if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); + x = try math.add(T, x, try math.cast(T, digit)); + } + + return x; +} + +test "fmt.parseUnsigned" { + testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); + testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); + testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); + + testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); + testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); + + testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); + + testing.expect((try parseUnsigned(u7, "1", 10)) == 1); + testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); + + testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); + testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); + + testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); + + // these numbers should fit even though the radix itself doesn't fit in the destination type + testing.expect((try parseUnsigned(u1, "0", 10)) == 0); + testing.expect((try parseUnsigned(u1, "1", 10)) == 1); + testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); + testing.expect((try parseUnsigned(u1, "001", 16)) == 1); + testing.expect((try parseUnsigned(u2, "3", 16)) == 3); + testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); +} + +pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; + +test "fmt.parseFloat" { + _ = @import("fmt/parse_float.zig"); +} + +pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { + const value = switch (c) { + '0'...'9' => c - '0', + 'A'...'Z' => c - 'A' + 10, + 'a'...'z' => c - 'a' + 10, + else => return error.InvalidCharacter, + }; + + if (value >= radix) return error.InvalidCharacter; + + return value; +} + +fn digitToChar(digit: u8, uppercase: bool) u8 { + return switch (digit) { + 0...9 => digit + '0', + 10...35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), + else => unreachable, + }; +} + +const BufPrintContext = struct { + remaining: []u8, +}; + +fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { + if (context.remaining.len < bytes.len) return error.BufferTooSmall; + mem.copy(u8, context.remaining, bytes); + context.remaining = context.remaining[bytes.len..]; +} + +pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { + var context = BufPrintContext{ .remaining = buf }; + try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); + return buf[0 .. buf.len - context.remaining.len]; +} + +pub const AllocPrintError = error{OutOfMemory}; + +pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { + var size: usize = 0; + format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; + const buf = try allocator.alloc(u8, size); + return bufPrint(buf, fmt, args) catch |err| switch (err) { + error.BufferTooSmall => unreachable, // we just counted the size above + }; +} + +fn countSize(size: *usize, bytes: []const u8) (error{}!void) { + size.* += bytes.len; +} + +test "buf print int" { + var buffer: [max_int_digits]u8 = undefined; + const buf = buffer[0..]; + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); + + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); + + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); + + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); + testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); +} + +fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { + return buf[0..formatIntBuf(buf, value, base, uppercase, width)]; +} + +test "parse u64 digit too big" { + _ = parseUnsigned(u64, "123a", 10) catch |err| { + if (err == error.InvalidCharacter) return; + unreachable; + }; + unreachable; +} + +test "parse unsigned comptime" { + comptime { + testing.expect((try parseUnsigned(usize, "2", 10)) == 2); + } +} + +test "fmt.format" { + { + const value: ?i32 = 1234; + try testFmt("optional: 1234\n", "optional: {}\n", value); + } + { + const value: ?i32 = null; + try testFmt("optional: null\n", "optional: {}\n", value); + } + { + const value: anyerror!i32 = 1234; + try testFmt("error union: 1234\n", "error union: {}\n", value); + } + { + const value: anyerror!i32 = error.InvalidChar; + try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); + } + { + const value: u3 = 0b101; + try testFmt("u3: 5\n", "u3: {}\n", value); + } + { + const value: u8 = 'a'; + try testFmt("u8: a\n", "u8: {c}\n", value); + } + { + const value: u8 = 0b1100; + try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); + } + { + var buf1: [32]u8 = undefined; + var context = BufPrintContext{ .remaining = buf1[0..] }; + try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite); + 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); + 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); + res = buf1[0 .. buf1.len - context.remaining.len]; + testing.expect(mem.eql(u8, res, "1100")); + } + { + const value: [3]u8 = "abc"; + try testFmt("array: abc\n", "array: {}\n", value); + try testFmt("array: abc\n", "array: {}\n", &value); + + var buf: [100]u8 = undefined; + try testFmt( + try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), + "array: {*}\n", + &value, + ); + } + { + const value: []const u8 = "abc"; + try testFmt("slice: abc\n", "slice: {}\n", value); + } + { + const value = @intToPtr(*i32, 0xdeadbeef); + try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); + try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); + } + { + const value = @intToPtr(fn () void, 0xdeadbeef); + try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); + } + try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); + try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); + try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); + try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); + try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); + try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); + { + const Struct = struct { + field: u8, + }; + const value = Struct{ .field = 42 }; + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value); + try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value); + } + { + const Struct = struct { + a: u0, + b: u1, + }; + const value = Struct{ .a = 0, .b = 1 }; + try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); + } + { + const Enum = enum { + One, + Two, + }; + const value = Enum.Two; + try testFmt("enum: Enum.Two\n", "enum: {}\n", value); + try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 1.34; + const result = try bufPrint(buf1[0..], "f32: {e}\n", value); + testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 12.34; + const result = try bufPrint(buf1[0..], "f32: {e}\n", value); + testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = -12.34e10; + const result = try bufPrint(buf1[0..], "f64: {e}\n", value); + testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n")); + } + { + // This fails on release due to a minor rounding difference. + // --release-fast outputs 9.999960000000001e-40 vs. the expected. + // TODO fix this, it should be the same in Debug and ReleaseFast + if (builtin.mode == builtin.Mode.Debug) { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999960e-40; + const result = try bufPrint(buf1[0..], "f64: {e}\n", value); + testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); + } + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.409706e-42; + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(814313563)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(1006632960)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n")); + } + { + // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. + // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. + var buf1: [32]u8 = undefined; + const value: f64 = @bitCast(f32, u32(1203982400)); + const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); + testing.expect(mem.eql(u8, result, "f64: nan\n")); + } + if (builtin.arch != builtin.Arch.arm) { + // negative nan is not defined by IEE 754, + // and ARM thus normalizes it to positive nan + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); + testing.expect(mem.eql(u8, result, "f64: -nan\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); + testing.expect(mem.eql(u8, result, "f64: inf\n")); + } + { + var buf1: [32]u8 = undefined; + const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); + testing.expect(mem.eql(u8, result, "f64: -inf\n")); + } + { + var buf1: [64]u8 = undefined; + const value: f64 = 1.52314e+29; + const result = try bufPrint(buf1[0..], "f64: {.}\n", value); + testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 1.1234; + const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); + testing.expect(mem.eql(u8, result, "f32: 1.1\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 1234.567; + const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); + testing.expect(mem.eql(u8, result, "f32: 1234.57\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = -11.1234; + const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); + // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). + // -11.12339... is rounded back up to -11.1234 + testing.expect(mem.eql(u8, result, "f32: -11.1234\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f32 = 91.12345; + const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f32: 91.12345\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 91.12345678901235; + const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); + testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 0.0; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 5.700; + const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); + testing.expect(mem.eql(u8, result, "f64: 6\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999; + const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); + testing.expect(mem.eql(u8, result, "f64: 10.0\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.0; + const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); + testing.expect(mem.eql(u8, result, "f64: 1.000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 0.0003; + const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.00030000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 1.40130e-45; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = 9.999960e-40; + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); + } + // libc checks + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(916964781))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(925353389))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1036831278))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.10000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1065353133))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 1.00000\n")); + } + { + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1092616192))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 10.00000\n")); + } + // libc differences + { + var buf1: [32]u8 = undefined; + // This is 0.015625 exactly according to gdb. We thus round down, + // however glibc rounds up for some reason. This occurs for all + // floats of the form x.yyyy25 on a precision point. + const value: f64 = f64(@bitCast(f32, u32(1015021568))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); + } + // std-windows-x86_64-Debug-bare test case fails + { + // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 + // also rounds to 630 so I'm inclined to believe libc is not + // optimal here. + var buf1: [32]u8 = undefined; + const value: f64 = f64(@bitCast(f32, u32(1518338049))); + const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); + testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); + } + //custom type format + { + const Vec2 = struct { + const SelfType = @This(); + x: f32, + y: f32, + + pub fn format( + self: SelfType, + comptime fmt: []const u8, + context: var, + comptime Errors: type, + output: fn (@typeOf(context), []const u8) Errors!void, + ) Errors!void { + switch (fmt.len) { + 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), + 1 => switch (fmt[0]) { + //point format + 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), + //dimension format + 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), + else => unreachable, + }, + else => unreachable, + } + } + }; + + var buf1: [32]u8 = undefined; + var value = Vec2{ + .x = 10.2, + .y = 2.22, + }; + try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); + + // same thing but not passing a pointer + try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); + try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); + } + //struct format + { + const S = struct { + a: u32, + b: anyerror, + }; + + const inst = S{ + .a = 456, + .b = error.Unused, + }; + + try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); + } + //union format + { + const TU = union(enum) { + float: f32, + int: u32, + }; + + const UU = union { + float: f32, + int: u32, + }; + + const EU = extern union { + float: f32, + int: u32, + }; + + const tu_inst = TU{ .int = 123 }; + const uu_inst = UU{ .int = 456 }; + const eu_inst = EU{ .float = 321.123 }; + + try testFmt("TU{ .int = 123 }", "{}", tu_inst); + + var buf: [100]u8 = undefined; + const uu_result = try bufPrint(buf[0..], "{}", uu_inst); + testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); + + const eu_result = try bufPrint(buf[0..], "{}", eu_inst); + testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); + } + //enum format + { + const E = enum { + One, + Two, + Three, + }; + + const inst = E.Two; + + try testFmt("E.Two", "{}", inst); + } + //print bytes as hex + { + const some_bytes = "\xCA\xFE\xBA\xBE"; + try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); + try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); + //Test Slices + try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); + try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); + const bytes_with_zeros = "\x00\x0E\xBA\xBE"; + try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); + } +} + +fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { + var buf: [100]u8 = undefined; + const result = try bufPrint(buf[0..], template, args); + if (mem.eql(u8, result, expected)) return; + + std.debug.warn("\n====== expected this output: =========\n"); + std.debug.warn("{}", expected); + std.debug.warn("\n======== instead found this: =========\n"); + std.debug.warn("{}", result); + std.debug.warn("\n======================================\n"); + return error.TestFailed; +} + +pub fn trim(buf: []const u8) []const u8 { + var start: usize = 0; + while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} + + var end: usize = buf.len; + while (true) { + if (end > start) { + const new_end = end - 1; + if (isWhiteSpace(buf[new_end])) { + end = new_end; + continue; + } + } + break; + } + return buf[start..end]; +} + +test "fmt.trim" { + testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); + testing.expect(mem.eql(u8, "", trim(" "))); + testing.expect(mem.eql(u8, "", trim(""))); + testing.expect(mem.eql(u8, "abc", trim(" abc"))); + testing.expect(mem.eql(u8, "abc", trim("abc "))); +} + +pub fn isWhiteSpace(byte: u8) bool { + return switch (byte) { + ' ', '\t', '\n', '\r' => true, + else => false, + }; +} + +pub fn hexToBytes(out: []u8, input: []const u8) !void { + if (out.len * 2 < input.len) + return error.InvalidLength; + + var in_i: usize = 0; + while (in_i != input.len) : (in_i += 2) { + const hi = try charToDigit(input[in_i], 16); + const lo = try charToDigit(input[in_i + 1], 16); + out[in_i / 2] = (hi << 4) | lo; + } +} + +test "fmt.hexToBytes" { + const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; + var pb: [32]u8 = undefined; + try hexToBytes(pb[0..], test_hex_str); + try testFmt(test_hex_str, "{X}", pb); +} diff --git a/std/fmt/errol.zig b/std/fmt/errol.zig new file mode 100644 index 0000000000..9304c54874 --- /dev/null +++ b/std/fmt/errol.zig @@ -0,0 +1,704 @@ +const std = @import("../std.zig"); +const enum3 = @import("errol/enum3.zig").enum3; +const enum3_data = @import("errol/enum3.zig").enum3_data; +const lookup_table = @import("errol/lookup.zig").lookup_table; +const HP = @import("errol/lookup.zig").HP; +const math = std.math; +const mem = std.mem; +const assert = std.debug.assert; + +pub const FloatDecimal = struct { + digits: []u8, + exp: i32, +}; + +pub const RoundMode = enum { + // Round only the fractional portion (e.g. 1234.23 has precision 2) + Decimal, + // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5) + Scientific, +}; + +/// Round a FloatDecimal as returned by errol3 to the specified fractional precision. +/// All digits after the specified precision should be considered invalid. +pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: RoundMode) void { + // The round digit refers to the index which we should look at to determine + // whether we need to round to match the specified precision. + var round_digit: usize = 0; + + switch (mode) { + RoundMode.Decimal => { + if (float_decimal.exp >= 0) { + round_digit = precision + @intCast(usize, float_decimal.exp); + } else { + // if a small negative exp, then adjust we need to offset by the number + // of leading zeros that will occur. + const min_exp_required = @intCast(usize, -float_decimal.exp); + if (precision > min_exp_required) { + round_digit = precision - min_exp_required; + } + } + }, + RoundMode.Scientific => { + round_digit = 1 + precision; + }, + } + + // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05 + // first, and then to 0.1 in the case of a {.1} single precision. + + // Find the digit which will signify the round point and start rounding backwards. + if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) { + assert(round_digit >= 0); + + var i = round_digit; + while (true) { + if (i == 0) { + // Rounded all the way past the start. This was of the form 9.999... + // Slot the new digit in place and increase the exponent. + float_decimal.exp += 1; + + // Re-size the buffer to use the reserved leading byte. + const one_before = @intToPtr([*]u8, @ptrToInt(&float_decimal.digits[0]) - 1); + float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; + float_decimal.digits[0] = '1'; + return; + } + + i -= 1; + + const new_value = (float_decimal.digits[i] - '0' + 1) % 10; + float_decimal.digits[i] = new_value + '0'; + + // must continue rounding until non-9 + if (new_value != 0) { + return; + } + } + } +} + +/// Corrected Errol3 double to ASCII conversion. +pub fn errol3(value: f64, buffer: []u8) FloatDecimal { + const bits = @bitCast(u64, value); + const i = tableLowerBound(bits); + if (i < enum3.len and enum3[i] == bits) { + const data = enum3_data[i]; + const digits = buffer[1 .. data.str.len + 1]; + mem.copy(u8, digits, data.str); + return FloatDecimal{ + .digits = digits, + .exp = data.exp, + }; + } + + return errol3u(value, buffer); +} + +/// Uncorrected Errol3 double to ASCII conversion. +fn errol3u(val: f64, buffer: []u8) FloatDecimal { + // check if in integer or fixed range + if (val > 9.007199254740992e15 and val < 3.40282366920938e+38) { + return errolInt(val, buffer); + } else if (val >= 16.0 and val < 9.007199254740992e15) { + return errolFixed(val, buffer); + } + + // normalize the midpoint + + const e = math.frexp(val).exponent; + var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); + if (exp < 20) { + exp = 20; + } else if (@intCast(usize, exp) >= lookup_table.len) { + exp = @intCast(i16, lookup_table.len - 1); + } + + var mid = lookup_table[@intCast(usize, exp)]; + mid = hpProd(mid, val); + const lten = lookup_table[@intCast(usize, exp)].val; + + exp -= 307; + + var ten: f64 = 1.0; + + while (mid.val > 10.0 or (mid.val == 10.0 and mid.off >= 0.0)) { + exp += 1; + hpDiv10(&mid); + ten /= 10.0; + } + + while (mid.val < 1.0 or (mid.val == 1.0 and mid.off < 0.0)) { + exp -= 1; + hpMul10(&mid); + ten *= 10.0; + } + + // compute boundaries + var high = HP{ + .val = mid.val, + .off = mid.off + (fpnext(val) - val) * lten * ten / 2.0, + }; + var low = HP{ + .val = mid.val, + .off = mid.off + (fpprev(val) - val) * lten * ten / 2.0, + }; + + hpNormalize(&high); + hpNormalize(&low); + + // normalized boundaries + + while (high.val > 10.0 or (high.val == 10.0 and high.off >= 0.0)) { + exp += 1; + hpDiv10(&high); + hpDiv10(&low); + } + + while (high.val < 1.0 or (high.val == 1.0 and high.off < 0.0)) { + exp -= 1; + hpMul10(&high); + hpMul10(&low); + } + + // digit generation + + // We generate digits starting at index 1. If rounding a buffer later then it may be + // required to generate a preceding digit in some cases (9.999) in which case we use + // the 0-index for this extra digit. + var buf_index: usize = 1; + while (true) { + var hdig = @floatToInt(u8, math.floor(high.val)); + if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; + + var ldig = @floatToInt(u8, math.floor(low.val)); + if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; + + if (ldig != hdig) break; + + buffer[buf_index] = hdig + '0'; + buf_index += 1; + high.val -= @intToFloat(f64, hdig); + low.val -= @intToFloat(f64, ldig); + hpMul10(&high); + hpMul10(&low); + } + + const tmp = (high.val + low.val) / 2.0; + var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); + if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; + + buffer[buf_index] = mdig + '0'; + buf_index += 1; + + return FloatDecimal{ + .digits = buffer[1..buf_index], + .exp = exp, + }; +} + +fn tableLowerBound(k: u64) usize { + var i = enum3.len; + var j: usize = 0; + + while (j < enum3.len) { + if (enum3[j] < k) { + j = 2 * j + 2; + } else { + i = j; + j = 2 * j + 1; + } + } + + return i; +} + +/// Compute the product of an HP number and a double. +/// @in: The HP number. +/// @val: The double. +/// &returns: The HP number. +fn hpProd(in: HP, val: f64) HP { + var hi: f64 = undefined; + var lo: f64 = undefined; + split(in.val, &hi, &lo); + + var hi2: f64 = undefined; + var lo2: f64 = undefined; + split(val, &hi2, &lo2); + + const p = in.val * val; + const e = ((hi * hi2 - p) + lo * hi2 + hi * lo2) + lo * lo2; + + return HP{ + .val = p, + .off = in.off * val + e, + }; +} + +/// Split a double into two halves. +/// @val: The double. +/// @hi: The high bits. +/// @lo: The low bits. +fn split(val: f64, hi: *f64, lo: *f64) void { + hi.* = gethi(val); + lo.* = val - hi.*; +} + +fn gethi(in: f64) f64 { + const bits = @bitCast(u64, in); + const new_bits = bits & 0xFFFFFFFFF8000000; + return @bitCast(f64, new_bits); +} + +/// Normalize the number by factoring in the error. +/// @hp: The float pair. +fn hpNormalize(hp: *HP) void { + const val = hp.val; + hp.val += hp.off; + hp.off += val - hp.val; +} + +/// Divide the high-precision number by ten. +/// @hp: The high-precision number +fn hpDiv10(hp: *HP) void { + var val = hp.val; + + hp.val /= 10.0; + hp.off /= 10.0; + + val -= hp.val * 8.0; + val -= hp.val * 2.0; + + hp.off += val / 10.0; + + hpNormalize(hp); +} + +/// Multiply the high-precision number by ten. +/// @hp: The high-precision number +fn hpMul10(hp: *HP) void { + const val = hp.val; + + hp.val *= 10.0; + hp.off *= 10.0; + + var off = hp.val; + off -= val * 8.0; + off -= val * 2.0; + + hp.off -= off; + + hpNormalize(hp); +} + +/// Integer conversion algorithm, guaranteed correct, optimal, and best. +/// @val: The val. +/// @buf: The output buffer. +/// &return: The exponent. +fn errolInt(val: f64, buffer: []u8) FloatDecimal { + const pow19 = u128(1e19); + + assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); + + var mid = @floatToInt(u128, val); + var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); + var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); + + if (@bitCast(u64, val) & 0x1 != 0) { + high -= 1; + } else { + low -= 1; + } + + var l64 = @intCast(u64, low % pow19); + const lf = @intCast(u64, (low / pow19) % pow19); + + var h64 = @intCast(u64, high % pow19); + const hf = @intCast(u64, (high / pow19) % pow19); + + if (lf != hf) { + l64 = lf; + h64 = hf; + mid = mid / (pow19 / 10); + } + + var mi: i32 = mismatch10(l64, h64); + var x: u64 = 1; + { + var i: i32 = @boolToInt(lf == hf); + while (i < mi) : (i += 1) { + x *= 10; + } + } + const m64 = @truncate(u64, @divTrunc(mid, x)); + + if (lf != hf) mi += 19; + + var buf_index = u64toa(m64, buffer) - 1; + + if (mi != 0) { + buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); + } else { + buf_index += 1; + } + + return FloatDecimal{ + .digits = buffer[0..buf_index], + .exp = @intCast(i32, buf_index) + mi, + }; +} + +/// Fixed point conversion algorithm, guaranteed correct, optimal, and best. +/// @val: The val. +/// @buf: The output buffer. +/// &return: The exponent. +fn errolFixed(val: f64, buffer: []u8) FloatDecimal { + assert((val >= 16.0) and (val < 9.007199254740992e15)); + + const u = @floatToInt(u64, val); + const n = @intToFloat(f64, u); + + var mid = val - n; + var lo = ((fpprev(val) - n) + mid) / 2.0; + var hi = ((fpnext(val) - n) + mid) / 2.0; + + var buf_index = u64toa(u, buffer); + var exp = @intCast(i32, buf_index); + var j = buf_index; + buffer[j] = 0; + + if (mid != 0.0) { + while (mid != 0.0) { + lo *= 10.0; + const ldig = @floatToInt(i32, lo); + lo -= @intToFloat(f64, ldig); + + mid *= 10.0; + const mdig = @floatToInt(i32, mid); + mid -= @intToFloat(f64, mdig); + + hi *= 10.0; + const hdig = @floatToInt(i32, hi); + hi -= @intToFloat(f64, hdig); + + buffer[j] = @intCast(u8, mdig + '0'); + j += 1; + + if (hdig != ldig or j > 50) break; + } + + if (mid > 0.5) { + buffer[j - 1] += 1; + } else if ((mid == 0.5) and (buffer[j - 1] & 0x1) != 0) { + buffer[j - 1] += 1; + } + } else { + while (buffer[j - 1] == '0') { + buffer[j - 1] = 0; + j -= 1; + } + } + + buffer[j] = 0; + + return FloatDecimal{ + .digits = buffer[0..j], + .exp = exp, + }; +} + +fn fpnext(val: f64) f64 { + return @bitCast(f64, @bitCast(u64, val) +% 1); +} + +fn fpprev(val: f64) f64 { + return @bitCast(f64, @bitCast(u64, val) -% 1); +} + +pub const c_digits_lut = []u8{ + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', + '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', + '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', + '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', + '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', + '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', + '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', + '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', + '5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', + '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', + '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', + '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', + '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', + '9', '8', '9', '9', +}; + +fn u64toa(value_param: u64, buffer: []u8) usize { + var value = value_param; + const kTen8: u64 = 100000000; + const kTen9: u64 = kTen8 * 10; + const kTen10: u64 = kTen8 * 100; + const kTen11: u64 = kTen8 * 1000; + const kTen12: u64 = kTen8 * 10000; + const kTen13: u64 = kTen8 * 100000; + const kTen14: u64 = kTen8 * 1000000; + const kTen15: u64 = kTen8 * 10000000; + const kTen16: u64 = kTen8 * kTen8; + + var buf_index: usize = 0; + + if (value < kTen8) { + const v = @intCast(u32, value); + if (v < 10000) { + const d1: u32 = (v / 100) << 1; + const d2: u32 = (v % 100) << 1; + + if (v >= 1000) { + buffer[buf_index] = c_digits_lut[d1]; + buf_index += 1; + } + if (v >= 100) { + buffer[buf_index] = c_digits_lut[d1 + 1]; + buf_index += 1; + } + if (v >= 10) { + buffer[buf_index] = c_digits_lut[d2]; + buf_index += 1; + } + buffer[buf_index] = c_digits_lut[d2 + 1]; + buf_index += 1; + } else { + // value = bbbbcccc + const b: u32 = v / 10000; + const c: u32 = v % 10000; + + const d1: u32 = (b / 100) << 1; + const d2: u32 = (b % 100) << 1; + + const d3: u32 = (c / 100) << 1; + const d4: u32 = (c % 100) << 1; + + if (value >= 10000000) { + buffer[buf_index] = c_digits_lut[d1]; + buf_index += 1; + } + if (value >= 1000000) { + buffer[buf_index] = c_digits_lut[d1 + 1]; + buf_index += 1; + } + if (value >= 100000) { + buffer[buf_index] = c_digits_lut[d2]; + buf_index += 1; + } + buffer[buf_index] = c_digits_lut[d2 + 1]; + buf_index += 1; + + buffer[buf_index] = c_digits_lut[d3]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d3 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d4]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d4 + 1]; + buf_index += 1; + } + } else if (value < kTen16) { + const v0: u32 = @intCast(u32, value / kTen8); + const v1: u32 = @intCast(u32, value % kTen8); + + const b0: u32 = v0 / 10000; + const c0: u32 = v0 % 10000; + + const d1: u32 = (b0 / 100) << 1; + const d2: u32 = (b0 % 100) << 1; + + const d3: u32 = (c0 / 100) << 1; + const d4: u32 = (c0 % 100) << 1; + + const b1: u32 = v1 / 10000; + const c1: u32 = v1 % 10000; + + const d5: u32 = (b1 / 100) << 1; + const d6: u32 = (b1 % 100) << 1; + + const d7: u32 = (c1 / 100) << 1; + const d8: u32 = (c1 % 100) << 1; + + if (value >= kTen15) { + buffer[buf_index] = c_digits_lut[d1]; + buf_index += 1; + } + if (value >= kTen14) { + buffer[buf_index] = c_digits_lut[d1 + 1]; + buf_index += 1; + } + if (value >= kTen13) { + buffer[buf_index] = c_digits_lut[d2]; + buf_index += 1; + } + if (value >= kTen12) { + buffer[buf_index] = c_digits_lut[d2 + 1]; + buf_index += 1; + } + if (value >= kTen11) { + buffer[buf_index] = c_digits_lut[d3]; + buf_index += 1; + } + if (value >= kTen10) { + buffer[buf_index] = c_digits_lut[d3 + 1]; + buf_index += 1; + } + if (value >= kTen9) { + buffer[buf_index] = c_digits_lut[d4]; + buf_index += 1; + } + if (value >= kTen8) { + buffer[buf_index] = c_digits_lut[d4 + 1]; + buf_index += 1; + } + + buffer[buf_index] = c_digits_lut[d5]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d5 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d6]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d6 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d7]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d7 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d8]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d8 + 1]; + buf_index += 1; + } else { + const a = @intCast(u32, value / kTen16); // 1 to 1844 + value %= kTen16; + + if (a < 10) { + buffer[buf_index] = '0' + @intCast(u8, a); + buf_index += 1; + } else if (a < 100) { + const i: u32 = a << 1; + buffer[buf_index] = c_digits_lut[i]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[i + 1]; + buf_index += 1; + } else if (a < 1000) { + buffer[buf_index] = '0' + @intCast(u8, a / 100); + buf_index += 1; + + const i: u32 = (a % 100) << 1; + buffer[buf_index] = c_digits_lut[i]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[i + 1]; + buf_index += 1; + } else { + const i: u32 = (a / 100) << 1; + const j: u32 = (a % 100) << 1; + buffer[buf_index] = c_digits_lut[i]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[i + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[j]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[j + 1]; + buf_index += 1; + } + + const v0 = @intCast(u32, value / kTen8); + const v1 = @intCast(u32, value % kTen8); + + const b0: u32 = v0 / 10000; + const c0: u32 = v0 % 10000; + + const d1: u32 = (b0 / 100) << 1; + const d2: u32 = (b0 % 100) << 1; + + const d3: u32 = (c0 / 100) << 1; + const d4: u32 = (c0 % 100) << 1; + + const b1: u32 = v1 / 10000; + const c1: u32 = v1 % 10000; + + const d5: u32 = (b1 / 100) << 1; + const d6: u32 = (b1 % 100) << 1; + + const d7: u32 = (c1 / 100) << 1; + const d8: u32 = (c1 % 100) << 1; + + buffer[buf_index] = c_digits_lut[d1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d1 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d2]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d2 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d3]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d3 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d4]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d4 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d5]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d5 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d6]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d6 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d7]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d7 + 1]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d8]; + buf_index += 1; + buffer[buf_index] = c_digits_lut[d8 + 1]; + buf_index += 1; + } + + return buf_index; +} + +fn fpeint(from: f64) u128 { + const bits = @bitCast(u64, from); + assert((bits & ((1 << 52) - 1)) == 0); + + return u128(1) << @truncate(u7, (bits >> 52) -% 1023); +} + +/// Given two different integers with the same length in terms of the number +/// of decimal digits, index the digits from the right-most position starting +/// from zero, find the first index where the digits in the two integers +/// divergent starting from the highest index. +/// @a: Integer a. +/// @b: Integer b. +/// &returns: An index within [0, 19). +fn mismatch10(a: u64, b: u64) i32 { + const pow10 = 10000000000; + const af = a / pow10; + const bf = b / pow10; + + var i: i32 = 0; + var a_copy = a; + var b_copy = b; + + if (af != bf) { + i = 10; + a_copy = af; + b_copy = bf; + } + + while (true) : (i += 1) { + a_copy /= 10; + b_copy /= 10; + + if (a_copy == b_copy) return i; + } +} diff --git a/std/fmt/errol/index.zig b/std/fmt/errol/index.zig deleted file mode 100644 index 0fdb1c4ab8..0000000000 --- a/std/fmt/errol/index.zig +++ /dev/null @@ -1,704 +0,0 @@ -const std = @import("../../index.zig"); -const enum3 = @import("enum3.zig").enum3; -const enum3_data = @import("enum3.zig").enum3_data; -const lookup_table = @import("lookup.zig").lookup_table; -const HP = @import("lookup.zig").HP; -const math = std.math; -const mem = std.mem; -const assert = std.debug.assert; - -pub const FloatDecimal = struct { - digits: []u8, - exp: i32, -}; - -pub const RoundMode = enum { - // Round only the fractional portion (e.g. 1234.23 has precision 2) - Decimal, - // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5) - Scientific, -}; - -/// Round a FloatDecimal as returned by errol3 to the specified fractional precision. -/// All digits after the specified precision should be considered invalid. -pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: RoundMode) void { - // The round digit refers to the index which we should look at to determine - // whether we need to round to match the specified precision. - var round_digit: usize = 0; - - switch (mode) { - RoundMode.Decimal => { - if (float_decimal.exp >= 0) { - round_digit = precision + @intCast(usize, float_decimal.exp); - } else { - // if a small negative exp, then adjust we need to offset by the number - // of leading zeros that will occur. - const min_exp_required = @intCast(usize, -float_decimal.exp); - if (precision > min_exp_required) { - round_digit = precision - min_exp_required; - } - } - }, - RoundMode.Scientific => { - round_digit = 1 + precision; - }, - } - - // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05 - // first, and then to 0.1 in the case of a {.1} single precision. - - // Find the digit which will signify the round point and start rounding backwards. - if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) { - assert(round_digit >= 0); - - var i = round_digit; - while (true) { - if (i == 0) { - // Rounded all the way past the start. This was of the form 9.999... - // Slot the new digit in place and increase the exponent. - float_decimal.exp += 1; - - // Re-size the buffer to use the reserved leading byte. - const one_before = @intToPtr([*]u8, @ptrToInt(&float_decimal.digits[0]) - 1); - float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; - float_decimal.digits[0] = '1'; - return; - } - - i -= 1; - - const new_value = (float_decimal.digits[i] - '0' + 1) % 10; - float_decimal.digits[i] = new_value + '0'; - - // must continue rounding until non-9 - if (new_value != 0) { - return; - } - } - } -} - -/// Corrected Errol3 double to ASCII conversion. -pub fn errol3(value: f64, buffer: []u8) FloatDecimal { - const bits = @bitCast(u64, value); - const i = tableLowerBound(bits); - if (i < enum3.len and enum3[i] == bits) { - const data = enum3_data[i]; - const digits = buffer[1 .. data.str.len + 1]; - mem.copy(u8, digits, data.str); - return FloatDecimal{ - .digits = digits, - .exp = data.exp, - }; - } - - return errol3u(value, buffer); -} - -/// Uncorrected Errol3 double to ASCII conversion. -fn errol3u(val: f64, buffer: []u8) FloatDecimal { - // check if in integer or fixed range - if (val > 9.007199254740992e15 and val < 3.40282366920938e+38) { - return errolInt(val, buffer); - } else if (val >= 16.0 and val < 9.007199254740992e15) { - return errolFixed(val, buffer); - } - - // normalize the midpoint - - const e = math.frexp(val).exponent; - var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); - if (exp < 20) { - exp = 20; - } else if (@intCast(usize, exp) >= lookup_table.len) { - exp = @intCast(i16, lookup_table.len - 1); - } - - var mid = lookup_table[@intCast(usize, exp)]; - mid = hpProd(mid, val); - const lten = lookup_table[@intCast(usize, exp)].val; - - exp -= 307; - - var ten: f64 = 1.0; - - while (mid.val > 10.0 or (mid.val == 10.0 and mid.off >= 0.0)) { - exp += 1; - hpDiv10(&mid); - ten /= 10.0; - } - - while (mid.val < 1.0 or (mid.val == 1.0 and mid.off < 0.0)) { - exp -= 1; - hpMul10(&mid); - ten *= 10.0; - } - - // compute boundaries - var high = HP{ - .val = mid.val, - .off = mid.off + (fpnext(val) - val) * lten * ten / 2.0, - }; - var low = HP{ - .val = mid.val, - .off = mid.off + (fpprev(val) - val) * lten * ten / 2.0, - }; - - hpNormalize(&high); - hpNormalize(&low); - - // normalized boundaries - - while (high.val > 10.0 or (high.val == 10.0 and high.off >= 0.0)) { - exp += 1; - hpDiv10(&high); - hpDiv10(&low); - } - - while (high.val < 1.0 or (high.val == 1.0 and high.off < 0.0)) { - exp -= 1; - hpMul10(&high); - hpMul10(&low); - } - - // digit generation - - // We generate digits starting at index 1. If rounding a buffer later then it may be - // required to generate a preceding digit in some cases (9.999) in which case we use - // the 0-index for this extra digit. - var buf_index: usize = 1; - while (true) { - var hdig = @floatToInt(u8, math.floor(high.val)); - if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; - - var ldig = @floatToInt(u8, math.floor(low.val)); - if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; - - if (ldig != hdig) break; - - buffer[buf_index] = hdig + '0'; - buf_index += 1; - high.val -= @intToFloat(f64, hdig); - low.val -= @intToFloat(f64, ldig); - hpMul10(&high); - hpMul10(&low); - } - - const tmp = (high.val + low.val) / 2.0; - var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); - if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; - - buffer[buf_index] = mdig + '0'; - buf_index += 1; - - return FloatDecimal{ - .digits = buffer[1..buf_index], - .exp = exp, - }; -} - -fn tableLowerBound(k: u64) usize { - var i = enum3.len; - var j: usize = 0; - - while (j < enum3.len) { - if (enum3[j] < k) { - j = 2 * j + 2; - } else { - i = j; - j = 2 * j + 1; - } - } - - return i; -} - -/// Compute the product of an HP number and a double. -/// @in: The HP number. -/// @val: The double. -/// &returns: The HP number. -fn hpProd(in: HP, val: f64) HP { - var hi: f64 = undefined; - var lo: f64 = undefined; - split(in.val, &hi, &lo); - - var hi2: f64 = undefined; - var lo2: f64 = undefined; - split(val, &hi2, &lo2); - - const p = in.val * val; - const e = ((hi * hi2 - p) + lo * hi2 + hi * lo2) + lo * lo2; - - return HP{ - .val = p, - .off = in.off * val + e, - }; -} - -/// Split a double into two halves. -/// @val: The double. -/// @hi: The high bits. -/// @lo: The low bits. -fn split(val: f64, hi: *f64, lo: *f64) void { - hi.* = gethi(val); - lo.* = val - hi.*; -} - -fn gethi(in: f64) f64 { - const bits = @bitCast(u64, in); - const new_bits = bits & 0xFFFFFFFFF8000000; - return @bitCast(f64, new_bits); -} - -/// Normalize the number by factoring in the error. -/// @hp: The float pair. -fn hpNormalize(hp: *HP) void { - const val = hp.val; - hp.val += hp.off; - hp.off += val - hp.val; -} - -/// Divide the high-precision number by ten. -/// @hp: The high-precision number -fn hpDiv10(hp: *HP) void { - var val = hp.val; - - hp.val /= 10.0; - hp.off /= 10.0; - - val -= hp.val * 8.0; - val -= hp.val * 2.0; - - hp.off += val / 10.0; - - hpNormalize(hp); -} - -/// Multiply the high-precision number by ten. -/// @hp: The high-precision number -fn hpMul10(hp: *HP) void { - const val = hp.val; - - hp.val *= 10.0; - hp.off *= 10.0; - - var off = hp.val; - off -= val * 8.0; - off -= val * 2.0; - - hp.off -= off; - - hpNormalize(hp); -} - -/// Integer conversion algorithm, guaranteed correct, optimal, and best. -/// @val: The val. -/// @buf: The output buffer. -/// &return: The exponent. -fn errolInt(val: f64, buffer: []u8) FloatDecimal { - const pow19 = u128(1e19); - - assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); - - var mid = @floatToInt(u128, val); - var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); - var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); - - if (@bitCast(u64, val) & 0x1 != 0) { - high -= 1; - } else { - low -= 1; - } - - var l64 = @intCast(u64, low % pow19); - const lf = @intCast(u64, (low / pow19) % pow19); - - var h64 = @intCast(u64, high % pow19); - const hf = @intCast(u64, (high / pow19) % pow19); - - if (lf != hf) { - l64 = lf; - h64 = hf; - mid = mid / (pow19 / 10); - } - - var mi: i32 = mismatch10(l64, h64); - var x: u64 = 1; - { - var i: i32 = @boolToInt(lf == hf); - while (i < mi) : (i += 1) { - x *= 10; - } - } - const m64 = @truncate(u64, @divTrunc(mid, x)); - - if (lf != hf) mi += 19; - - var buf_index = u64toa(m64, buffer) - 1; - - if (mi != 0) { - buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); - } else { - buf_index += 1; - } - - return FloatDecimal{ - .digits = buffer[0..buf_index], - .exp = @intCast(i32, buf_index) + mi, - }; -} - -/// Fixed point conversion algorithm, guaranteed correct, optimal, and best. -/// @val: The val. -/// @buf: The output buffer. -/// &return: The exponent. -fn errolFixed(val: f64, buffer: []u8) FloatDecimal { - assert((val >= 16.0) and (val < 9.007199254740992e15)); - - const u = @floatToInt(u64, val); - const n = @intToFloat(f64, u); - - var mid = val - n; - var lo = ((fpprev(val) - n) + mid) / 2.0; - var hi = ((fpnext(val) - n) + mid) / 2.0; - - var buf_index = u64toa(u, buffer); - var exp = @intCast(i32, buf_index); - var j = buf_index; - buffer[j] = 0; - - if (mid != 0.0) { - while (mid != 0.0) { - lo *= 10.0; - const ldig = @floatToInt(i32, lo); - lo -= @intToFloat(f64, ldig); - - mid *= 10.0; - const mdig = @floatToInt(i32, mid); - mid -= @intToFloat(f64, mdig); - - hi *= 10.0; - const hdig = @floatToInt(i32, hi); - hi -= @intToFloat(f64, hdig); - - buffer[j] = @intCast(u8, mdig + '0'); - j += 1; - - if (hdig != ldig or j > 50) break; - } - - if (mid > 0.5) { - buffer[j - 1] += 1; - } else if ((mid == 0.5) and (buffer[j - 1] & 0x1) != 0) { - buffer[j - 1] += 1; - } - } else { - while (buffer[j - 1] == '0') { - buffer[j - 1] = 0; - j -= 1; - } - } - - buffer[j] = 0; - - return FloatDecimal{ - .digits = buffer[0..j], - .exp = exp, - }; -} - -fn fpnext(val: f64) f64 { - return @bitCast(f64, @bitCast(u64, val) +% 1); -} - -fn fpprev(val: f64) f64 { - return @bitCast(f64, @bitCast(u64, val) -% 1); -} - -pub const c_digits_lut = []u8{ - '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', - '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', - '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', - '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', - '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', - '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', - '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', - '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', - '5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', - '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', - '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', - '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', - '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', - '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', - '9', '8', '9', '9', -}; - -fn u64toa(value_param: u64, buffer: []u8) usize { - var value = value_param; - const kTen8: u64 = 100000000; - const kTen9: u64 = kTen8 * 10; - const kTen10: u64 = kTen8 * 100; - const kTen11: u64 = kTen8 * 1000; - const kTen12: u64 = kTen8 * 10000; - const kTen13: u64 = kTen8 * 100000; - const kTen14: u64 = kTen8 * 1000000; - const kTen15: u64 = kTen8 * 10000000; - const kTen16: u64 = kTen8 * kTen8; - - var buf_index: usize = 0; - - if (value < kTen8) { - const v = @intCast(u32, value); - if (v < 10000) { - const d1: u32 = (v / 100) << 1; - const d2: u32 = (v % 100) << 1; - - if (v >= 1000) { - buffer[buf_index] = c_digits_lut[d1]; - buf_index += 1; - } - if (v >= 100) { - buffer[buf_index] = c_digits_lut[d1 + 1]; - buf_index += 1; - } - if (v >= 10) { - buffer[buf_index] = c_digits_lut[d2]; - buf_index += 1; - } - buffer[buf_index] = c_digits_lut[d2 + 1]; - buf_index += 1; - } else { - // value = bbbbcccc - const b: u32 = v / 10000; - const c: u32 = v % 10000; - - const d1: u32 = (b / 100) << 1; - const d2: u32 = (b % 100) << 1; - - const d3: u32 = (c / 100) << 1; - const d4: u32 = (c % 100) << 1; - - if (value >= 10000000) { - buffer[buf_index] = c_digits_lut[d1]; - buf_index += 1; - } - if (value >= 1000000) { - buffer[buf_index] = c_digits_lut[d1 + 1]; - buf_index += 1; - } - if (value >= 100000) { - buffer[buf_index] = c_digits_lut[d2]; - buf_index += 1; - } - buffer[buf_index] = c_digits_lut[d2 + 1]; - buf_index += 1; - - buffer[buf_index] = c_digits_lut[d3]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d3 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d4]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d4 + 1]; - buf_index += 1; - } - } else if (value < kTen16) { - const v0: u32 = @intCast(u32, value / kTen8); - const v1: u32 = @intCast(u32, value % kTen8); - - const b0: u32 = v0 / 10000; - const c0: u32 = v0 % 10000; - - const d1: u32 = (b0 / 100) << 1; - const d2: u32 = (b0 % 100) << 1; - - const d3: u32 = (c0 / 100) << 1; - const d4: u32 = (c0 % 100) << 1; - - const b1: u32 = v1 / 10000; - const c1: u32 = v1 % 10000; - - const d5: u32 = (b1 / 100) << 1; - const d6: u32 = (b1 % 100) << 1; - - const d7: u32 = (c1 / 100) << 1; - const d8: u32 = (c1 % 100) << 1; - - if (value >= kTen15) { - buffer[buf_index] = c_digits_lut[d1]; - buf_index += 1; - } - if (value >= kTen14) { - buffer[buf_index] = c_digits_lut[d1 + 1]; - buf_index += 1; - } - if (value >= kTen13) { - buffer[buf_index] = c_digits_lut[d2]; - buf_index += 1; - } - if (value >= kTen12) { - buffer[buf_index] = c_digits_lut[d2 + 1]; - buf_index += 1; - } - if (value >= kTen11) { - buffer[buf_index] = c_digits_lut[d3]; - buf_index += 1; - } - if (value >= kTen10) { - buffer[buf_index] = c_digits_lut[d3 + 1]; - buf_index += 1; - } - if (value >= kTen9) { - buffer[buf_index] = c_digits_lut[d4]; - buf_index += 1; - } - if (value >= kTen8) { - buffer[buf_index] = c_digits_lut[d4 + 1]; - buf_index += 1; - } - - buffer[buf_index] = c_digits_lut[d5]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d5 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d6]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d6 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d7]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d7 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d8]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d8 + 1]; - buf_index += 1; - } else { - const a = @intCast(u32, value / kTen16); // 1 to 1844 - value %= kTen16; - - if (a < 10) { - buffer[buf_index] = '0' + @intCast(u8, a); - buf_index += 1; - } else if (a < 100) { - const i: u32 = a << 1; - buffer[buf_index] = c_digits_lut[i]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[i + 1]; - buf_index += 1; - } else if (a < 1000) { - buffer[buf_index] = '0' + @intCast(u8, a / 100); - buf_index += 1; - - const i: u32 = (a % 100) << 1; - buffer[buf_index] = c_digits_lut[i]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[i + 1]; - buf_index += 1; - } else { - const i: u32 = (a / 100) << 1; - const j: u32 = (a % 100) << 1; - buffer[buf_index] = c_digits_lut[i]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[i + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[j]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[j + 1]; - buf_index += 1; - } - - const v0 = @intCast(u32, value / kTen8); - const v1 = @intCast(u32, value % kTen8); - - const b0: u32 = v0 / 10000; - const c0: u32 = v0 % 10000; - - const d1: u32 = (b0 / 100) << 1; - const d2: u32 = (b0 % 100) << 1; - - const d3: u32 = (c0 / 100) << 1; - const d4: u32 = (c0 % 100) << 1; - - const b1: u32 = v1 / 10000; - const c1: u32 = v1 % 10000; - - const d5: u32 = (b1 / 100) << 1; - const d6: u32 = (b1 % 100) << 1; - - const d7: u32 = (c1 / 100) << 1; - const d8: u32 = (c1 % 100) << 1; - - buffer[buf_index] = c_digits_lut[d1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d1 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d2]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d2 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d3]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d3 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d4]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d4 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d5]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d5 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d6]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d6 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d7]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d7 + 1]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d8]; - buf_index += 1; - buffer[buf_index] = c_digits_lut[d8 + 1]; - buf_index += 1; - } - - return buf_index; -} - -fn fpeint(from: f64) u128 { - const bits = @bitCast(u64, from); - assert((bits & ((1 << 52) - 1)) == 0); - - return u128(1) << @truncate(u7, (bits >> 52) -% 1023); -} - -/// Given two different integers with the same length in terms of the number -/// of decimal digits, index the digits from the right-most position starting -/// from zero, find the first index where the digits in the two integers -/// divergent starting from the highest index. -/// @a: Integer a. -/// @b: Integer b. -/// &returns: An index within [0, 19). -fn mismatch10(a: u64, b: u64) i32 { - const pow10 = 10000000000; - const af = a / pow10; - const bf = b / pow10; - - var i: i32 = 0; - var a_copy = a; - var b_copy = b; - - if (af != bf) { - i = 10; - a_copy = af; - b_copy = bf; - } - - while (true) : (i += 1) { - a_copy /= 10; - b_copy /= 10; - - if (a_copy == b_copy) return i; - } -} diff --git a/std/fmt/index.zig b/std/fmt/index.zig deleted file mode 100644 index 4f864b3662..0000000000 --- a/std/fmt/index.zig +++ /dev/null @@ -1,1441 +0,0 @@ -const std = @import("../index.zig"); -const math = std.math; -const debug = std.debug; -const assert = debug.assert; -const testing = std.testing; -const mem = std.mem; -const builtin = @import("builtin"); -const errol = @import("errol/index.zig"); -const lossyCast = std.math.lossyCast; - -const max_int_digits = 65; - -/// Renders fmt string with args, calling output with slices of bytes. -/// If `output` returns an error, the error is returned from `format` and -/// `output` is not called again. -pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { - const State = enum { - Start, - OpenBrace, - CloseBrace, - FormatString, - Pointer, - }; - - comptime var start_index = 0; - comptime var state = State.Start; - comptime var next_arg = 0; - - inline for (fmt) |c, i| { - switch (state) { - State.Start => switch (c) { - '{' => { - if (start_index < i) { - try output(context, fmt[start_index..i]); - } - start_index = i; - state = State.OpenBrace; - }, - - '}' => { - if (start_index < i) { - try output(context, fmt[start_index..i]); - } - state = State.CloseBrace; - }, - else => {}, - }, - State.OpenBrace => switch (c) { - '{' => { - state = State.Start; - start_index = i; - }, - '}' => { - try formatType(args[next_arg], fmt[0..0], context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '*' => state = State.Pointer, - else => { - state = State.FormatString; - }, - }, - State.CloseBrace => switch (c) { - '}' => { - state = State.Start; - start_index = i; - }, - else => @compileError("Single '}' encountered in format string"), - }, - State.FormatString => switch (c) { - '}' => { - const s = start_index + 1; - try formatType(args[next_arg], fmt[s..i], context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - else => {}, - }, - State.Pointer => switch (c) { - '}' => { - try output(context, @typeName(@typeOf(args[next_arg]).Child)); - try output(context, "@"); - try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - else => @compileError("Unexpected format character after '*'"), - }, - } - } - comptime { - if (args.len != next_arg) { - @compileError("Unused arguments"); - } - if (state != State.Start) { - @compileError("Incomplete format string: " ++ fmt); - } - } - if (start_index < fmt.len) { - try output(context, fmt[start_index..]); - } -} - -pub fn formatType( - value: var, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - const T = @typeOf(value); - if (T == anyerror) { - try output(context, "error."); - return output(context, @errorName(value)); - } - switch (@typeInfo(T)) { - builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { - return formatValue(value, fmt, context, Errors, output); - }, - builtin.TypeId.Void => { - return output(context, "void"); - }, - builtin.TypeId.Bool => { - return output(context, if (value) "true" else "false"); - }, - builtin.TypeId.Optional => { - if (value) |payload| { - return formatType(payload, fmt, context, Errors, output); - } else { - return output(context, "null"); - } - }, - builtin.TypeId.ErrorUnion => { - if (value) |payload| { - return formatType(payload, fmt, context, Errors, output); - } else |err| { - return formatType(err, fmt, context, Errors, output); - } - }, - builtin.TypeId.ErrorSet => { - try output(context, "error."); - return output(context, @errorName(value)); - }, - builtin.TypeId.Promise => { - return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); - }, - builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { - const has_cust_fmt = comptime cf: { - const info = @typeInfo(T); - const defs = switch (info) { - builtin.TypeId.Struct => |s| s.defs, - builtin.TypeId.Union => |u| u.defs, - builtin.TypeId.Enum => |e| e.defs, - else => unreachable, - }; - - for (defs) |def| { - if (mem.eql(u8, def.name, "format")) { - break :cf true; - } - } - break :cf false; - }; - if (has_cust_fmt) return value.format(fmt, context, Errors, output); - - try output(context, @typeName(T)); - switch (comptime @typeId(T)) { - builtin.TypeId.Enum => { - try output(context, "."); - try formatType(@tagName(value), "", context, Errors, output); - return; - }, - builtin.TypeId.Struct => { - comptime var field_i = 0; - inline while (field_i < @memberCount(T)) : (field_i += 1) { - if (field_i == 0) { - try output(context, "{ ."); - } else { - try output(context, ", ."); - } - try output(context, @memberName(T, field_i)); - try output(context, " = "); - try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output); - } - try output(context, " }"); - }, - builtin.TypeId.Union => { - const info = @typeInfo(T).Union; - if (info.tag_type) |UnionTagType| { - try output(context, "{ ."); - try output(context, @tagName(UnionTagType(value))); - try output(context, " = "); - inline for (info.fields) |u_field| { - if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { - try formatType(@field(value, u_field.name), "", context, Errors, output); - } - } - try output(context, " }"); - } else { - try format(context, Errors, output, "@{x}", @ptrToInt(&value)); - } - }, - else => unreachable, - } - return; - }, - builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { - builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { - builtin.TypeId.Array => |info| { - if (info.child == u8) { - return formatText(value, fmt, context, Errors, output); - } - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); - }, - builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { - return formatType(value.*, fmt, context, Errors, output); - }, - else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), - }, - builtin.TypeInfo.Pointer.Size.Many => { - if (ptr_info.child == u8) { - if (fmt.len > 0 and fmt[0] == 's') { - const len = std.cstr.len(value); - return formatText(value[0..len], fmt, context, Errors, output); - } - } - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); - }, - builtin.TypeInfo.Pointer.Size.Slice => { - if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { - return formatText(value, fmt, context, Errors, output); - } - const casted_value = ([]const u8)(value); - return output(context, casted_value); - }, - builtin.TypeInfo.Pointer.Size.C => { - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); - }, - }, - builtin.TypeId.Array => |info| { - if (info.child == u8) { - return formatText(value, fmt, context, Errors, output); - } - return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); - }, - builtin.TypeId.Fn => { - return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); - }, - else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), - } -} - -fn formatValue( - value: var, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - if (fmt.len > 0) { - if (fmt[0] == 'B') { - comptime var width: ?usize = null; - if (fmt.len > 1) { - if (fmt[1] == 'i') { - if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); - return formatBytes(value, width, 1024, context, Errors, output); - } - width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); - } - return formatBytes(value, width, 1000, context, Errors, output); - } - } - - const T = @typeOf(value); - switch (@typeId(T)) { - builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), - builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), - builtin.TypeId.ComptimeInt => { - const Int = math.IntFittingRange(value, value); - return formatIntValue(Int(value), fmt, context, Errors, output); - }, - else => comptime unreachable, - } -} - -pub fn formatIntValue( - value: var, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - comptime var radix = 10; - comptime var uppercase = false; - comptime var width = 0; - if (fmt.len > 0) { - switch (fmt[0]) { - 'c' => { - if (@typeOf(value).bit_count <= 8) { - if (fmt.len > 1) - @compileError("Unknown format character: " ++ []u8{fmt[1]}); - return formatAsciiChar(u8(value), context, Errors, output); - } - }, - 'b' => { - radix = 2; - uppercase = false; - width = 0; - }, - 'd' => { - radix = 10; - uppercase = false; - width = 0; - }, - 'x' => { - radix = 16; - uppercase = false; - width = 0; - }, - 'X' => { - radix = 16; - uppercase = true; - width = 0; - }, - else => @compileError("Unknown format character: " ++ []u8{fmt[0]}), - } - if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); - } - return formatInt(value, radix, uppercase, width, context, Errors, output); -} - -fn formatFloatValue( - value: var, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - comptime var width: ?usize = null; - comptime var float_fmt = 'e'; - if (fmt.len > 0) { - float_fmt = fmt[0]; - if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); - } - - switch (float_fmt) { - 'e' => try formatFloatScientific(value, width, context, Errors, output), - '.' => try formatFloatDecimal(value, width, context, Errors, output), - else => @compileError("Unknown format character: " ++ []u8{float_fmt}), - } -} - -pub fn formatText( - bytes: []const u8, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - if (fmt.len > 0) { - if (fmt[0] == 's') { - comptime var width = 0; - if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); - return formatBuf(bytes, width, context, Errors, output); - } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { - for (bytes) |c| { - try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); - } - return; - } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); - } - return output(context, bytes); -} - -pub fn formatAsciiChar( - c: u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - return output(context, (*[1]u8)(&c)[0..]); -} - -pub fn formatBuf( - buf: []const u8, - width: usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - try output(context, buf); - - var leftover_padding = if (width > buf.len) (width - buf.len) else return; - const pad_byte: u8 = ' '; - while (leftover_padding > 0) : (leftover_padding -= 1) { - try output(context, (*[1]u8)(&pad_byte)[0..1]); - } -} - -// Print a float in scientific notation to the specified precision. Null uses full precision. -// It should be the case that every full precision, printed value can be re-parsed back to the -// same type unambiguously. -pub fn formatFloatScientific( - value: var, - maybe_precision: ?usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - var x = @floatCast(f64, value); - - // Errol doesn't handle these special cases. - if (math.signbit(x)) { - try output(context, "-"); - x = -x; - } - - if (math.isNan(x)) { - return output(context, "nan"); - } - if (math.isPositiveInf(x)) { - return output(context, "inf"); - } - if (x == 0.0) { - try output(context, "0"); - - if (maybe_precision) |precision| { - if (precision != 0) { - try output(context, "."); - var i: usize = 0; - while (i < precision) : (i += 1) { - try output(context, "0"); - } - } - } else { - try output(context, ".0"); - } - - try output(context, "e+00"); - return; - } - - var buffer: [32]u8 = undefined; - var float_decimal = errol.errol3(x, buffer[0..]); - - if (maybe_precision) |precision| { - errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); - - try output(context, float_decimal.digits[0..1]); - - // {e0} case prints no `.` - if (precision != 0) { - try output(context, "."); - - var printed: usize = 0; - if (float_decimal.digits.len > 1) { - const num_digits = math.min(float_decimal.digits.len, precision + 1); - try output(context, float_decimal.digits[1..num_digits]); - printed += num_digits - 1; - } - - while (printed < precision) : (printed += 1) { - try output(context, "0"); - } - } - } else { - try output(context, float_decimal.digits[0..1]); - try output(context, "."); - if (float_decimal.digits.len > 1) { - const num_digits = if (@typeOf(value) == f32) math.min(usize(9), float_decimal.digits.len) else float_decimal.digits.len; - - try output(context, float_decimal.digits[1..num_digits]); - } else { - try output(context, "0"); - } - } - - try output(context, "e"); - const exp = float_decimal.exp - 1; - - if (exp >= 0) { - try output(context, "+"); - if (exp > -10 and exp < 10) { - try output(context, "0"); - } - try formatInt(exp, 10, false, 0, context, Errors, output); - } else { - try output(context, "-"); - if (exp > -10 and exp < 10) { - try output(context, "0"); - } - try formatInt(-exp, 10, false, 0, context, Errors, output); - } -} - -// Print a float of the format x.yyyyy where the number of y is specified by the precision argument. -// By default floats are printed at full precision (no rounding). -pub fn formatFloatDecimal( - value: var, - maybe_precision: ?usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - var x = f64(value); - - // Errol doesn't handle these special cases. - if (math.signbit(x)) { - try output(context, "-"); - x = -x; - } - - if (math.isNan(x)) { - return output(context, "nan"); - } - if (math.isPositiveInf(x)) { - return output(context, "inf"); - } - if (x == 0.0) { - try output(context, "0"); - - if (maybe_precision) |precision| { - if (precision != 0) { - try output(context, "."); - var i: usize = 0; - while (i < precision) : (i += 1) { - try output(context, "0"); - } - } else { - try output(context, ".0"); - } - } else { - try output(context, "0"); - } - - return; - } - - // non-special case, use errol3 - var buffer: [32]u8 = undefined; - var float_decimal = errol.errol3(x, buffer[0..]); - - if (maybe_precision) |precision| { - errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); - - // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; - - // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. - var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); - - if (num_digits_whole > 0) { - // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); - - var i = num_digits_whole_no_pad; - while (i < num_digits_whole) : (i += 1) { - try output(context, "0"); - } - } else { - try output(context, "0"); - } - - // {.0} special case doesn't want a trailing '.' - if (precision == 0) { - return; - } - - try output(context, "."); - - // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. - var printed: usize = 0; - - // Zero-fill until we reach significant digits or run out of precision. - if (float_decimal.exp <= 0) { - const zero_digit_count = @intCast(usize, -float_decimal.exp); - const zeros_to_print = math.min(zero_digit_count, precision); - - var i: usize = 0; - while (i < zeros_to_print) : (i += 1) { - try output(context, "0"); - printed += 1; - } - - if (printed >= precision) { - return; - } - } - - // Remaining fractional portion, zero-padding if insufficient. - assert(precision >= printed); - if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { - try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); - return; - } else { - try output(context, float_decimal.digits[num_digits_whole_no_pad..]); - printed += float_decimal.digits.len - num_digits_whole_no_pad; - - while (printed < precision) : (printed += 1) { - try output(context, "0"); - } - } - } else { - // exp < 0 means the leading is always 0 as errol result is normalized. - var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; - - // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. - var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); - - if (num_digits_whole > 0) { - // We may have to zero pad, for instance 1e4 requires zero padding. - try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); - - var i = num_digits_whole_no_pad; - while (i < num_digits_whole) : (i += 1) { - try output(context, "0"); - } - } else { - try output(context, "0"); - } - - // Omit `.` if no fractional portion - if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { - return; - } - - try output(context, "."); - - // Zero-fill until we reach significant digits or run out of precision. - if (float_decimal.exp < 0) { - const zero_digit_count = @intCast(usize, -float_decimal.exp); - - var i: usize = 0; - while (i < zero_digit_count) : (i += 1) { - try output(context, "0"); - } - } - - try output(context, float_decimal.digits[num_digits_whole_no_pad..]); - } -} - -pub fn formatBytes( - value: var, - width: ?usize, - comptime radix: usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - if (value == 0) { - return output(context, "0B"); - } - - const mags_si = " kMGTPEZY"; - const mags_iec = " KMGTPEZY"; - const magnitude = switch (radix) { - 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), - 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), - else => unreachable, - }; - const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); - const suffix = switch (radix) { - 1000 => mags_si[magnitude], - 1024 => mags_iec[magnitude], - else => unreachable, - }; - - try formatFloatDecimal(new_value, width, context, Errors, output); - - if (suffix == ' ') { - return output(context, "B"); - } - - const buf = switch (radix) { - 1000 => []u8{ suffix, 'B' }, - 1024 => []u8{ suffix, 'i', 'B' }, - else => unreachable, - }; - return output(context, buf); -} - -pub fn formatInt( - value: var, - base: u8, - uppercase: bool, - width: usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - if (@typeOf(value).is_signed) { - return formatIntSigned(value, base, uppercase, width, context, Errors, output); - } else { - return formatIntUnsigned(value, base, uppercase, width, context, Errors, output); - } -} - -fn formatIntSigned( - value: var, - base: u8, - uppercase: bool, - width: usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - const uint = @IntType(false, @typeOf(value).bit_count); - if (value < 0) { - const minus_sign: u8 = '-'; - try output(context, (*[1]u8)(&minus_sign)[0..]); - const new_value = @intCast(uint, -(value + 1)) + 1; - const new_width = if (width == 0) 0 else (width - 1); - return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); - } else if (width == 0) { - return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); - } else { - const plus_sign: u8 = '+'; - try output(context, (*[1]u8)(&plus_sign)[0..]); - const new_value = @intCast(uint, value); - const new_width = if (width == 0) 0 else (width - 1); - return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); - } -} - -fn formatIntUnsigned( - value: var, - base: u8, - uppercase: bool, - width: usize, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, -) Errors!void { - // max_int_digits accounts for the minus sign. when printing an unsigned - // number we don't need to do that. - var buf: [max_int_digits - 1]u8 = undefined; - const min_int_bits = comptime math.max(@typeOf(value).bit_count, @typeOf(base).bit_count); - const MinInt = @IntType(@typeOf(value).is_signed, min_int_bits); - var a: MinInt = value; - var index: usize = buf.len; - - while (true) { - const digit = a % base; - index -= 1; - buf[index] = digitToChar(@intCast(u8, digit), uppercase); - a /= base; - if (a == 0) break; - } - - const digits_buf = buf[index..]; - const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; - - if (padding > index) { - const zero_byte: u8 = '0'; - var leftover_padding = padding - index; - while (true) { - try output(context, (*[1]u8)(&zero_byte)[0..]); - leftover_padding -= 1; - if (leftover_padding == 0) break; - } - mem.set(u8, buf[0..index], '0'); - return output(context, buf); - } else { - const padded_buf = buf[index - padding ..]; - mem.set(u8, padded_buf[0..padding], '0'); - return output(context, padded_buf); - } -} - -pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { - var context = FormatIntBuf{ - .out_buf = out_buf, - .index = 0, - }; - formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable; - return context.index; -} -const FormatIntBuf = struct { - out_buf: []u8, - index: usize, -}; -fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { - mem.copy(u8, context.out_buf[context.index..], bytes); - context.index += bytes.len; -} - -pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { - if (!T.is_signed) return parseUnsigned(T, buf, radix); - if (buf.len == 0) return T(0); - if (buf[0] == '-') { - return math.negate(try parseUnsigned(T, buf[1..], radix)); - } else if (buf[0] == '+') { - return parseUnsigned(T, buf[1..], radix); - } else { - return parseUnsigned(T, buf, radix); - } -} - -test "fmt.parseInt" { - testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); - testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); - testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); - testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); - testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); - testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); - testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); -} - -const ParseUnsignedError = error{ - /// The result cannot fit in the type specified - Overflow, - - /// The input had a byte that was not a digit - InvalidCharacter, -}; - -pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { - var x: T = 0; - - for (buf) |c| { - const digit = try charToDigit(c, radix); - - if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); - x = try math.add(T, x, try math.cast(T, digit)); - } - - return x; -} - -test "fmt.parseUnsigned" { - testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); - testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); - testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); - - testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); - testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); - - testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); - - testing.expect((try parseUnsigned(u7, "1", 10)) == 1); - testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); - - testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); - testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); - - testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); - - // these numbers should fit even though the radix itself doesn't fit in the destination type - testing.expect((try parseUnsigned(u1, "0", 10)) == 0); - testing.expect((try parseUnsigned(u1, "1", 10)) == 1); - testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); - testing.expect((try parseUnsigned(u1, "001", 16)) == 1); - testing.expect((try parseUnsigned(u2, "3", 16)) == 3); - testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); -} - -pub const parseFloat = @import("parse_float.zig").parseFloat; - -test "fmt.parseFloat" { - _ = @import("parse_float.zig"); -} - -pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { - const value = switch (c) { - '0'...'9' => c - '0', - 'A'...'Z' => c - 'A' + 10, - 'a'...'z' => c - 'a' + 10, - else => return error.InvalidCharacter, - }; - - if (value >= radix) return error.InvalidCharacter; - - return value; -} - -fn digitToChar(digit: u8, uppercase: bool) u8 { - return switch (digit) { - 0...9 => digit + '0', - 10...35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), - else => unreachable, - }; -} - -const BufPrintContext = struct { - remaining: []u8, -}; - -fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { - if (context.remaining.len < bytes.len) return error.BufferTooSmall; - mem.copy(u8, context.remaining, bytes); - context.remaining = context.remaining[bytes.len..]; -} - -pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { - var context = BufPrintContext{ .remaining = buf }; - try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); - return buf[0 .. buf.len - context.remaining.len]; -} - -pub const AllocPrintError = error{OutOfMemory}; - -pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { - var size: usize = 0; - format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; - const buf = try allocator.alloc(u8, size); - return bufPrint(buf, fmt, args) catch |err| switch (err) { - error.BufferTooSmall => unreachable, // we just counted the size above - }; -} - -fn countSize(size: *usize, bytes: []const u8) (error{}!void) { - size.* += bytes.len; -} - -test "buf print int" { - var buffer: [max_int_digits]u8 = undefined; - const buf = buffer[0..]; - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); - - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); - - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); - - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); - testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); -} - -fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { - return buf[0..formatIntBuf(buf, value, base, uppercase, width)]; -} - -test "parse u64 digit too big" { - _ = parseUnsigned(u64, "123a", 10) catch |err| { - if (err == error.InvalidCharacter) return; - unreachable; - }; - unreachable; -} - -test "parse unsigned comptime" { - comptime { - testing.expect((try parseUnsigned(usize, "2", 10)) == 2); - } -} - -test "fmt.format" { - { - const value: ?i32 = 1234; - try testFmt("optional: 1234\n", "optional: {}\n", value); - } - { - const value: ?i32 = null; - try testFmt("optional: null\n", "optional: {}\n", value); - } - { - const value: anyerror!i32 = 1234; - try testFmt("error union: 1234\n", "error union: {}\n", value); - } - { - const value: anyerror!i32 = error.InvalidChar; - try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); - } - { - const value: u3 = 0b101; - try testFmt("u3: 5\n", "u3: {}\n", value); - } - { - const value: u8 = 'a'; - try testFmt("u8: a\n", "u8: {c}\n", value); - } - { - const value: u8 = 0b1100; - try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); - } - { - var buf1: [32]u8 = undefined; - var context = BufPrintContext{ .remaining = buf1[0..] }; - try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite); - 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); - 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); - res = buf1[0 .. buf1.len - context.remaining.len]; - testing.expect(mem.eql(u8, res, "1100")); - } - { - const value: [3]u8 = "abc"; - try testFmt("array: abc\n", "array: {}\n", value); - try testFmt("array: abc\n", "array: {}\n", &value); - - var buf: [100]u8 = undefined; - try testFmt( - try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), - "array: {*}\n", - &value, - ); - } - { - const value: []const u8 = "abc"; - try testFmt("slice: abc\n", "slice: {}\n", value); - } - { - const value = @intToPtr(*i32, 0xdeadbeef); - try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); - try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); - } - { - const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); - } - { - const value = @intToPtr(fn () void, 0xdeadbeef); - try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); - } - try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); - try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); - try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); - try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); - try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); - try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); - { - const Struct = struct { - field: u8, - }; - const value = Struct{ .field = 42 }; - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value); - try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value); - } - { - const Struct = struct { - a: u0, - b: u1, - }; - const value = Struct{ .a = 0, .b = 1 }; - try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); - } - { - const Enum = enum { - One, - Two, - }; - const value = Enum.Two; - try testFmt("enum: Enum.Two\n", "enum: {}\n", value); - try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 1.34; - const result = try bufPrint(buf1[0..], "f32: {e}\n", value); - testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 12.34; - const result = try bufPrint(buf1[0..], "f32: {e}\n", value); - testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = -12.34e10; - const result = try bufPrint(buf1[0..], "f64: {e}\n", value); - testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n")); - } - { - // This fails on release due to a minor rounding difference. - // --release-fast outputs 9.999960000000001e-40 vs. the expected. - // TODO fix this, it should be the same in Debug and ReleaseFast - if (builtin.mode == builtin.Mode.Debug) { - var buf1: [32]u8 = undefined; - const value: f64 = 9.999960e-40; - const result = try bufPrint(buf1[0..], "f64: {e}\n", value); - testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); - } - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 1.409706e-42; - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = @bitCast(f32, u32(814313563)); - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = @bitCast(f32, u32(1006632960)); - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n")); - } - { - // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. - // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. - var buf1: [32]u8 = undefined; - const value: f64 = @bitCast(f32, u32(1203982400)); - const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); - testing.expect(mem.eql(u8, result, "f64: nan\n")); - } - if (builtin.arch != builtin.Arch.arm) { - // negative nan is not defined by IEE 754, - // and ARM thus normalizes it to positive nan - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); - testing.expect(mem.eql(u8, result, "f64: -nan\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); - testing.expect(mem.eql(u8, result, "f64: inf\n")); - } - { - var buf1: [32]u8 = undefined; - const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); - testing.expect(mem.eql(u8, result, "f64: -inf\n")); - } - { - var buf1: [64]u8 = undefined; - const value: f64 = 1.52314e+29; - const result = try bufPrint(buf1[0..], "f64: {.}\n", value); - testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 1.1234; - const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); - testing.expect(mem.eql(u8, result, "f32: 1.1\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 1234.567; - const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); - testing.expect(mem.eql(u8, result, "f32: 1234.57\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = -11.1234; - const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); - // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). - // -11.12339... is rounded back up to -11.1234 - testing.expect(mem.eql(u8, result, "f32: -11.1234\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f32 = 91.12345; - const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f32: 91.12345\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 91.12345678901235; - const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); - testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 0.0; - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 5.700; - const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); - testing.expect(mem.eql(u8, result, "f64: 6\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 9.999; - const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); - testing.expect(mem.eql(u8, result, "f64: 10.0\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 1.0; - const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); - testing.expect(mem.eql(u8, result, "f64: 1.000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 0.0003; - const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.00030000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 1.40130e-45; - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = 9.999960e-40; - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); - } - // libc checks - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(916964781))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(925353389))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1036831278))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.10000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1065353133))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 1.00000\n")); - } - { - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1092616192))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 10.00000\n")); - } - // libc differences - { - var buf1: [32]u8 = undefined; - // This is 0.015625 exactly according to gdb. We thus round down, - // however glibc rounds up for some reason. This occurs for all - // floats of the form x.yyyy25 on a precision point. - const value: f64 = f64(@bitCast(f32, u32(1015021568))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); - } - // std-windows-x86_64-Debug-bare test case fails - { - // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 - // also rounds to 630 so I'm inclined to believe libc is not - // optimal here. - var buf1: [32]u8 = undefined; - const value: f64 = f64(@bitCast(f32, u32(1518338049))); - const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); - testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); - } - //custom type format - { - const Vec2 = struct { - const SelfType = @This(); - x: f32, - y: f32, - - pub fn format( - self: SelfType, - comptime fmt: []const u8, - context: var, - comptime Errors: type, - output: fn (@typeOf(context), []const u8) Errors!void, - ) Errors!void { - switch (fmt.len) { - 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), - 1 => switch (fmt[0]) { - //point format - 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), - //dimension format - 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), - else => unreachable, - }, - else => unreachable, - } - } - }; - - var buf1: [32]u8 = undefined; - var value = Vec2{ - .x = 10.2, - .y = 2.22, - }; - try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); - - // same thing but not passing a pointer - try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); - try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); - } - //struct format - { - const S = struct { - a: u32, - b: anyerror, - }; - - const inst = S{ - .a = 456, - .b = error.Unused, - }; - - try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); - } - //union format - { - const TU = union(enum) { - float: f32, - int: u32, - }; - - const UU = union { - float: f32, - int: u32, - }; - - const EU = extern union { - float: f32, - int: u32, - }; - - const tu_inst = TU{ .int = 123 }; - const uu_inst = UU{ .int = 456 }; - const eu_inst = EU{ .float = 321.123 }; - - try testFmt("TU{ .int = 123 }", "{}", tu_inst); - - var buf: [100]u8 = undefined; - const uu_result = try bufPrint(buf[0..], "{}", uu_inst); - testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); - - const eu_result = try bufPrint(buf[0..], "{}", eu_inst); - testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); - } - //enum format - { - const E = enum { - One, - Two, - Three, - }; - - const inst = E.Two; - - try testFmt("E.Two", "{}", inst); - } - //print bytes as hex - { - const some_bytes = "\xCA\xFE\xBA\xBE"; - try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); - try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); - //Test Slices - try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); - try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); - const bytes_with_zeros = "\x00\x0E\xBA\xBE"; - try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); - } -} - -fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { - var buf: [100]u8 = undefined; - const result = try bufPrint(buf[0..], template, args); - if (mem.eql(u8, result, expected)) return; - - std.debug.warn("\n====== expected this output: =========\n"); - std.debug.warn("{}", expected); - std.debug.warn("\n======== instead found this: =========\n"); - std.debug.warn("{}", result); - std.debug.warn("\n======================================\n"); - return error.TestFailed; -} - -pub fn trim(buf: []const u8) []const u8 { - var start: usize = 0; - while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} - - var end: usize = buf.len; - while (true) { - if (end > start) { - const new_end = end - 1; - if (isWhiteSpace(buf[new_end])) { - end = new_end; - continue; - } - } - break; - } - return buf[start..end]; -} - -test "fmt.trim" { - testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); - testing.expect(mem.eql(u8, "", trim(" "))); - testing.expect(mem.eql(u8, "", trim(""))); - testing.expect(mem.eql(u8, "abc", trim(" abc"))); - testing.expect(mem.eql(u8, "abc", trim("abc "))); -} - -pub fn isWhiteSpace(byte: u8) bool { - return switch (byte) { - ' ', '\t', '\n', '\r' => true, - else => false, - }; -} - -pub fn hexToBytes(out: []u8, input: []const u8) !void { - if (out.len * 2 < input.len) - return error.InvalidLength; - - var in_i: usize = 0; - while (in_i != input.len) : (in_i += 2) { - const hi = try charToDigit(input[in_i], 16); - const lo = try charToDigit(input[in_i + 1], 16); - out[in_i / 2] = (hi << 4) | lo; - } -} - -test "fmt.hexToBytes" { - const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; - var pb: [32]u8 = undefined; - try hexToBytes(pb[0..], test_hex_str); - try testFmt(test_hex_str, "{X}", pb); -} diff --git a/std/fmt/parse_float.zig b/std/fmt/parse_float.zig index de9619efe2..c13977dc42 100644 --- a/std/fmt/parse_float.zig +++ b/std/fmt/parse_float.zig @@ -29,7 +29,7 @@ // - Only supports round-to-zero // - Does not handle denormals -const std = @import("../index.zig"); +const std = @import("../std.zig"); const max_digits = 25; diff --git a/std/hash.zig b/std/hash.zig new file mode 100644 index 0000000000..19ee62824d --- /dev/null +++ b/std/hash.zig @@ -0,0 +1,22 @@ +const adler = @import("hash/adler.zig"); +pub const Adler32 = adler.Adler32; + +// pub for polynomials + generic crc32 construction +pub const crc = @import("hash/crc.zig"); +pub const Crc32 = crc.Crc32; + +const fnv = @import("hash/fnv.zig"); +pub const Fnv1a_32 = fnv.Fnv1a_32; +pub const Fnv1a_64 = fnv.Fnv1a_64; +pub const Fnv1a_128 = fnv.Fnv1a_128; + +const siphash = @import("hash/siphash.zig"); +pub const SipHash64 = siphash.SipHash64; +pub const SipHash128 = siphash.SipHash128; + +test "hash" { + _ = @import("hash/adler.zig"); + _ = @import("hash/crc.zig"); + _ = @import("hash/fnv.zig"); + _ = @import("hash/siphash.zig"); +} diff --git a/std/hash/adler.zig b/std/hash/adler.zig index 78f960367a..e95203cc6c 100644 --- a/std/hash/adler.zig +++ b/std/hash/adler.zig @@ -3,7 +3,7 @@ // https://tools.ietf.org/html/rfc1950#section-9 // https://github.com/madler/zlib/blob/master/adler32.c -const std = @import("../index.zig"); +const std = @import("../std.zig"); const testing = std.testing; pub const Adler32 = struct { diff --git a/std/hash/crc.zig b/std/hash/crc.zig index 9bea358bf1..53b4262c93 100644 --- a/std/hash/crc.zig +++ b/std/hash/crc.zig @@ -5,7 +5,7 @@ // - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is // still moderately fast just slow relative to the slicing approach. -const std = @import("../index.zig"); +const std = @import("../std.zig"); const debug = std.debug; const testing = std.testing; diff --git a/std/hash/fnv.zig b/std/hash/fnv.zig index 6876b636f6..8094134e19 100644 --- a/std/hash/fnv.zig +++ b/std/hash/fnv.zig @@ -4,7 +4,7 @@ // // https://tools.ietf.org/html/draft-eastlake-fnv-14 -const std = @import("../index.zig"); +const std = @import("../std.zig"); const testing = std.testing; pub const Fnv1a_32 = Fnv1a(u32, 0x01000193, 0x811c9dc5); diff --git a/std/hash/index.zig b/std/hash/index.zig deleted file mode 100644 index 8cce35f3c5..0000000000 --- a/std/hash/index.zig +++ /dev/null @@ -1,22 +0,0 @@ -const adler = @import("adler.zig"); -pub const Adler32 = adler.Adler32; - -// pub for polynomials + generic crc32 construction -pub const crc = @import("crc.zig"); -pub const Crc32 = crc.Crc32; - -const fnv = @import("fnv.zig"); -pub const Fnv1a_32 = fnv.Fnv1a_32; -pub const Fnv1a_64 = fnv.Fnv1a_64; -pub const Fnv1a_128 = fnv.Fnv1a_128; - -const siphash = @import("siphash.zig"); -pub const SipHash64 = siphash.SipHash64; -pub const SipHash128 = siphash.SipHash128; - -test "hash" { - _ = @import("adler.zig"); - _ = @import("crc.zig"); - _ = @import("fnv.zig"); - _ = @import("siphash.zig"); -} diff --git a/std/hash/siphash.zig b/std/hash/siphash.zig index c9a6128d35..77194c3e03 100644 --- a/std/hash/siphash.zig +++ b/std/hash/siphash.zig @@ -5,7 +5,7 @@ // // https://131002.net/siphash/ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; const testing = std.testing; const math = std.math; diff --git a/std/hash_map.zig b/std/hash_map.zig index 6ae9e8e2c3..d31fcdb597 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const assert = debug.assert; const testing = std.testing; diff --git a/std/heap.zig b/std/heap.zig index e257977f9d..e03e77a39c 100644 --- a/std/heap.zig +++ b/std/heap.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const assert = debug.assert; const testing = std.testing; diff --git a/std/index.zig b/std/index.zig deleted file mode 100644 index f8c1b065e9..0000000000 --- a/std/index.zig +++ /dev/null @@ -1,95 +0,0 @@ -pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList; -pub const ArrayList = @import("array_list.zig").ArrayList; -pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; -pub const BufMap = @import("buf_map.zig").BufMap; -pub const BufSet = @import("buf_set.zig").BufSet; -pub const Buffer = @import("buffer.zig").Buffer; -pub const BufferOutStream = @import("io.zig").BufferOutStream; -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 PriorityQueue = @import("priority_queue.zig").PriorityQueue; -pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; -pub const SegmentedList = @import("segmented_list.zig").SegmentedList; -pub const SpinLock = @import("spinlock.zig").SpinLock; - -pub const atomic = @import("atomic/index.zig"); -pub const base64 = @import("base64.zig"); -pub const build = @import("build.zig"); -pub const c = @import("c/index.zig"); -pub const coff = @import("coff.zig"); -pub const crypto = @import("crypto/index.zig"); -pub const cstr = @import("cstr.zig"); -pub const debug = @import("debug/index.zig"); -pub const dwarf = @import("dwarf.zig"); -pub const elf = @import("elf.zig"); -pub const empty_import = @import("empty.zig"); -pub const event = @import("event.zig"); -pub const fmt = @import("fmt/index.zig"); -pub const hash = @import("hash/index.zig"); -pub const hash_map = @import("hash_map.zig"); -pub const heap = @import("heap.zig"); -pub const io = @import("io.zig"); -pub const json = @import("json.zig"); -pub const lazyInit = @import("lazy_init.zig").lazyInit; -pub const macho = @import("macho.zig"); -pub const math = @import("math/index.zig"); -pub const mem = @import("mem.zig"); -pub const meta = @import("meta/index.zig"); -pub const net = @import("net.zig"); -pub const os = @import("os/index.zig"); -pub const pdb = @import("pdb.zig"); -pub const rand = @import("rand/index.zig"); -pub const rb = @import("rb.zig"); -pub const sort = @import("sort.zig"); -pub const testing = @import("testing.zig"); -pub const unicode = @import("unicode.zig"); -pub const zig = @import("zig/index.zig"); - -test "std" { - // run tests from these - _ = @import("array_list.zig"); - _ = @import("atomic/index.zig"); - _ = @import("buf_map.zig"); - _ = @import("buf_set.zig"); - _ = @import("buffer.zig"); - _ = @import("hash_map.zig"); - _ = @import("linked_list.zig"); - _ = @import("mutex.zig"); - _ = @import("statically_initialized_mutex.zig"); - _ = @import("segmented_list.zig"); - _ = @import("spinlock.zig"); - - _ = @import("base64.zig"); - _ = @import("build.zig"); - _ = @import("c/index.zig"); - _ = @import("coff.zig"); - _ = @import("crypto/index.zig"); - _ = @import("cstr.zig"); - _ = @import("debug/index.zig"); - _ = @import("dwarf.zig"); - _ = @import("dynamic_library.zig"); - _ = @import("elf.zig"); - _ = @import("empty.zig"); - _ = @import("event.zig"); - _ = @import("fmt/index.zig"); - _ = @import("hash/index.zig"); - _ = @import("heap.zig"); - _ = @import("io.zig"); - _ = @import("json.zig"); - _ = @import("lazy_init.zig"); - _ = @import("macho.zig"); - _ = @import("math/index.zig"); - _ = @import("mem.zig"); - _ = @import("meta/index.zig"); - _ = @import("net.zig"); - _ = @import("os/index.zig"); - _ = @import("pdb.zig"); - _ = @import("priority_queue.zig"); - _ = @import("rand/index.zig"); - _ = @import("sort.zig"); - _ = @import("testing.zig"); - _ = @import("unicode.zig"); - _ = @import("zig/index.zig"); -} diff --git a/std/io.zig b/std/io.zig index f6e3790af6..7756f36116 100644 --- a/std/io.zig +++ b/std/io.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const c = std.c; diff --git a/std/io/seekable_stream.zig b/std/io/seekable_stream.zig index a766f4fb89..5529e42ff1 100644 --- a/std/io/seekable_stream.zig +++ b/std/io/seekable_stream.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const InStream = std.io.InStream; pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { diff --git a/std/io_test.zig b/std/io_test.zig index 87b970edf8..3c4c15ba12 100644 --- a/std/io_test.zig +++ b/std/io_test.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const io = std.io; const meta = std.meta; const trait = std.trait; @@ -34,8 +34,7 @@ test "write a file, read it, then delete it" { // make sure openWriteNoClobber doesn't harm the file if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { unreachable; - } - else |err| { + } else |err| { std.debug.assert(err == os.File.OpenError.PathAlreadyExists); } } @@ -180,7 +179,7 @@ test "BitInStream" { expect(out_bits == 16); _ = try bit_stream_be.readBits(u0, 0, &out_bits); - + expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); expect(out_bits == 0); expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); @@ -212,7 +211,7 @@ test "BitInStream" { expect(out_bits == 16); _ = try bit_stream_le.readBits(u0, 0, &out_bits); - + expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); expect(out_bits == 0); expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); @@ -281,7 +280,7 @@ test "BitStreams with File Stream" { var file_out_stream = &file_out.stream; const OutError = os.File.WriteError; var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); - + try bit_stream.writeBits(u2(1), 1); try bit_stream.writeBits(u5(2), 2); try bit_stream.writeBits(u128(3), 3); @@ -298,7 +297,7 @@ test "BitStreams with File Stream" { var file_in_stream = &file_in.stream; const InError = os.File.ReadError; var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); - + var out_bits: usize = undefined; expect(1 == try bit_stream.readBits(u2, 1, &out_bits)); @@ -313,7 +312,7 @@ test "BitStreams with File Stream" { expect(out_bits == 5); expect(1 == try bit_stream.readBits(u1, 1, &out_bits)); expect(out_bits == 1); - + expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); } try os.deleteFile(tmp_file_name); @@ -322,14 +321,14 @@ test "BitStreams with File Stream" { fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { //@NOTE: if this test is taking too long, reduce the maximum tested bitsize const max_test_bitsize = 128; - + const total_bytes = comptime blk: { var bytes = 0; comptime var i = 0; while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); break :blk bytes * 2; }; - + var data_mem: [total_bytes]u8 = undefined; var out = io.SliceOutStream.init(data_mem[0..]); const OutError = io.SliceOutStream.Error; @@ -386,12 +385,13 @@ test "Serializer/Deserializer Int" { try testIntSerializerDeserializer(builtin.Endian.Little, true); } -fn testIntSerializerDeserializerInfNaN(comptime endian: builtin.Endian, - comptime is_packed: bool) !void -{ - const mem_size = (16*2 + 32*2 + 64*2 + 128*2) / comptime meta.bitCount(u8); +fn testIntSerializerDeserializerInfNaN( + comptime endian: builtin.Endian, + comptime is_packed: bool, +) !void { + const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8); var data_mem: [mem_size]u8 = undefined; - + var out = io.SliceOutStream.init(data_mem[0..]); const OutError = io.SliceOutStream.Error; var out_stream = &out.stream; @@ -472,8 +472,6 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe f_u2: u2, }; - - //to test custom serialization const Custom = struct { f_f16: f16, diff --git a/std/json.zig b/std/json.zig index dd053e7635..551c3a8da7 100644 --- a/std/json.zig +++ b/std/json.zig @@ -2,7 +2,7 @@ // // https://tools.ietf.org/html/rfc8259 -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const testing = std.testing; const mem = std.mem; diff --git a/std/json_test.zig b/std/json_test.zig index edc50be8cb..a323e6e979 100644 --- a/std/json_test.zig +++ b/std/json_test.zig @@ -3,7 +3,7 @@ // Tests are taken from https://github.com/nst/JSONTestSuite // Read also http://seriot.ch/parsing_json.php for a good overview. -const std = @import("index.zig"); +const std = @import("std.zig"); fn ok(comptime s: []const u8) void { std.testing.expect(std.json.validate(s)); diff --git a/std/lazy_init.zig b/std/lazy_init.zig index a09168786b..7beabb9cde 100644 --- a/std/lazy_init.zig +++ b/std/lazy_init.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; diff --git a/std/linked_list.zig b/std/linked_list.zig index 86e5cd056e..c4ad525913 100644 --- a/std/linked_list.zig +++ b/std/linked_list.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const assert = debug.assert; const testing = std.testing; diff --git a/std/math.zig b/std/math.zig new file mode 100644 index 0000000000..cead685778 --- /dev/null +++ b/std/math.zig @@ -0,0 +1,797 @@ +const builtin = @import("builtin"); +const std = @import("std.zig"); +const TypeId = builtin.TypeId; +const assert = std.debug.assert; +const testing = std.testing; + +pub const e = 2.71828182845904523536028747135266249775724709369995; +pub const pi = 3.14159265358979323846264338327950288419716939937510; + +// From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) +pub const f128_true_min = @bitCast(f128, u128(0x00000000000000000000000000000001)); +pub const f128_min = @bitCast(f128, u128(0x00010000000000000000000000000000)); +pub const f128_max = @bitCast(f128, u128(0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); +pub const f128_epsilon = @bitCast(f128, u128(0x3F8F0000000000000000000000000000)); +pub const f128_toint = 1.0 / f128_epsilon; + +// float.h details +pub const f64_true_min = 4.94065645841246544177e-324; +pub const f64_min = 2.2250738585072014e-308; +pub const f64_max = 1.79769313486231570815e+308; +pub const f64_epsilon = 2.22044604925031308085e-16; +pub const f64_toint = 1.0 / f64_epsilon; + +pub const f32_true_min = 1.40129846432481707092e-45; +pub const f32_min = 1.17549435082228750797e-38; +pub const f32_max = 3.40282346638528859812e+38; +pub const f32_epsilon = 1.1920928955078125e-07; +pub const f32_toint = 1.0 / f32_epsilon; + +pub const f16_true_min = 0.000000059604644775390625; // 2**-24 +pub const f16_min = 0.00006103515625; // 2**-14 +pub const f16_max = 65504; +pub const f16_epsilon = 0.0009765625; // 2**-10 +pub const f16_toint = 1.0 / f16_epsilon; + +pub const nan_u16 = u16(0x7C01); +pub const nan_f16 = @bitCast(f16, nan_u16); + +pub const inf_u16 = u16(0x7C00); +pub const inf_f16 = @bitCast(f16, inf_u16); + +pub const nan_u32 = u32(0x7F800001); +pub const nan_f32 = @bitCast(f32, nan_u32); + +pub const inf_u32 = u32(0x7F800000); +pub const inf_f32 = @bitCast(f32, inf_u32); + +pub const nan_u64 = u64(0x7FF << 52) | 1; +pub const nan_f64 = @bitCast(f64, nan_u64); + +pub const inf_u64 = u64(0x7FF << 52); +pub const inf_f64 = @bitCast(f64, inf_u64); + +pub const nan_u128 = u128(0x7fff0000000000000000000000000001); +pub const nan_f128 = @bitCast(f128, nan_u128); + +pub const inf_u128 = u128(0x7fff0000000000000000000000000000); +pub const inf_f128 = @bitCast(f128, inf_u128); + +pub const nan = @import("math/nan.zig").nan; +pub const snan = @import("math/nan.zig").snan; +pub const inf = @import("math/inf.zig").inf; + +pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { + assert(@typeId(T) == TypeId.Float); + return fabs(x - y) < epsilon; +} + +// TODO: Hide the following in an internal module. +pub fn forceEval(value: var) void { + const T = @typeOf(value); + switch (T) { + f16 => { + var x: f16 = undefined; + const p = @ptrCast(*volatile f16, &x); + p.* = x; + }, + f32 => { + var x: f32 = undefined; + const p = @ptrCast(*volatile f32, &x); + p.* = x; + }, + f64 => { + var x: f64 = undefined; + const p = @ptrCast(*volatile f64, &x); + p.* = x; + }, + else => { + @compileError("forceEval not implemented for " ++ @typeName(T)); + }, + } +} + +pub fn raiseInvalid() void { + // Raise INVALID fpu exception +} + +pub fn raiseUnderflow() void { + // Raise UNDERFLOW fpu exception +} + +pub fn raiseOverflow() void { + // Raise OVERFLOW fpu exception +} + +pub fn raiseInexact() void { + // Raise INEXACT fpu exception +} + +pub fn raiseDivByZero() void { + // Raise INEXACT fpu exception +} + +pub const isNan = @import("math/isnan.zig").isNan; +pub const isSignalNan = @import("math/isnan.zig").isSignalNan; +pub const fabs = @import("math/fabs.zig").fabs; +pub const ceil = @import("math/ceil.zig").ceil; +pub const floor = @import("math/floor.zig").floor; +pub const trunc = @import("math/trunc.zig").trunc; +pub const round = @import("math/round.zig").round; +pub const frexp = @import("math/frexp.zig").frexp; +pub const frexp32_result = @import("math/frexp.zig").frexp32_result; +pub const frexp64_result = @import("math/frexp.zig").frexp64_result; +pub const modf = @import("math/modf.zig").modf; +pub const modf32_result = @import("math/modf.zig").modf32_result; +pub const modf64_result = @import("math/modf.zig").modf64_result; +pub const copysign = @import("math/copysign.zig").copysign; +pub const isFinite = @import("math/isfinite.zig").isFinite; +pub const isInf = @import("math/isinf.zig").isInf; +pub const isPositiveInf = @import("math/isinf.zig").isPositiveInf; +pub const isNegativeInf = @import("math/isinf.zig").isNegativeInf; +pub const isNormal = @import("math/isnormal.zig").isNormal; +pub const signbit = @import("math/signbit.zig").signbit; +pub const scalbn = @import("math/scalbn.zig").scalbn; +pub const pow = @import("math/pow.zig").pow; +pub const powi = @import("math/powi.zig").powi; +pub const sqrt = @import("math/sqrt.zig").sqrt; +pub const cbrt = @import("math/cbrt.zig").cbrt; +pub const acos = @import("math/acos.zig").acos; +pub const asin = @import("math/asin.zig").asin; +pub const atan = @import("math/atan.zig").atan; +pub const atan2 = @import("math/atan2.zig").atan2; +pub const hypot = @import("math/hypot.zig").hypot; +pub const exp = @import("math/exp.zig").exp; +pub const exp2 = @import("math/exp2.zig").exp2; +pub const expm1 = @import("math/expm1.zig").expm1; +pub const ilogb = @import("math/ilogb.zig").ilogb; +pub const ln = @import("math/ln.zig").ln; +pub const log = @import("math/log.zig").log; +pub const log2 = @import("math/log2.zig").log2; +pub const log10 = @import("math/log10.zig").log10; +pub const log1p = @import("math/log1p.zig").log1p; +pub const fma = @import("math/fma.zig").fma; +pub const asinh = @import("math/asinh.zig").asinh; +pub const acosh = @import("math/acosh.zig").acosh; +pub const atanh = @import("math/atanh.zig").atanh; +pub const sinh = @import("math/sinh.zig").sinh; +pub const cosh = @import("math/cosh.zig").cosh; +pub const tanh = @import("math/tanh.zig").tanh; +pub const cos = @import("math/cos.zig").cos; +pub const sin = @import("math/sin.zig").sin; +pub const tan = @import("math/tan.zig").tan; + +pub const complex = @import("math/complex.zig"); +pub const Complex = complex.Complex; + +pub const big = @import("math/big.zig"); + +test "math" { + _ = @import("math/nan.zig"); + _ = @import("math/isnan.zig"); + _ = @import("math/fabs.zig"); + _ = @import("math/ceil.zig"); + _ = @import("math/floor.zig"); + _ = @import("math/trunc.zig"); + _ = @import("math/round.zig"); + _ = @import("math/frexp.zig"); + _ = @import("math/modf.zig"); + _ = @import("math/copysign.zig"); + _ = @import("math/isfinite.zig"); + _ = @import("math/isinf.zig"); + _ = @import("math/isnormal.zig"); + _ = @import("math/signbit.zig"); + _ = @import("math/scalbn.zig"); + _ = @import("math/pow.zig"); + _ = @import("math/powi.zig"); + _ = @import("math/sqrt.zig"); + _ = @import("math/cbrt.zig"); + _ = @import("math/acos.zig"); + _ = @import("math/asin.zig"); + _ = @import("math/atan.zig"); + _ = @import("math/atan2.zig"); + _ = @import("math/hypot.zig"); + _ = @import("math/exp.zig"); + _ = @import("math/exp2.zig"); + _ = @import("math/expm1.zig"); + _ = @import("math/ilogb.zig"); + _ = @import("math/ln.zig"); + _ = @import("math/log.zig"); + _ = @import("math/log2.zig"); + _ = @import("math/log10.zig"); + _ = @import("math/log1p.zig"); + _ = @import("math/fma.zig"); + _ = @import("math/asinh.zig"); + _ = @import("math/acosh.zig"); + _ = @import("math/atanh.zig"); + _ = @import("math/sinh.zig"); + _ = @import("math/cosh.zig"); + _ = @import("math/tanh.zig"); + _ = @import("math/sin.zig"); + _ = @import("math/cos.zig"); + _ = @import("math/tan.zig"); + + _ = @import("math/complex.zig"); + + _ = @import("math/big.zig"); +} + +pub fn floatMantissaBits(comptime T: type) comptime_int { + assert(@typeId(T) == builtin.TypeId.Float); + + return switch (T.bit_count) { + 16 => 10, + 32 => 23, + 64 => 52, + 80 => 64, + 128 => 112, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +pub fn floatExponentBits(comptime T: type) comptime_int { + assert(@typeId(T) == builtin.TypeId.Float); + + return switch (T.bit_count) { + 16 => 5, + 32 => 8, + 64 => 11, + 80 => 15, + 128 => 15, + else => @compileError("unknown floating point type " ++ @typeName(T)), + }; +} + +pub fn min(x: var, y: var) @typeOf(x + y) { + return if (x < y) x else y; +} + +test "math.min" { + testing.expect(min(i32(-1), i32(2)) == -1); +} + +pub fn max(x: var, y: var) @typeOf(x + y) { + return if (x > y) x else y; +} + +test "math.max" { + testing.expect(max(i32(-1), i32(2)) == 2); +} + +pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { + var answer: T = undefined; + return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; +} + +pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) { + var answer: T = undefined; + return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; +} + +pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { + var answer: T = undefined; + return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; +} + +pub fn negate(x: var) !@typeOf(x) { + return sub(@typeOf(x), 0, x); +} + +pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { + var answer: T = undefined; + return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer; +} + +/// Shifts left. Overflowed bits are truncated. +/// A negative shift amount results in a right shift. +pub fn shl(comptime T: type, a: T, shift_amt: var) T { + const abs_shift_amt = absCast(shift_amt); + const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); + + if (@typeOf(shift_amt).is_signed) { + if (shift_amt >= 0) { + return a << casted_shift_amt; + } else { + return a >> casted_shift_amt; + } + } + + return a << casted_shift_amt; +} + +test "math.shl" { + testing.expect(shl(u8, 0b11111111, usize(3)) == 0b11111000); + testing.expect(shl(u8, 0b11111111, usize(8)) == 0); + testing.expect(shl(u8, 0b11111111, usize(9)) == 0); + testing.expect(shl(u8, 0b11111111, isize(-2)) == 0b00111111); +} + +/// Shifts right. Overflowed bits are truncated. +/// A negative shift amount results in a lefft shift. +pub fn shr(comptime T: type, a: T, shift_amt: var) T { + const abs_shift_amt = absCast(shift_amt); + const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); + + if (@typeOf(shift_amt).is_signed) { + if (shift_amt >= 0) { + return a >> casted_shift_amt; + } else { + return a << casted_shift_amt; + } + } + + return a >> casted_shift_amt; +} + +test "math.shr" { + testing.expect(shr(u8, 0b11111111, usize(3)) == 0b00011111); + testing.expect(shr(u8, 0b11111111, usize(8)) == 0); + testing.expect(shr(u8, 0b11111111, usize(9)) == 0); + testing.expect(shr(u8, 0b11111111, isize(-2)) == 0b11111100); +} + +/// Rotates right. Only unsigned values can be rotated. +/// Negative shift values results in shift modulo the bit count. +pub fn rotr(comptime T: type, x: T, r: var) T { + if (T.is_signed) { + @compileError("cannot rotate signed integer"); + } else { + const ar = @mod(r, T.bit_count); + return shr(T, x, ar) | shl(T, x, T.bit_count - ar); + } +} + +test "math.rotr" { + testing.expect(rotr(u8, 0b00000001, usize(0)) == 0b00000001); + testing.expect(rotr(u8, 0b00000001, usize(9)) == 0b10000000); + testing.expect(rotr(u8, 0b00000001, usize(8)) == 0b00000001); + testing.expect(rotr(u8, 0b00000001, usize(4)) == 0b00010000); + testing.expect(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); +} + +/// Rotates left. Only unsigned values can be rotated. +/// Negative shift values results in shift modulo the bit count. +pub fn rotl(comptime T: type, x: T, r: var) T { + if (T.is_signed) { + @compileError("cannot rotate signed integer"); + } else { + const ar = @mod(r, T.bit_count); + return shl(T, x, ar) | shr(T, x, T.bit_count - ar); + } +} + +test "math.rotl" { + testing.expect(rotl(u8, 0b00000001, usize(0)) == 0b00000001); + testing.expect(rotl(u8, 0b00000001, usize(9)) == 0b00000010); + testing.expect(rotl(u8, 0b00000001, usize(8)) == 0b00000001); + testing.expect(rotl(u8, 0b00000001, usize(4)) == 0b00010000); + testing.expect(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); +} + +pub fn Log2Int(comptime T: type) type { + // comptime ceil log2 + comptime var count = 0; + comptime var s = T.bit_count - 1; + inline while (s != 0) : (s >>= 1) { + count += 1; + } + + return @IntType(false, count); +} + +pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type { + assert(from <= to); + if (from == 0 and to == 0) { + return u0; + } + const is_signed = from < 0; + const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement + const base = log2(largest_positive_integer); + const upper = (1 << base) - 1; + var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; + if (is_signed) { + magnitude_bits += 1; + } + return @IntType(is_signed, magnitude_bits); +} + +test "math.IntFittingRange" { + testing.expect(IntFittingRange(0, 0) == u0); + testing.expect(IntFittingRange(0, 1) == u1); + testing.expect(IntFittingRange(0, 2) == u2); + testing.expect(IntFittingRange(0, 3) == u2); + testing.expect(IntFittingRange(0, 4) == u3); + testing.expect(IntFittingRange(0, 7) == u3); + testing.expect(IntFittingRange(0, 8) == u4); + testing.expect(IntFittingRange(0, 9) == u4); + testing.expect(IntFittingRange(0, 15) == u4); + testing.expect(IntFittingRange(0, 16) == u5); + testing.expect(IntFittingRange(0, 17) == u5); + testing.expect(IntFittingRange(0, 4095) == u12); + testing.expect(IntFittingRange(2000, 4095) == u12); + testing.expect(IntFittingRange(0, 4096) == u13); + testing.expect(IntFittingRange(2000, 4096) == u13); + testing.expect(IntFittingRange(0, 4097) == u13); + testing.expect(IntFittingRange(2000, 4097) == u13); + testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87); + testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177); + + testing.expect(IntFittingRange(-1, -1) == i1); + testing.expect(IntFittingRange(-1, 0) == i1); + testing.expect(IntFittingRange(-1, 1) == i2); + testing.expect(IntFittingRange(-2, -2) == i2); + testing.expect(IntFittingRange(-2, -1) == i2); + testing.expect(IntFittingRange(-2, 0) == i2); + testing.expect(IntFittingRange(-2, 1) == i2); + testing.expect(IntFittingRange(-2, 2) == i3); + testing.expect(IntFittingRange(-1, 2) == i3); + testing.expect(IntFittingRange(-1, 3) == i3); + testing.expect(IntFittingRange(-1, 4) == i4); + testing.expect(IntFittingRange(-1, 7) == i4); + testing.expect(IntFittingRange(-1, 8) == i5); + testing.expect(IntFittingRange(-1, 9) == i5); + testing.expect(IntFittingRange(-1, 15) == i5); + testing.expect(IntFittingRange(-1, 16) == i6); + testing.expect(IntFittingRange(-1, 17) == i6); + testing.expect(IntFittingRange(-1, 4095) == i13); + testing.expect(IntFittingRange(-4096, 4095) == i13); + testing.expect(IntFittingRange(-1, 4096) == i14); + testing.expect(IntFittingRange(-4097, 4095) == i14); + testing.expect(IntFittingRange(-1, 4097) == i14); + testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88); + testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178); +} + +test "math overflow functions" { + testOverflow(); + comptime testOverflow(); +} + +fn testOverflow() void { + testing.expect((mul(i32, 3, 4) catch unreachable) == 12); + testing.expect((add(i32, 3, 4) catch unreachable) == 7); + testing.expect((sub(i32, 3, 4) catch unreachable) == -1); + testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); +} + +pub fn absInt(x: var) !@typeOf(x) { + const T = @typeOf(x); + comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt + comptime assert(T.is_signed); // must pass a signed integer to absInt + + if (x == minInt(@typeOf(x))) { + return error.Overflow; + } else { + @setRuntimeSafety(false); + return if (x < 0) -x else x; + } +} + +test "math.absInt" { + testAbsInt(); + comptime testAbsInt(); +} +fn testAbsInt() void { + testing.expect((absInt(i32(-10)) catch unreachable) == 10); + testing.expect((absInt(i32(10)) catch unreachable) == 10); +} + +pub const absFloat = @import("fabs.zig").fabs; + +pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { + @setRuntimeSafety(false); + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; + return @divTrunc(numerator, denominator); +} + +test "math.divTrunc" { + testDivTrunc(); + comptime testDivTrunc(); +} +fn testDivTrunc() void { + testing.expect((divTrunc(i32, 5, 3) catch unreachable) == 1); + testing.expect((divTrunc(i32, -5, 3) catch unreachable) == -1); + testing.expectError(error.DivisionByZero, divTrunc(i8, -5, 0)); + testing.expectError(error.Overflow, divTrunc(i8, -128, -1)); + + testing.expect((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0); + testing.expect((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); +} + +pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { + @setRuntimeSafety(false); + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; + return @divFloor(numerator, denominator); +} + +test "math.divFloor" { + testDivFloor(); + comptime testDivFloor(); +} +fn testDivFloor() void { + testing.expect((divFloor(i32, 5, 3) catch unreachable) == 1); + testing.expect((divFloor(i32, -5, 3) catch unreachable) == -2); + testing.expectError(error.DivisionByZero, divFloor(i8, -5, 0)); + testing.expectError(error.Overflow, divFloor(i8, -128, -1)); + + testing.expect((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0); + testing.expect((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); +} + +pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { + @setRuntimeSafety(false); + if (denominator == 0) return error.DivisionByZero; + if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; + const result = @divTrunc(numerator, denominator); + if (result * denominator != numerator) return error.UnexpectedRemainder; + return result; +} + +test "math.divExact" { + testDivExact(); + comptime testDivExact(); +} +fn testDivExact() void { + testing.expect((divExact(i32, 10, 5) catch unreachable) == 2); + testing.expect((divExact(i32, -10, 5) catch unreachable) == -2); + testing.expectError(error.DivisionByZero, divExact(i8, -5, 0)); + testing.expectError(error.Overflow, divExact(i8, -128, -1)); + testing.expectError(error.UnexpectedRemainder, divExact(i32, 5, 2)); + + testing.expect((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0); + testing.expect((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0); + testing.expectError(error.UnexpectedRemainder, divExact(f32, 5.0, 2.0)); +} + +pub fn mod(comptime T: type, numerator: T, denominator: T) !T { + @setRuntimeSafety(false); + if (denominator == 0) return error.DivisionByZero; + if (denominator < 0) return error.NegativeDenominator; + return @mod(numerator, denominator); +} + +test "math.mod" { + testMod(); + comptime testMod(); +} +fn testMod() void { + testing.expect((mod(i32, -5, 3) catch unreachable) == 1); + testing.expect((mod(i32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, mod(i32, 10, -1)); + testing.expectError(error.DivisionByZero, mod(i32, 10, 0)); + + testing.expect((mod(f32, -5, 3) catch unreachable) == 1); + testing.expect((mod(f32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, mod(f32, 10, -1)); + testing.expectError(error.DivisionByZero, mod(f32, 10, 0)); +} + +pub fn rem(comptime T: type, numerator: T, denominator: T) !T { + @setRuntimeSafety(false); + if (denominator == 0) return error.DivisionByZero; + if (denominator < 0) return error.NegativeDenominator; + return @rem(numerator, denominator); +} + +test "math.rem" { + testRem(); + comptime testRem(); +} +fn testRem() void { + testing.expect((rem(i32, -5, 3) catch unreachable) == -2); + testing.expect((rem(i32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, rem(i32, 10, -1)); + testing.expectError(error.DivisionByZero, rem(i32, 10, 0)); + + testing.expect((rem(f32, -5, 3) catch unreachable) == -2); + testing.expect((rem(f32, 5, 3) catch unreachable) == 2); + testing.expectError(error.NegativeDenominator, rem(f32, 10, -1)); + testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); +} + +/// Returns the absolute value of the integer parameter. +/// Result is an unsigned integer. +pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { + const uint = @IntType(false, @typeOf(x).bit_count); + if (x >= 0) return @intCast(uint, x); + + return @intCast(uint, -(x + 1)) + 1; +} + +test "math.absCast" { + testing.expect(absCast(i32(-999)) == 999); + testing.expect(@typeOf(absCast(i32(-999))) == u32); + + testing.expect(absCast(i32(999)) == 999); + testing.expect(@typeOf(absCast(i32(999))) == u32); + + testing.expect(absCast(i32(minInt(i32))) == -minInt(i32)); + testing.expect(@typeOf(absCast(i32(minInt(i32)))) == u32); +} + +/// Returns the negation of the integer parameter. +/// Result is a signed integer. +pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { + if (@typeOf(x).is_signed) return negate(x); + + const int = @IntType(true, @typeOf(x).bit_count); + if (x > -minInt(int)) return error.Overflow; + + if (x == -minInt(int)) return minInt(int); + + return -@intCast(int, x); +} + +test "math.negateCast" { + testing.expect((negateCast(u32(999)) catch unreachable) == -999); + testing.expect(@typeOf(negateCast(u32(999)) catch unreachable) == i32); + + testing.expect((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); + testing.expect(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); + + testing.expectError(error.Overflow, negateCast(u32(maxInt(i32) + 10))); +} + +/// Cast an integer to a different integer type. If the value doesn't fit, +/// return an error. +pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { + comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer + comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer + if (maxInt(@typeOf(x)) > maxInt(T) and x > maxInt(T)) { + return error.Overflow; + } else if (minInt(@typeOf(x)) < minInt(T) and x < minInt(T)) { + return error.Overflow; + } else { + return @intCast(T, x); + } +} + +test "math.cast" { + testing.expectError(error.Overflow, cast(u8, u32(300))); + testing.expectError(error.Overflow, cast(i8, i32(-200))); + testing.expectError(error.Overflow, cast(u8, i8(-1))); + testing.expectError(error.Overflow, cast(u64, i8(-1))); + + testing.expect((try cast(u8, u32(255))) == u8(255)); + testing.expect(@typeOf(try cast(u8, u32(255))) == u8); +} + +pub const AlignCastError = error{UnalignedMemory}; + +/// Align cast a pointer but return an error if it's the wrong alignment +pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { + const addr = @ptrToInt(ptr); + if (addr % alignment != 0) { + return error.UnalignedMemory; + } + return @alignCast(alignment, ptr); +} + +pub fn floorPowerOfTwo(comptime T: type, value: T) T { + var x = value; + + comptime var i = 1; + inline while (T.bit_count > i) : (i *= 2) { + x |= (x >> i); + } + + return x - (x >> 1); +} + +test "math.floorPowerOfTwo" { + testFloorPowerOfTwo(); + comptime testFloorPowerOfTwo(); +} + +pub fn log2_int(comptime T: type, x: T) Log2Int(T) { + assert(x != 0); + return @intCast(Log2Int(T), T.bit_count - 1 - @clz(x)); +} + +pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { + assert(x != 0); + const log2_val = log2_int(T, x); + if (T(1) << log2_val == x) + return log2_val; + return log2_val + 1; +} + +test "std.math.log2_int_ceil" { + testing.expect(log2_int_ceil(u32, 1) == 0); + testing.expect(log2_int_ceil(u32, 2) == 1); + testing.expect(log2_int_ceil(u32, 3) == 2); + testing.expect(log2_int_ceil(u32, 4) == 2); + testing.expect(log2_int_ceil(u32, 5) == 3); + testing.expect(log2_int_ceil(u32, 6) == 3); + testing.expect(log2_int_ceil(u32, 7) == 3); + testing.expect(log2_int_ceil(u32, 8) == 3); + testing.expect(log2_int_ceil(u32, 9) == 4); + testing.expect(log2_int_ceil(u32, 10) == 4); +} + +fn testFloorPowerOfTwo() void { + testing.expect(floorPowerOfTwo(u32, 63) == 32); + testing.expect(floorPowerOfTwo(u32, 64) == 64); + testing.expect(floorPowerOfTwo(u32, 65) == 64); + testing.expect(floorPowerOfTwo(u4, 7) == 4); + testing.expect(floorPowerOfTwo(u4, 8) == 8); + testing.expect(floorPowerOfTwo(u4, 9) == 8); +} + +pub fn lossyCast(comptime T: type, value: var) T { + switch (@typeInfo(@typeOf(value))) { + builtin.TypeId.Int => return @intToFloat(T, value), + builtin.TypeId.Float => return @floatCast(T, value), + builtin.TypeId.ComptimeInt => return T(value), + builtin.TypeId.ComptimeFloat => return T(value), + else => @compileError("bad type"), + } +} + +test "math.f64_min" { + const f64_min_u64 = 0x0010000000000000; + const fmin: f64 = f64_min; + testing.expect(@bitCast(u64, fmin) == f64_min_u64); +} + +pub fn maxInt(comptime T: type) comptime_int { + const info = @typeInfo(T); + const bit_count = info.Int.bits; + if (bit_count == 0) return 0; + return (1 << (bit_count - @boolToInt(info.Int.is_signed))) - 1; +} + +pub fn minInt(comptime T: type) comptime_int { + const info = @typeInfo(T); + const bit_count = info.Int.bits; + if (!info.Int.is_signed) return 0; + if (bit_count == 0) return 0; + return -(1 << (bit_count - 1)); +} + +test "minInt and maxInt" { + testing.expect(maxInt(u0) == 0); + testing.expect(maxInt(u1) == 1); + testing.expect(maxInt(u8) == 255); + testing.expect(maxInt(u16) == 65535); + testing.expect(maxInt(u32) == 4294967295); + testing.expect(maxInt(u64) == 18446744073709551615); + testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); + + testing.expect(maxInt(i0) == 0); + testing.expect(maxInt(i1) == 0); + testing.expect(maxInt(i8) == 127); + testing.expect(maxInt(i16) == 32767); + testing.expect(maxInt(i32) == 2147483647); + testing.expect(maxInt(i63) == 4611686018427387903); + testing.expect(maxInt(i64) == 9223372036854775807); + testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); + + testing.expect(minInt(u0) == 0); + testing.expect(minInt(u1) == 0); + testing.expect(minInt(u8) == 0); + testing.expect(minInt(u16) == 0); + testing.expect(minInt(u32) == 0); + testing.expect(minInt(u63) == 0); + testing.expect(minInt(u64) == 0); + testing.expect(minInt(u128) == 0); + + testing.expect(minInt(i0) == 0); + testing.expect(minInt(i1) == -1); + testing.expect(minInt(i8) == -128); + testing.expect(minInt(i16) == -32768); + testing.expect(minInt(i32) == -2147483648); + testing.expect(minInt(i63) == -4611686018427387904); + testing.expect(minInt(i64) == -9223372036854775808); + testing.expect(minInt(i128) == -170141183460469231731687303715884105728); +} + +test "max value type" { + // If the type of maxInt(i32) was i32 then this implicit cast to + // u32 would not work. But since the value is a number literal, + // it works fine. + const x: u32 = maxInt(i32); + testing.expect(x == 2147483647); +} diff --git a/std/math/acos.zig b/std/math/acos.zig index 734f7a8651..763d9d8abd 100644 --- a/std/math/acos.zig +++ b/std/math/acos.zig @@ -2,7 +2,7 @@ // // - acos(x) = nan if x < -1 or x > 1 -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/acosh.zig b/std/math/acosh.zig index a9c4a908ef..a2a9926863 100644 --- a/std/math/acosh.zig +++ b/std/math/acosh.zig @@ -4,7 +4,7 @@ // - acosh(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/asin.zig b/std/math/asin.zig index c9dbdf704f..b01a1ac49e 100644 --- a/std/math/asin.zig +++ b/std/math/asin.zig @@ -3,7 +3,7 @@ // - asin(+-0) = +-0 // - asin(x) = nan if x < -1 or x > 1 -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/asinh.zig b/std/math/asinh.zig index 05bd8d008e..cef9bfb23f 100644 --- a/std/math/asinh.zig +++ b/std/math/asinh.zig @@ -4,7 +4,7 @@ // - asinh(+-inf) = +-inf // - asinh(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/atan.zig b/std/math/atan.zig index 72d17b4db2..ba5a11dd10 100644 --- a/std/math/atan.zig +++ b/std/math/atan.zig @@ -3,7 +3,7 @@ // - atan(+-0) = +-0 // - atan(+-inf) = +-pi/2 -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/atan2.zig b/std/math/atan2.zig index 6e1f67cfbb..7f13507402 100644 --- a/std/math/atan2.zig +++ b/std/math/atan2.zig @@ -18,7 +18,7 @@ // atan2(+inf, x) = +pi/2 // atan2(-inf, x) = -pi/2 -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/atanh.zig b/std/math/atanh.zig index f2feab0207..9056064f5a 100644 --- a/std/math/atanh.zig +++ b/std/math/atanh.zig @@ -4,7 +4,7 @@ // - atanh(x) = nan if |x| > 1 with signal // - atanh(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/big.zig b/std/math/big.zig new file mode 100644 index 0000000000..94b6d864e7 --- /dev/null +++ b/std/math/big.zig @@ -0,0 +1,5 @@ +pub use @import("big/int.zig"); + +test "math.big" { + _ = @import("big/int.zig"); +} diff --git a/std/math/big/index.zig b/std/math/big/index.zig deleted file mode 100644 index 26fa538c4f..0000000000 --- a/std/math/big/index.zig +++ /dev/null @@ -1,5 +0,0 @@ -pub use @import("int.zig"); - -test "math.big" { - _ = @import("int.zig"); -} diff --git a/std/math/big/int.zig b/std/math/big/int.zig index f21e5df8aa..7fca4a147f 100644 --- a/std/math/big/int.zig +++ b/std/math/big/int.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const builtin = @import("builtin"); const debug = std.debug; const testing = std.testing; diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig index 957e026af4..9497255269 100644 --- a/std/math/cbrt.zig +++ b/std/math/cbrt.zig @@ -4,7 +4,7 @@ // - cbrt(+-inf) = +-inf // - cbrt(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/ceil.zig b/std/math/ceil.zig index 5c6b98b2ca..da83e0ec59 100644 --- a/std/math/ceil.zig +++ b/std/math/ceil.zig @@ -5,7 +5,7 @@ // - ceil(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/complex.zig b/std/math/complex.zig new file mode 100644 index 0000000000..cc0573b227 --- /dev/null +++ b/std/math/complex.zig @@ -0,0 +1,171 @@ +const std = @import("../std.zig"); +const testing = std.testing; +const math = std.math; + +pub const abs = @import("complex/abs.zig").abs; +pub const acosh = @import("complex/acosh.zig").acosh; +pub const acos = @import("complex/acos.zig").acos; +pub const arg = @import("complex/arg.zig").arg; +pub const asinh = @import("complex/asinh.zig").asinh; +pub const asin = @import("complex/asin.zig").asin; +pub const atanh = @import("complex/atanh.zig").atanh; +pub const atan = @import("complex/atan.zig").atan; +pub const conj = @import("complex/conj.zig").conj; +pub const cosh = @import("complex/cosh.zig").cosh; +pub const cos = @import("complex/cos.zig").cos; +pub const exp = @import("complex/exp.zig").exp; +pub const log = @import("complex/log.zig").log; +pub const pow = @import("complex/pow.zig").pow; +pub const proj = @import("complex/proj.zig").proj; +pub const sinh = @import("complex/sinh.zig").sinh; +pub const sin = @import("complex/sin.zig").sin; +pub const sqrt = @import("complex/sqrt.zig").sqrt; +pub const tanh = @import("complex/tanh.zig").tanh; +pub const tan = @import("complex/tan.zig").tan; + +pub fn Complex(comptime T: type) type { + return struct { + const Self = @This(); + + re: T, + im: T, + + pub fn new(re: T, im: T) Self { + return Self{ + .re = re, + .im = im, + }; + } + + pub fn add(self: Self, other: Self) Self { + return Self{ + .re = self.re + other.re, + .im = self.im + other.im, + }; + } + + pub fn sub(self: Self, other: Self) Self { + return Self{ + .re = self.re - other.re, + .im = self.im - other.im, + }; + } + + pub fn mul(self: Self, other: Self) Self { + return Self{ + .re = self.re * other.re - self.im * other.im, + .im = self.im * other.re + self.re * other.im, + }; + } + + 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; + const den = other.re * other.re + other.im * other.im; + + return Self{ + .re = re_num / den, + .im = im_num / den, + }; + } + + pub fn conjugate(self: Self) Self { + return Self{ + .re = self.re, + .im = -self.im, + }; + } + + pub fn reciprocal(self: Self) Self { + const m = self.re * self.re + self.im * self.im; + return Self{ + .re = self.re / m, + .im = -self.im / m, + }; + } + + pub fn magnitude(self: Self) T { + return math.sqrt(self.re * self.re + self.im * self.im); + } + }; +} + +const epsilon = 0.0001; + +test "complex.add" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.add(b); + + testing.expect(c.re == 7 and c.im == 10); +} + +test "complex.sub" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.sub(b); + + testing.expect(c.re == 3 and c.im == -4); +} + +test "complex.mul" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.mul(b); + + testing.expect(c.re == -11 and c.im == 41); +} + +test "complex.div" { + const a = Complex(f32).new(5, 3); + const b = Complex(f32).new(2, 7); + const c = a.div(b); + + testing.expect(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and + math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); +} + +test "complex.conjugate" { + const a = Complex(f32).new(5, 3); + const c = a.conjugate(); + + testing.expect(c.re == 5 and c.im == -3); +} + +test "complex.reciprocal" { + const a = Complex(f32).new(5, 3); + const c = a.reciprocal(); + + testing.expect(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and + math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); +} + +test "complex.magnitude" { + const a = Complex(f32).new(5, 3); + const c = a.magnitude(); + + testing.expect(math.approxEq(f32, c, 5.83095, epsilon)); +} + +test "complex.cmath" { + _ = @import("complex/abs.zig"); + _ = @import("complex/acosh.zig"); + _ = @import("complex/acos.zig"); + _ = @import("complex/arg.zig"); + _ = @import("complex/asinh.zig"); + _ = @import("complex/asin.zig"); + _ = @import("complex/atanh.zig"); + _ = @import("complex/atan.zig"); + _ = @import("complex/conj.zig"); + _ = @import("complex/cosh.zig"); + _ = @import("complex/cos.zig"); + _ = @import("complex/exp.zig"); + _ = @import("complex/log.zig"); + _ = @import("complex/pow.zig"); + _ = @import("complex/proj.zig"); + _ = @import("complex/sinh.zig"); + _ = @import("complex/sin.zig"); + _ = @import("complex/sqrt.zig"); + _ = @import("complex/tanh.zig"); + _ = @import("complex/tan.zig"); +} diff --git a/std/math/complex/abs.zig b/std/math/complex/abs.zig index 245d67d4c5..e1368d6ef6 100644 --- a/std/math/complex/abs.zig +++ b/std/math/complex/abs.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/acos.zig b/std/math/complex/acos.zig index 1b314bc31a..8aed26a71b 100644 --- a/std/math/complex/acos.zig +++ b/std/math/complex/acos.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/acosh.zig b/std/math/complex/acosh.zig index 0e4c0121f4..e72bf431fe 100644 --- a/std/math/complex/acosh.zig +++ b/std/math/complex/acosh.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/arg.zig b/std/math/complex/arg.zig index be117a5940..0a2441d1fd 100644 --- a/std/math/complex/arg.zig +++ b/std/math/complex/arg.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/asin.zig b/std/math/complex/asin.zig index cf802ea206..6be775d748 100644 --- a/std/math/complex/asin.zig +++ b/std/math/complex/asin.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/asinh.zig b/std/math/complex/asinh.zig index 0386a636b0..8e09036750 100644 --- a/std/math/complex/asinh.zig +++ b/std/math/complex/asinh.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/atan.zig b/std/math/complex/atan.zig index 8e60e58e43..6b83adbd97 100644 --- a/std/math/complex/atan.zig +++ b/std/math/complex/atan.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/atanh.zig b/std/math/complex/atanh.zig index 5b18fe1992..8edfb6e78e 100644 --- a/std/math/complex/atanh.zig +++ b/std/math/complex/atanh.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/conj.zig b/std/math/complex/conj.zig index 143543f9e7..7a42d365ea 100644 --- a/std/math/complex/conj.zig +++ b/std/math/complex/conj.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/cos.zig b/std/math/complex/cos.zig index 658d19c3b6..71f9603c75 100644 --- a/std/math/complex/cos.zig +++ b/std/math/complex/cos.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/cosh.zig b/std/math/complex/cosh.zig index 5ce10b03f8..9806e41170 100644 --- a/std/math/complex/cosh.zig +++ b/std/math/complex/cosh.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/exp.zig b/std/math/complex/exp.zig index 9ded698d08..c74ac2fc08 100644 --- a/std/math/complex/exp.zig +++ b/std/math/complex/exp.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/index.zig b/std/math/complex/index.zig deleted file mode 100644 index ffbf14d83e..0000000000 --- a/std/math/complex/index.zig +++ /dev/null @@ -1,171 +0,0 @@ -const std = @import("../../index.zig"); -const testing = std.testing; -const math = std.math; - -pub const abs = @import("abs.zig").abs; -pub const acosh = @import("acosh.zig").acosh; -pub const acos = @import("acos.zig").acos; -pub const arg = @import("arg.zig").arg; -pub const asinh = @import("asinh.zig").asinh; -pub const asin = @import("asin.zig").asin; -pub const atanh = @import("atanh.zig").atanh; -pub const atan = @import("atan.zig").atan; -pub const conj = @import("conj.zig").conj; -pub const cosh = @import("cosh.zig").cosh; -pub const cos = @import("cos.zig").cos; -pub const exp = @import("exp.zig").exp; -pub const log = @import("log.zig").log; -pub const pow = @import("pow.zig").pow; -pub const proj = @import("proj.zig").proj; -pub const sinh = @import("sinh.zig").sinh; -pub const sin = @import("sin.zig").sin; -pub const sqrt = @import("sqrt.zig").sqrt; -pub const tanh = @import("tanh.zig").tanh; -pub const tan = @import("tan.zig").tan; - -pub fn Complex(comptime T: type) type { - return struct { - const Self = @This(); - - re: T, - im: T, - - pub fn new(re: T, im: T) Self { - return Self{ - .re = re, - .im = im, - }; - } - - pub fn add(self: Self, other: Self) Self { - return Self{ - .re = self.re + other.re, - .im = self.im + other.im, - }; - } - - pub fn sub(self: Self, other: Self) Self { - return Self{ - .re = self.re - other.re, - .im = self.im - other.im, - }; - } - - pub fn mul(self: Self, other: Self) Self { - return Self{ - .re = self.re * other.re - self.im * other.im, - .im = self.im * other.re + self.re * other.im, - }; - } - - 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; - const den = other.re * other.re + other.im * other.im; - - return Self{ - .re = re_num / den, - .im = im_num / den, - }; - } - - pub fn conjugate(self: Self) Self { - return Self{ - .re = self.re, - .im = -self.im, - }; - } - - pub fn reciprocal(self: Self) Self { - const m = self.re * self.re + self.im * self.im; - return Self{ - .re = self.re / m, - .im = -self.im / m, - }; - } - - pub fn magnitude(self: Self) T { - return math.sqrt(self.re * self.re + self.im * self.im); - } - }; -} - -const epsilon = 0.0001; - -test "complex.add" { - const a = Complex(f32).new(5, 3); - const b = Complex(f32).new(2, 7); - const c = a.add(b); - - testing.expect(c.re == 7 and c.im == 10); -} - -test "complex.sub" { - const a = Complex(f32).new(5, 3); - const b = Complex(f32).new(2, 7); - const c = a.sub(b); - - testing.expect(c.re == 3 and c.im == -4); -} - -test "complex.mul" { - const a = Complex(f32).new(5, 3); - const b = Complex(f32).new(2, 7); - const c = a.mul(b); - - testing.expect(c.re == -11 and c.im == 41); -} - -test "complex.div" { - const a = Complex(f32).new(5, 3); - const b = Complex(f32).new(2, 7); - const c = a.div(b); - - testing.expect(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and - math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); -} - -test "complex.conjugate" { - const a = Complex(f32).new(5, 3); - const c = a.conjugate(); - - testing.expect(c.re == 5 and c.im == -3); -} - -test "complex.reciprocal" { - const a = Complex(f32).new(5, 3); - const c = a.reciprocal(); - - testing.expect(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and - math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); -} - -test "complex.magnitude" { - const a = Complex(f32).new(5, 3); - const c = a.magnitude(); - - testing.expect(math.approxEq(f32, c, 5.83095, epsilon)); -} - -test "complex.cmath" { - _ = @import("abs.zig"); - _ = @import("acosh.zig"); - _ = @import("acos.zig"); - _ = @import("arg.zig"); - _ = @import("asinh.zig"); - _ = @import("asin.zig"); - _ = @import("atanh.zig"); - _ = @import("atan.zig"); - _ = @import("conj.zig"); - _ = @import("cosh.zig"); - _ = @import("cos.zig"); - _ = @import("exp.zig"); - _ = @import("log.zig"); - _ = @import("pow.zig"); - _ = @import("proj.zig"); - _ = @import("sinh.zig"); - _ = @import("sin.zig"); - _ = @import("sqrt.zig"); - _ = @import("tanh.zig"); - _ = @import("tan.zig"); -} diff --git a/std/math/complex/ldexp.zig b/std/math/complex/ldexp.zig index e919ef6bec..6b4306bf77 100644 --- a/std/math/complex/ldexp.zig +++ b/std/math/complex/ldexp.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const debug = std.debug; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/log.zig b/std/math/complex/log.zig index 360bb7d21e..2b43a6970f 100644 --- a/std/math/complex/log.zig +++ b/std/math/complex/log.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/pow.zig b/std/math/complex/pow.zig index bd625626c8..9174bb3626 100644 --- a/std/math/complex/pow.zig +++ b/std/math/complex/pow.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/proj.zig b/std/math/complex/proj.zig index d31006129e..aadcff6ff6 100644 --- a/std/math/complex/proj.zig +++ b/std/math/complex/proj.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/sin.zig b/std/math/complex/sin.zig index 9d54ab0969..049631f31e 100644 --- a/std/math/complex/sin.zig +++ b/std/math/complex/sin.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/sinh.zig b/std/math/complex/sinh.zig index 469ea6067a..0b656e5354 100644 --- a/std/math/complex/sinh.zig +++ b/std/math/complex/sinh.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/sqrt.zig b/std/math/complex/sqrt.zig index 60f6061baa..e935d0b238 100644 --- a/std/math/complex/sqrt.zig +++ b/std/math/complex/sqrt.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/tan.zig b/std/math/complex/tan.zig index db34580598..45e2873eb6 100644 --- a/std/math/complex/tan.zig +++ b/std/math/complex/tan.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/complex/tanh.zig b/std/math/complex/tanh.zig index 03ab431312..de905ee3f6 100644 --- a/std/math/complex/tanh.zig +++ b/std/math/complex/tanh.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const testing = std.testing; const math = std.math; const cmath = math.complex; diff --git a/std/math/copysign.zig b/std/math/copysign.zig index dbf8f6e1ef..ff8140b3ab 100644 --- a/std/math/copysign.zig +++ b/std/math/copysign.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/cos.zig b/std/math/cos.zig index 7783ddc09b..9479482894 100644 --- a/std/math/cos.zig +++ b/std/math/cos.zig @@ -4,7 +4,7 @@ // - cos(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/cosh.zig b/std/math/cosh.zig index 57c5eef828..eb3082e5fa 100644 --- a/std/math/cosh.zig +++ b/std/math/cosh.zig @@ -5,7 +5,7 @@ // - cosh(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expo2 = @import("expo2.zig").expo2; const expect = std.testing.expect; diff --git a/std/math/exp.zig b/std/math/exp.zig index cf8fd62d80..aabccffd0b 100644 --- a/std/math/exp.zig +++ b/std/math/exp.zig @@ -3,7 +3,7 @@ // - exp(+inf) = +inf // - exp(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const assert = std.debug.assert; const builtin = @import("builtin"); diff --git a/std/math/exp2.zig b/std/math/exp2.zig index ba0001a09a..392d45bf68 100644 --- a/std/math/exp2.zig +++ b/std/math/exp2.zig @@ -3,7 +3,7 @@ // - exp2(+inf) = +inf // - exp2(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/expm1.zig b/std/math/expm1.zig index ba00ec2561..b83cc3e82e 100644 --- a/std/math/expm1.zig +++ b/std/math/expm1.zig @@ -5,7 +5,7 @@ // - expm1(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/expo2.zig b/std/math/expo2.zig index 15b80c710c..57e2bf34c9 100644 --- a/std/math/expo2.zig +++ b/std/math/expo2.zig @@ -1,4 +1,4 @@ -const math = @import("index.zig"); +const math = @import("../math.zig"); pub fn expo2(x: var) @typeOf(x) { const T = @typeOf(x); diff --git a/std/math/fabs.zig b/std/math/fabs.zig index a605f4f33f..e30f788ae7 100644 --- a/std/math/fabs.zig +++ b/std/math/fabs.zig @@ -3,7 +3,7 @@ // - fabs(+-inf) = +inf // - fabs(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/floor.zig b/std/math/floor.zig index c7c12e37ba..ab45a8fee7 100644 --- a/std/math/floor.zig +++ b/std/math/floor.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const expect = std.testing.expect; -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; pub fn floor(x: var) @typeOf(x) { diff --git a/std/math/fma.zig b/std/math/fma.zig index b084cf3cbd..a317bc96de 100644 --- a/std/math/fma.zig +++ b/std/math/fma.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/frexp.zig b/std/math/frexp.zig index 35f3e081a9..35eec315d9 100644 --- a/std/math/frexp.zig +++ b/std/math/frexp.zig @@ -4,7 +4,7 @@ // - frexp(+-inf) = +-inf, 0 // - frexp(nan) = nan, undefined -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/hypot.zig b/std/math/hypot.zig index dea11e0a61..fddbe7c068 100644 --- a/std/math/hypot.zig +++ b/std/math/hypot.zig @@ -5,7 +5,7 @@ // - hypot(nan, y) = nan // - hypot(x, nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig index e7b6485357..2dfd42a62c 100644 --- a/std/math/ilogb.zig +++ b/std/math/ilogb.zig @@ -4,7 +4,7 @@ // - ilogb(0) = maxInt(i32) // - ilogb(nan) = maxInt(i32) -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/index.zig b/std/math/index.zig deleted file mode 100644 index 89f228d35e..0000000000 --- a/std/math/index.zig +++ /dev/null @@ -1,797 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("../index.zig"); -const TypeId = builtin.TypeId; -const assert = std.debug.assert; -const testing = std.testing; - -pub const e = 2.71828182845904523536028747135266249775724709369995; -pub const pi = 3.14159265358979323846264338327950288419716939937510; - -// From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) -pub const f128_true_min = @bitCast(f128, u128(0x00000000000000000000000000000001)); -pub const f128_min = @bitCast(f128, u128(0x00010000000000000000000000000000)); -pub const f128_max = @bitCast(f128, u128(0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); -pub const f128_epsilon = @bitCast(f128, u128(0x3F8F0000000000000000000000000000)); -pub const f128_toint = 1.0 / f128_epsilon; - -// float.h details -pub const f64_true_min = 4.94065645841246544177e-324; -pub const f64_min = 2.2250738585072014e-308; -pub const f64_max = 1.79769313486231570815e+308; -pub const f64_epsilon = 2.22044604925031308085e-16; -pub const f64_toint = 1.0 / f64_epsilon; - -pub const f32_true_min = 1.40129846432481707092e-45; -pub const f32_min = 1.17549435082228750797e-38; -pub const f32_max = 3.40282346638528859812e+38; -pub const f32_epsilon = 1.1920928955078125e-07; -pub const f32_toint = 1.0 / f32_epsilon; - -pub const f16_true_min = 0.000000059604644775390625; // 2**-24 -pub const f16_min = 0.00006103515625; // 2**-14 -pub const f16_max = 65504; -pub const f16_epsilon = 0.0009765625; // 2**-10 -pub const f16_toint = 1.0 / f16_epsilon; - -pub const nan_u16 = u16(0x7C01); -pub const nan_f16 = @bitCast(f16, nan_u16); - -pub const inf_u16 = u16(0x7C00); -pub const inf_f16 = @bitCast(f16, inf_u16); - -pub const nan_u32 = u32(0x7F800001); -pub const nan_f32 = @bitCast(f32, nan_u32); - -pub const inf_u32 = u32(0x7F800000); -pub const inf_f32 = @bitCast(f32, inf_u32); - -pub const nan_u64 = u64(0x7FF << 52) | 1; -pub const nan_f64 = @bitCast(f64, nan_u64); - -pub const inf_u64 = u64(0x7FF << 52); -pub const inf_f64 = @bitCast(f64, inf_u64); - -pub const nan_u128 = u128(0x7fff0000000000000000000000000001); -pub const nan_f128 = @bitCast(f128, nan_u128); - -pub const inf_u128 = u128(0x7fff0000000000000000000000000000); -pub const inf_f128 = @bitCast(f128, inf_u128); - -pub const nan = @import("nan.zig").nan; -pub const snan = @import("nan.zig").snan; -pub const inf = @import("inf.zig").inf; - -pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { - assert(@typeId(T) == TypeId.Float); - return fabs(x - y) < epsilon; -} - -// TODO: Hide the following in an internal module. -pub fn forceEval(value: var) void { - const T = @typeOf(value); - switch (T) { - f16 => { - var x: f16 = undefined; - const p = @ptrCast(*volatile f16, &x); - p.* = x; - }, - f32 => { - var x: f32 = undefined; - const p = @ptrCast(*volatile f32, &x); - p.* = x; - }, - f64 => { - var x: f64 = undefined; - const p = @ptrCast(*volatile f64, &x); - p.* = x; - }, - else => { - @compileError("forceEval not implemented for " ++ @typeName(T)); - }, - } -} - -pub fn raiseInvalid() void { - // Raise INVALID fpu exception -} - -pub fn raiseUnderflow() void { - // Raise UNDERFLOW fpu exception -} - -pub fn raiseOverflow() void { - // Raise OVERFLOW fpu exception -} - -pub fn raiseInexact() void { - // Raise INEXACT fpu exception -} - -pub fn raiseDivByZero() void { - // Raise INEXACT fpu exception -} - -pub const isNan = @import("isnan.zig").isNan; -pub const isSignalNan = @import("isnan.zig").isSignalNan; -pub const fabs = @import("fabs.zig").fabs; -pub const ceil = @import("ceil.zig").ceil; -pub const floor = @import("floor.zig").floor; -pub const trunc = @import("trunc.zig").trunc; -pub const round = @import("round.zig").round; -pub const frexp = @import("frexp.zig").frexp; -pub const frexp32_result = @import("frexp.zig").frexp32_result; -pub const frexp64_result = @import("frexp.zig").frexp64_result; -pub const modf = @import("modf.zig").modf; -pub const modf32_result = @import("modf.zig").modf32_result; -pub const modf64_result = @import("modf.zig").modf64_result; -pub const copysign = @import("copysign.zig").copysign; -pub const isFinite = @import("isfinite.zig").isFinite; -pub const isInf = @import("isinf.zig").isInf; -pub const isPositiveInf = @import("isinf.zig").isPositiveInf; -pub const isNegativeInf = @import("isinf.zig").isNegativeInf; -pub const isNormal = @import("isnormal.zig").isNormal; -pub const signbit = @import("signbit.zig").signbit; -pub const scalbn = @import("scalbn.zig").scalbn; -pub const pow = @import("pow.zig").pow; -pub const powi = @import("powi.zig").powi; -pub const sqrt = @import("sqrt.zig").sqrt; -pub const cbrt = @import("cbrt.zig").cbrt; -pub const acos = @import("acos.zig").acos; -pub const asin = @import("asin.zig").asin; -pub const atan = @import("atan.zig").atan; -pub const atan2 = @import("atan2.zig").atan2; -pub const hypot = @import("hypot.zig").hypot; -pub const exp = @import("exp.zig").exp; -pub const exp2 = @import("exp2.zig").exp2; -pub const expm1 = @import("expm1.zig").expm1; -pub const ilogb = @import("ilogb.zig").ilogb; -pub const ln = @import("ln.zig").ln; -pub const log = @import("log.zig").log; -pub const log2 = @import("log2.zig").log2; -pub const log10 = @import("log10.zig").log10; -pub const log1p = @import("log1p.zig").log1p; -pub const fma = @import("fma.zig").fma; -pub const asinh = @import("asinh.zig").asinh; -pub const acosh = @import("acosh.zig").acosh; -pub const atanh = @import("atanh.zig").atanh; -pub const sinh = @import("sinh.zig").sinh; -pub const cosh = @import("cosh.zig").cosh; -pub const tanh = @import("tanh.zig").tanh; -pub const cos = @import("cos.zig").cos; -pub const sin = @import("sin.zig").sin; -pub const tan = @import("tan.zig").tan; - -pub const complex = @import("complex/index.zig"); -pub const Complex = complex.Complex; - -pub const big = @import("big/index.zig"); - -test "math" { - _ = @import("nan.zig"); - _ = @import("isnan.zig"); - _ = @import("fabs.zig"); - _ = @import("ceil.zig"); - _ = @import("floor.zig"); - _ = @import("trunc.zig"); - _ = @import("round.zig"); - _ = @import("frexp.zig"); - _ = @import("modf.zig"); - _ = @import("copysign.zig"); - _ = @import("isfinite.zig"); - _ = @import("isinf.zig"); - _ = @import("isnormal.zig"); - _ = @import("signbit.zig"); - _ = @import("scalbn.zig"); - _ = @import("pow.zig"); - _ = @import("powi.zig"); - _ = @import("sqrt.zig"); - _ = @import("cbrt.zig"); - _ = @import("acos.zig"); - _ = @import("asin.zig"); - _ = @import("atan.zig"); - _ = @import("atan2.zig"); - _ = @import("hypot.zig"); - _ = @import("exp.zig"); - _ = @import("exp2.zig"); - _ = @import("expm1.zig"); - _ = @import("ilogb.zig"); - _ = @import("ln.zig"); - _ = @import("log.zig"); - _ = @import("log2.zig"); - _ = @import("log10.zig"); - _ = @import("log1p.zig"); - _ = @import("fma.zig"); - _ = @import("asinh.zig"); - _ = @import("acosh.zig"); - _ = @import("atanh.zig"); - _ = @import("sinh.zig"); - _ = @import("cosh.zig"); - _ = @import("tanh.zig"); - _ = @import("sin.zig"); - _ = @import("cos.zig"); - _ = @import("tan.zig"); - - _ = @import("complex/index.zig"); - - _ = @import("big/index.zig"); -} - -pub fn floatMantissaBits(comptime T: type) comptime_int { - assert(@typeId(T) == builtin.TypeId.Float); - - return switch (T.bit_count) { - 16 => 10, - 32 => 23, - 64 => 52, - 80 => 64, - 128 => 112, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; -} - -pub fn floatExponentBits(comptime T: type) comptime_int { - assert(@typeId(T) == builtin.TypeId.Float); - - return switch (T.bit_count) { - 16 => 5, - 32 => 8, - 64 => 11, - 80 => 15, - 128 => 15, - else => @compileError("unknown floating point type " ++ @typeName(T)), - }; -} - -pub fn min(x: var, y: var) @typeOf(x + y) { - return if (x < y) x else y; -} - -test "math.min" { - testing.expect(min(i32(-1), i32(2)) == -1); -} - -pub fn max(x: var, y: var) @typeOf(x + y) { - return if (x > y) x else y; -} - -test "math.max" { - testing.expect(max(i32(-1), i32(2)) == 2); -} - -pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { - var answer: T = undefined; - return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; -} - -pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) { - var answer: T = undefined; - return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; -} - -pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { - var answer: T = undefined; - return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; -} - -pub fn negate(x: var) !@typeOf(x) { - return sub(@typeOf(x), 0, x); -} - -pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { - var answer: T = undefined; - return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer; -} - -/// Shifts left. Overflowed bits are truncated. -/// A negative shift amount results in a right shift. -pub fn shl(comptime T: type, a: T, shift_amt: var) T { - const abs_shift_amt = absCast(shift_amt); - const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); - - if (@typeOf(shift_amt).is_signed) { - if (shift_amt >= 0) { - return a << casted_shift_amt; - } else { - return a >> casted_shift_amt; - } - } - - return a << casted_shift_amt; -} - -test "math.shl" { - testing.expect(shl(u8, 0b11111111, usize(3)) == 0b11111000); - testing.expect(shl(u8, 0b11111111, usize(8)) == 0); - testing.expect(shl(u8, 0b11111111, usize(9)) == 0); - testing.expect(shl(u8, 0b11111111, isize(-2)) == 0b00111111); -} - -/// Shifts right. Overflowed bits are truncated. -/// A negative shift amount results in a lefft shift. -pub fn shr(comptime T: type, a: T, shift_amt: var) T { - const abs_shift_amt = absCast(shift_amt); - const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); - - if (@typeOf(shift_amt).is_signed) { - if (shift_amt >= 0) { - return a >> casted_shift_amt; - } else { - return a << casted_shift_amt; - } - } - - return a >> casted_shift_amt; -} - -test "math.shr" { - testing.expect(shr(u8, 0b11111111, usize(3)) == 0b00011111); - testing.expect(shr(u8, 0b11111111, usize(8)) == 0); - testing.expect(shr(u8, 0b11111111, usize(9)) == 0); - testing.expect(shr(u8, 0b11111111, isize(-2)) == 0b11111100); -} - -/// Rotates right. Only unsigned values can be rotated. -/// Negative shift values results in shift modulo the bit count. -pub fn rotr(comptime T: type, x: T, r: var) T { - if (T.is_signed) { - @compileError("cannot rotate signed integer"); - } else { - const ar = @mod(r, T.bit_count); - return shr(T, x, ar) | shl(T, x, T.bit_count - ar); - } -} - -test "math.rotr" { - testing.expect(rotr(u8, 0b00000001, usize(0)) == 0b00000001); - testing.expect(rotr(u8, 0b00000001, usize(9)) == 0b10000000); - testing.expect(rotr(u8, 0b00000001, usize(8)) == 0b00000001); - testing.expect(rotr(u8, 0b00000001, usize(4)) == 0b00010000); - testing.expect(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); -} - -/// Rotates left. Only unsigned values can be rotated. -/// Negative shift values results in shift modulo the bit count. -pub fn rotl(comptime T: type, x: T, r: var) T { - if (T.is_signed) { - @compileError("cannot rotate signed integer"); - } else { - const ar = @mod(r, T.bit_count); - return shl(T, x, ar) | shr(T, x, T.bit_count - ar); - } -} - -test "math.rotl" { - testing.expect(rotl(u8, 0b00000001, usize(0)) == 0b00000001); - testing.expect(rotl(u8, 0b00000001, usize(9)) == 0b00000010); - testing.expect(rotl(u8, 0b00000001, usize(8)) == 0b00000001); - testing.expect(rotl(u8, 0b00000001, usize(4)) == 0b00010000); - testing.expect(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); -} - -pub fn Log2Int(comptime T: type) type { - // comptime ceil log2 - comptime var count = 0; - comptime var s = T.bit_count - 1; - inline while (s != 0) : (s >>= 1) { - count += 1; - } - - return @IntType(false, count); -} - -pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type { - assert(from <= to); - if (from == 0 and to == 0) { - return u0; - } - const is_signed = from < 0; - const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement - const base = log2(largest_positive_integer); - const upper = (1 << base) - 1; - var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; - if (is_signed) { - magnitude_bits += 1; - } - return @IntType(is_signed, magnitude_bits); -} - -test "math.IntFittingRange" { - testing.expect(IntFittingRange(0, 0) == u0); - testing.expect(IntFittingRange(0, 1) == u1); - testing.expect(IntFittingRange(0, 2) == u2); - testing.expect(IntFittingRange(0, 3) == u2); - testing.expect(IntFittingRange(0, 4) == u3); - testing.expect(IntFittingRange(0, 7) == u3); - testing.expect(IntFittingRange(0, 8) == u4); - testing.expect(IntFittingRange(0, 9) == u4); - testing.expect(IntFittingRange(0, 15) == u4); - testing.expect(IntFittingRange(0, 16) == u5); - testing.expect(IntFittingRange(0, 17) == u5); - testing.expect(IntFittingRange(0, 4095) == u12); - testing.expect(IntFittingRange(2000, 4095) == u12); - testing.expect(IntFittingRange(0, 4096) == u13); - testing.expect(IntFittingRange(2000, 4096) == u13); - testing.expect(IntFittingRange(0, 4097) == u13); - testing.expect(IntFittingRange(2000, 4097) == u13); - testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87); - testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177); - - testing.expect(IntFittingRange(-1, -1) == i1); - testing.expect(IntFittingRange(-1, 0) == i1); - testing.expect(IntFittingRange(-1, 1) == i2); - testing.expect(IntFittingRange(-2, -2) == i2); - testing.expect(IntFittingRange(-2, -1) == i2); - testing.expect(IntFittingRange(-2, 0) == i2); - testing.expect(IntFittingRange(-2, 1) == i2); - testing.expect(IntFittingRange(-2, 2) == i3); - testing.expect(IntFittingRange(-1, 2) == i3); - testing.expect(IntFittingRange(-1, 3) == i3); - testing.expect(IntFittingRange(-1, 4) == i4); - testing.expect(IntFittingRange(-1, 7) == i4); - testing.expect(IntFittingRange(-1, 8) == i5); - testing.expect(IntFittingRange(-1, 9) == i5); - testing.expect(IntFittingRange(-1, 15) == i5); - testing.expect(IntFittingRange(-1, 16) == i6); - testing.expect(IntFittingRange(-1, 17) == i6); - testing.expect(IntFittingRange(-1, 4095) == i13); - testing.expect(IntFittingRange(-4096, 4095) == i13); - testing.expect(IntFittingRange(-1, 4096) == i14); - testing.expect(IntFittingRange(-4097, 4095) == i14); - testing.expect(IntFittingRange(-1, 4097) == i14); - testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88); - testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178); -} - -test "math overflow functions" { - testOverflow(); - comptime testOverflow(); -} - -fn testOverflow() void { - testing.expect((mul(i32, 3, 4) catch unreachable) == 12); - testing.expect((add(i32, 3, 4) catch unreachable) == 7); - testing.expect((sub(i32, 3, 4) catch unreachable) == -1); - testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); -} - -pub fn absInt(x: var) !@typeOf(x) { - const T = @typeOf(x); - comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt - comptime assert(T.is_signed); // must pass a signed integer to absInt - - if (x == minInt(@typeOf(x))) { - return error.Overflow; - } else { - @setRuntimeSafety(false); - return if (x < 0) -x else x; - } -} - -test "math.absInt" { - testAbsInt(); - comptime testAbsInt(); -} -fn testAbsInt() void { - testing.expect((absInt(i32(-10)) catch unreachable) == 10); - testing.expect((absInt(i32(10)) catch unreachable) == 10); -} - -pub const absFloat = @import("fabs.zig").fabs; - -pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { - @setRuntimeSafety(false); - if (denominator == 0) return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; - return @divTrunc(numerator, denominator); -} - -test "math.divTrunc" { - testDivTrunc(); - comptime testDivTrunc(); -} -fn testDivTrunc() void { - testing.expect((divTrunc(i32, 5, 3) catch unreachable) == 1); - testing.expect((divTrunc(i32, -5, 3) catch unreachable) == -1); - testing.expectError(error.DivisionByZero, divTrunc(i8, -5, 0)); - testing.expectError(error.Overflow, divTrunc(i8, -128, -1)); - - testing.expect((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0); - testing.expect((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); -} - -pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { - @setRuntimeSafety(false); - if (denominator == 0) return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; - return @divFloor(numerator, denominator); -} - -test "math.divFloor" { - testDivFloor(); - comptime testDivFloor(); -} -fn testDivFloor() void { - testing.expect((divFloor(i32, 5, 3) catch unreachable) == 1); - testing.expect((divFloor(i32, -5, 3) catch unreachable) == -2); - testing.expectError(error.DivisionByZero, divFloor(i8, -5, 0)); - testing.expectError(error.Overflow, divFloor(i8, -128, -1)); - - testing.expect((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0); - testing.expect((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); -} - -pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { - @setRuntimeSafety(false); - if (denominator == 0) return error.DivisionByZero; - if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; - const result = @divTrunc(numerator, denominator); - if (result * denominator != numerator) return error.UnexpectedRemainder; - return result; -} - -test "math.divExact" { - testDivExact(); - comptime testDivExact(); -} -fn testDivExact() void { - testing.expect((divExact(i32, 10, 5) catch unreachable) == 2); - testing.expect((divExact(i32, -10, 5) catch unreachable) == -2); - testing.expectError(error.DivisionByZero, divExact(i8, -5, 0)); - testing.expectError(error.Overflow, divExact(i8, -128, -1)); - testing.expectError(error.UnexpectedRemainder, divExact(i32, 5, 2)); - - testing.expect((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0); - testing.expect((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0); - testing.expectError(error.UnexpectedRemainder, divExact(f32, 5.0, 2.0)); -} - -pub fn mod(comptime T: type, numerator: T, denominator: T) !T { - @setRuntimeSafety(false); - if (denominator == 0) return error.DivisionByZero; - if (denominator < 0) return error.NegativeDenominator; - return @mod(numerator, denominator); -} - -test "math.mod" { - testMod(); - comptime testMod(); -} -fn testMod() void { - testing.expect((mod(i32, -5, 3) catch unreachable) == 1); - testing.expect((mod(i32, 5, 3) catch unreachable) == 2); - testing.expectError(error.NegativeDenominator, mod(i32, 10, -1)); - testing.expectError(error.DivisionByZero, mod(i32, 10, 0)); - - testing.expect((mod(f32, -5, 3) catch unreachable) == 1); - testing.expect((mod(f32, 5, 3) catch unreachable) == 2); - testing.expectError(error.NegativeDenominator, mod(f32, 10, -1)); - testing.expectError(error.DivisionByZero, mod(f32, 10, 0)); -} - -pub fn rem(comptime T: type, numerator: T, denominator: T) !T { - @setRuntimeSafety(false); - if (denominator == 0) return error.DivisionByZero; - if (denominator < 0) return error.NegativeDenominator; - return @rem(numerator, denominator); -} - -test "math.rem" { - testRem(); - comptime testRem(); -} -fn testRem() void { - testing.expect((rem(i32, -5, 3) catch unreachable) == -2); - testing.expect((rem(i32, 5, 3) catch unreachable) == 2); - testing.expectError(error.NegativeDenominator, rem(i32, 10, -1)); - testing.expectError(error.DivisionByZero, rem(i32, 10, 0)); - - testing.expect((rem(f32, -5, 3) catch unreachable) == -2); - testing.expect((rem(f32, 5, 3) catch unreachable) == 2); - testing.expectError(error.NegativeDenominator, rem(f32, 10, -1)); - testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); -} - -/// Returns the absolute value of the integer parameter. -/// Result is an unsigned integer. -pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { - const uint = @IntType(false, @typeOf(x).bit_count); - if (x >= 0) return @intCast(uint, x); - - return @intCast(uint, -(x + 1)) + 1; -} - -test "math.absCast" { - testing.expect(absCast(i32(-999)) == 999); - testing.expect(@typeOf(absCast(i32(-999))) == u32); - - testing.expect(absCast(i32(999)) == 999); - testing.expect(@typeOf(absCast(i32(999))) == u32); - - testing.expect(absCast(i32(minInt(i32))) == -minInt(i32)); - testing.expect(@typeOf(absCast(i32(minInt(i32)))) == u32); -} - -/// Returns the negation of the integer parameter. -/// Result is a signed integer. -pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { - if (@typeOf(x).is_signed) return negate(x); - - const int = @IntType(true, @typeOf(x).bit_count); - if (x > -minInt(int)) return error.Overflow; - - if (x == -minInt(int)) return minInt(int); - - return -@intCast(int, x); -} - -test "math.negateCast" { - testing.expect((negateCast(u32(999)) catch unreachable) == -999); - testing.expect(@typeOf(negateCast(u32(999)) catch unreachable) == i32); - - testing.expect((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); - testing.expect(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); - - testing.expectError(error.Overflow, negateCast(u32(maxInt(i32) + 10))); -} - -/// Cast an integer to a different integer type. If the value doesn't fit, -/// return an error. -pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { - comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer - comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer - if (maxInt(@typeOf(x)) > maxInt(T) and x > maxInt(T)) { - return error.Overflow; - } else if (minInt(@typeOf(x)) < minInt(T) and x < minInt(T)) { - return error.Overflow; - } else { - return @intCast(T, x); - } -} - -test "math.cast" { - testing.expectError(error.Overflow, cast(u8, u32(300))); - testing.expectError(error.Overflow, cast(i8, i32(-200))); - testing.expectError(error.Overflow, cast(u8, i8(-1))); - testing.expectError(error.Overflow, cast(u64, i8(-1))); - - testing.expect((try cast(u8, u32(255))) == u8(255)); - testing.expect(@typeOf(try cast(u8, u32(255))) == u8); -} - -pub const AlignCastError = error{UnalignedMemory}; - -/// Align cast a pointer but return an error if it's the wrong alignment -pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { - const addr = @ptrToInt(ptr); - if (addr % alignment != 0) { - return error.UnalignedMemory; - } - return @alignCast(alignment, ptr); -} - -pub fn floorPowerOfTwo(comptime T: type, value: T) T { - var x = value; - - comptime var i = 1; - inline while (T.bit_count > i) : (i *= 2) { - x |= (x >> i); - } - - return x - (x >> 1); -} - -test "math.floorPowerOfTwo" { - testFloorPowerOfTwo(); - comptime testFloorPowerOfTwo(); -} - -pub fn log2_int(comptime T: type, x: T) Log2Int(T) { - assert(x != 0); - return @intCast(Log2Int(T), T.bit_count - 1 - @clz(x)); -} - -pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { - assert(x != 0); - const log2_val = log2_int(T, x); - if (T(1) << log2_val == x) - return log2_val; - return log2_val + 1; -} - -test "std.math.log2_int_ceil" { - testing.expect(log2_int_ceil(u32, 1) == 0); - testing.expect(log2_int_ceil(u32, 2) == 1); - testing.expect(log2_int_ceil(u32, 3) == 2); - testing.expect(log2_int_ceil(u32, 4) == 2); - testing.expect(log2_int_ceil(u32, 5) == 3); - testing.expect(log2_int_ceil(u32, 6) == 3); - testing.expect(log2_int_ceil(u32, 7) == 3); - testing.expect(log2_int_ceil(u32, 8) == 3); - testing.expect(log2_int_ceil(u32, 9) == 4); - testing.expect(log2_int_ceil(u32, 10) == 4); -} - -fn testFloorPowerOfTwo() void { - testing.expect(floorPowerOfTwo(u32, 63) == 32); - testing.expect(floorPowerOfTwo(u32, 64) == 64); - testing.expect(floorPowerOfTwo(u32, 65) == 64); - testing.expect(floorPowerOfTwo(u4, 7) == 4); - testing.expect(floorPowerOfTwo(u4, 8) == 8); - testing.expect(floorPowerOfTwo(u4, 9) == 8); -} - -pub fn lossyCast(comptime T: type, value: var) T { - switch (@typeInfo(@typeOf(value))) { - builtin.TypeId.Int => return @intToFloat(T, value), - builtin.TypeId.Float => return @floatCast(T, value), - builtin.TypeId.ComptimeInt => return T(value), - builtin.TypeId.ComptimeFloat => return T(value), - else => @compileError("bad type"), - } -} - -test "math.f64_min" { - const f64_min_u64 = 0x0010000000000000; - const fmin: f64 = f64_min; - testing.expect(@bitCast(u64, fmin) == f64_min_u64); -} - -pub fn maxInt(comptime T: type) comptime_int { - const info = @typeInfo(T); - const bit_count = info.Int.bits; - if (bit_count == 0) return 0; - return (1 << (bit_count - @boolToInt(info.Int.is_signed))) - 1; -} - -pub fn minInt(comptime T: type) comptime_int { - const info = @typeInfo(T); - const bit_count = info.Int.bits; - if (!info.Int.is_signed) return 0; - if (bit_count == 0) return 0; - return -(1 << (bit_count - 1)); -} - -test "minInt and maxInt" { - testing.expect(maxInt(u0) == 0); - testing.expect(maxInt(u1) == 1); - testing.expect(maxInt(u8) == 255); - testing.expect(maxInt(u16) == 65535); - testing.expect(maxInt(u32) == 4294967295); - testing.expect(maxInt(u64) == 18446744073709551615); - testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); - - testing.expect(maxInt(i0) == 0); - testing.expect(maxInt(i1) == 0); - testing.expect(maxInt(i8) == 127); - testing.expect(maxInt(i16) == 32767); - testing.expect(maxInt(i32) == 2147483647); - testing.expect(maxInt(i63) == 4611686018427387903); - testing.expect(maxInt(i64) == 9223372036854775807); - testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); - - testing.expect(minInt(u0) == 0); - testing.expect(minInt(u1) == 0); - testing.expect(minInt(u8) == 0); - testing.expect(minInt(u16) == 0); - testing.expect(minInt(u32) == 0); - testing.expect(minInt(u63) == 0); - testing.expect(minInt(u64) == 0); - testing.expect(minInt(u128) == 0); - - testing.expect(minInt(i0) == 0); - testing.expect(minInt(i1) == -1); - testing.expect(minInt(i8) == -128); - testing.expect(minInt(i16) == -32768); - testing.expect(minInt(i32) == -2147483648); - testing.expect(minInt(i63) == -4611686018427387904); - testing.expect(minInt(i64) == -9223372036854775808); - testing.expect(minInt(i128) == -170141183460469231731687303715884105728); -} - -test "max value type" { - // If the type of maxInt(i32) was i32 then this implicit cast to - // u32 would not work. But since the value is a number literal, - // it works fine. - const x: u32 = maxInt(i32); - testing.expect(x == 2147483647); -} diff --git a/std/math/inf.zig b/std/math/inf.zig index fb7a3489c5..e1bfbb197a 100644 --- a/std/math/inf.zig +++ b/std/math/inf.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; pub fn inf(comptime T: type) T { diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig index bf1c9ac63c..ee8a5ff590 100644 --- a/std/math/isfinite.zig +++ b/std/math/isfinite.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/isinf.zig b/std/math/isinf.zig index b1e3f7795e..1b1759bd36 100644 --- a/std/math/isinf.zig +++ b/std/math/isinf.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/isnan.zig b/std/math/isnan.zig index e8b03a1e34..7645528ea9 100644 --- a/std/math/isnan.zig +++ b/std/math/isnan.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig index 2c57aea7a9..cddcada1d3 100644 --- a/std/math/isnormal.zig +++ b/std/math/isnormal.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/ln.zig b/std/math/ln.zig index 257ce8054f..82b212f00f 100644 --- a/std/math/ln.zig +++ b/std/math/ln.zig @@ -5,7 +5,7 @@ // - ln(x) = nan if x < 0 // - ln(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const builtin = @import("builtin"); diff --git a/std/math/log.zig b/std/math/log.zig index 21cffcc078..100dc2fb7f 100644 --- a/std/math/log.zig +++ b/std/math/log.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const builtin = @import("builtin"); const TypeId = builtin.TypeId; diff --git a/std/math/log10.zig b/std/math/log10.zig index 8055f71280..7311778ddd 100644 --- a/std/math/log10.zig +++ b/std/math/log10.zig @@ -5,7 +5,7 @@ // - log10(x) = nan if x < 0 // - log10(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const testing = std.testing; const builtin = @import("builtin"); diff --git a/std/math/log1p.zig b/std/math/log1p.zig index 257e7b90d4..c9be1132be 100644 --- a/std/math/log1p.zig +++ b/std/math/log1p.zig @@ -7,7 +7,7 @@ // - log1p(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/log2.zig b/std/math/log2.zig index 1bb51bf9f9..e2dbf4105a 100644 --- a/std/math/log2.zig +++ b/std/math/log2.zig @@ -5,7 +5,7 @@ // - log2(x) = nan if x < 0 // - log2(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const builtin = @import("builtin"); diff --git a/std/math/modf.zig b/std/math/modf.zig index 2dadda76a9..e5a4964e63 100644 --- a/std/math/modf.zig +++ b/std/math/modf.zig @@ -3,7 +3,7 @@ // - modf(+-inf) = +-inf, nan // - modf(nan) = nan, nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/math/nan.zig b/std/math/nan.zig index d3ad43da23..1a11a141d5 100644 --- a/std/math/nan.zig +++ b/std/math/nan.zig @@ -1,4 +1,4 @@ -const math = @import("index.zig"); +const math = @import("../math.zig"); pub fn nan(comptime T: type) T { return switch (T) { diff --git a/std/math/pow.zig b/std/math/pow.zig index f037f66d7e..81bb2d95d5 100644 --- a/std/math/pow.zig +++ b/std/math/pow.zig @@ -22,7 +22,7 @@ // pow(x, y) = nan for finite x < 0 and finite non-integer y const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/powi.zig b/std/math/powi.zig index dffb564da7..b7212efcbf 100644 --- a/std/math/powi.zig +++ b/std/math/powi.zig @@ -9,7 +9,7 @@ // powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0 const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const assert = std.debug.assert; const testing = std.testing; diff --git a/std/math/round.zig b/std/math/round.zig index 7346b703c9..39ff56ca79 100644 --- a/std/math/round.zig +++ b/std/math/round.zig @@ -6,7 +6,7 @@ const builtin = @import("builtin"); const expect = std.testing.expect; -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; pub fn round(x: var) @typeOf(x) { diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig index d37a8659a9..d1338f5acb 100644 --- a/std/math/scalbn.zig +++ b/std/math/scalbn.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/signbit.zig b/std/math/signbit.zig index 728f651aec..9727152b07 100644 --- a/std/math/signbit.zig +++ b/std/math/signbit.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/sin.zig b/std/math/sin.zig index 5ade6636c7..e25b8a292b 100644 --- a/std/math/sin.zig +++ b/std/math/sin.zig @@ -5,7 +5,7 @@ // - sin(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/sinh.zig b/std/math/sinh.zig index 95924ba55a..cf4363c4a9 100644 --- a/std/math/sinh.zig +++ b/std/math/sinh.zig @@ -5,7 +5,7 @@ // - sinh(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const expo2 = @import("expo2.zig").expo2; diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig index 9996b44b20..9062f598a1 100644 --- a/std/math/sqrt.zig +++ b/std/math/sqrt.zig @@ -5,7 +5,7 @@ // - sqrt(x) = nan if x < 0 // - sqrt(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const builtin = @import("builtin"); diff --git a/std/math/tan.zig b/std/math/tan.zig index ec43092320..fc11ebdef7 100644 --- a/std/math/tan.zig +++ b/std/math/tan.zig @@ -5,7 +5,7 @@ // - tan(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; diff --git a/std/math/tanh.zig b/std/math/tanh.zig index a35449a053..f88ecfdc9c 100644 --- a/std/math/tanh.zig +++ b/std/math/tanh.zig @@ -5,7 +5,7 @@ // - sinh(nan) = nan const builtin = @import("builtin"); -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const expo2 = @import("expo2.zig").expo2; diff --git a/std/math/trunc.zig b/std/math/trunc.zig index 8c91ccc568..9449d0e7eb 100644 --- a/std/math/trunc.zig +++ b/std/math/trunc.zig @@ -4,7 +4,7 @@ // - trunc(+-inf) = +-inf // - trunc(nan) = nan -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const expect = std.testing.expect; const maxInt = std.math.maxInt; diff --git a/std/mem.zig b/std/mem.zig index 39b9701754..681054d087 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const debug = std.debug; const assert = debug.assert; const math = std.math; diff --git a/std/meta.zig b/std/meta.zig new file mode 100644 index 0000000000..215c5dfad8 --- /dev/null +++ b/std/meta.zig @@ -0,0 +1,569 @@ +const std = @import("std.zig"); +const builtin = @import("builtin"); +const debug = std.debug; +const mem = std.mem; +const math = std.math; +const testing = std.testing; + +pub const trait = @import("meta/trait.zig"); + +const TypeId = builtin.TypeId; +const TypeInfo = builtin.TypeInfo; + +pub fn tagName(v: var) []const u8 { + const T = @typeOf(v); + switch (@typeInfo(T)) { + TypeId.Enum => |info| { + const Tag = info.tag_type; + inline for (info.fields) |field| { + if (field.value == @enumToInt(v)) return field.name; + } + + unreachable; + }, + TypeId.Union => |info| { + const UnionTag = if (info.tag_type) |UT| UT else @compileError("union is untagged"); + const Tag = @typeInfo(UnionTag).Enum.tag_type; + inline for (info.fields) |field| { + if (field.enum_field.?.value == @enumToInt(UnionTag(v))) + return field.name; + } + + unreachable; + }, + TypeId.ErrorSet => |info| { + inline for (info.errors) |err| { + if (err.value == @errorToInt(v)) return err.name; + } + + unreachable; + }, + else => @compileError("expected enum, error set or union type, found '" ++ @typeName(T) ++ "'"), + } +} + +test "std.meta.tagName" { + const E1 = enum { + A, + B, + }; + const E2 = enum(u8) { + C = 33, + D, + }; + const U1 = union(enum) { + G: u8, + H: u16, + }; + const U2 = union(E2) { + C: u8, + D: u16, + }; + + var u1g = U1{ .G = 0 }; + var u1h = U1{ .H = 0 }; + var u2a = U2{ .C = 0 }; + var u2b = U2{ .D = 0 }; + + testing.expect(mem.eql(u8, tagName(E1.A), "A")); + testing.expect(mem.eql(u8, tagName(E1.B), "B")); + testing.expect(mem.eql(u8, tagName(E2.C), "C")); + testing.expect(mem.eql(u8, tagName(E2.D), "D")); + testing.expect(mem.eql(u8, tagName(error.E), "E")); + testing.expect(mem.eql(u8, tagName(error.F), "F")); + testing.expect(mem.eql(u8, tagName(u1g), "G")); + testing.expect(mem.eql(u8, tagName(u1h), "H")); + testing.expect(mem.eql(u8, tagName(u2a), "C")); + testing.expect(mem.eql(u8, tagName(u2b), "D")); +} + +pub fn stringToEnum(comptime T: type, str: []const u8) ?T { + inline for (@typeInfo(T).Enum.fields) |enumField| { + if (std.mem.eql(u8, str, enumField.name)) { + return @field(T, enumField.name); + } + } + return null; +} + +test "std.meta.stringToEnum" { + const E1 = enum { + A, + B, + }; + testing.expect(E1.A == stringToEnum(E1, "A").?); + testing.expect(E1.B == stringToEnum(E1, "B").?); + testing.expect(null == stringToEnum(E1, "C")); +} + +pub fn bitCount(comptime T: type) comptime_int { + return switch (@typeInfo(T)) { + TypeId.Int => |info| info.bits, + TypeId.Float => |info| info.bits, + else => @compileError("Expected int or float type, found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.bitCount" { + testing.expect(bitCount(u8) == 8); + testing.expect(bitCount(f32) == 32); +} + +pub fn alignment(comptime T: type) comptime_int { + //@alignOf works on non-pointer types + const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T; + return @typeInfo(P).Pointer.alignment; +} + +test "std.meta.alignment" { + testing.expect(alignment(u8) == 1); + testing.expect(alignment(*align(1) u8) == 1); + testing.expect(alignment(*align(2) u8) == 2); + testing.expect(alignment([]align(1) u8) == 1); + testing.expect(alignment([]align(2) u8) == 2); +} + +pub fn Child(comptime T: type) type { + return switch (@typeInfo(T)) { + TypeId.Array => |info| info.child, + TypeId.Pointer => |info| info.child, + TypeId.Optional => |info| info.child, + TypeId.Promise => |info| if (info.child) |child| child else null, + else => @compileError("Expected promise, pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.Child" { + testing.expect(Child([1]u8) == u8); + testing.expect(Child(*u8) == u8); + testing.expect(Child([]u8) == u8); + testing.expect(Child(?u8) == u8); + testing.expect(Child(promise->u8) == u8); +} + +pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { + return switch (@typeInfo(T)) { + TypeId.Struct => |info| info.layout, + TypeId.Enum => |info| info.layout, + TypeId.Union => |info| info.layout, + else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.containerLayout" { + const E1 = enum { + A, + }; + const E2 = packed enum { + A, + }; + const E3 = extern enum { + A, + }; + const S1 = struct {}; + const S2 = packed struct {}; + const S3 = extern struct {}; + const U1 = union { + a: u8, + }; + const U2 = packed union { + a: u8, + }; + const U3 = extern union { + a: u8, + }; + + testing.expect(containerLayout(E1) == TypeInfo.ContainerLayout.Auto); + testing.expect(containerLayout(E2) == TypeInfo.ContainerLayout.Packed); + testing.expect(containerLayout(E3) == TypeInfo.ContainerLayout.Extern); + testing.expect(containerLayout(S1) == TypeInfo.ContainerLayout.Auto); + testing.expect(containerLayout(S2) == TypeInfo.ContainerLayout.Packed); + testing.expect(containerLayout(S3) == TypeInfo.ContainerLayout.Extern); + testing.expect(containerLayout(U1) == TypeInfo.ContainerLayout.Auto); + testing.expect(containerLayout(U2) == TypeInfo.ContainerLayout.Packed); + testing.expect(containerLayout(U3) == TypeInfo.ContainerLayout.Extern); +} + +pub fn definitions(comptime T: type) []TypeInfo.Definition { + return switch (@typeInfo(T)) { + TypeId.Struct => |info| info.defs, + TypeId.Enum => |info| info.defs, + TypeId.Union => |info| info.defs, + else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.definitions" { + const E1 = enum { + A, + + fn a() void {} + }; + const S1 = struct { + fn a() void {} + }; + const U1 = union { + a: u8, + + fn a() void {} + }; + + const defs = comptime [][]TypeInfo.Definition{ + definitions(E1), + definitions(S1), + definitions(U1), + }; + + inline for (defs) |def| { + testing.expect(def.len == 1); + testing.expect(comptime mem.eql(u8, def[0].name, "a")); + } +} + +pub fn definitionInfo(comptime T: type, comptime def_name: []const u8) TypeInfo.Definition { + inline for (comptime definitions(T)) |def| { + if (comptime mem.eql(u8, def.name, def_name)) + return def; + } + + @compileError("'" ++ @typeName(T) ++ "' has no definition '" ++ def_name ++ "'"); +} + +test "std.meta.definitionInfo" { + const E1 = enum { + A, + + fn a() void {} + }; + const S1 = struct { + fn a() void {} + }; + const U1 = union { + a: u8, + + fn a() void {} + }; + + const infos = comptime []TypeInfo.Definition{ + definitionInfo(E1, "a"), + definitionInfo(S1, "a"), + definitionInfo(U1, "a"), + }; + + inline for (infos) |info| { + testing.expect(comptime mem.eql(u8, info.name, "a")); + testing.expect(!info.is_pub); + } +} + +pub fn fields(comptime T: type) switch (@typeInfo(T)) { + TypeId.Struct => []TypeInfo.StructField, + TypeId.Union => []TypeInfo.UnionField, + TypeId.ErrorSet => []TypeInfo.Error, + TypeId.Enum => []TypeInfo.EnumField, + else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), +} { + return switch (@typeInfo(T)) { + TypeId.Struct => |info| info.fields, + TypeId.Union => |info| info.fields, + TypeId.Enum => |info| info.fields, + TypeId.ErrorSet => |info| info.errors, + else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.fields" { + const E1 = enum { + A, + }; + const E2 = error{A}; + const S1 = struct { + a: u8, + }; + const U1 = union { + a: u8, + }; + + const e1f = comptime fields(E1); + const e2f = comptime fields(E2); + const sf = comptime fields(S1); + const uf = comptime fields(U1); + + testing.expect(e1f.len == 1); + testing.expect(e2f.len == 1); + testing.expect(sf.len == 1); + testing.expect(uf.len == 1); + testing.expect(mem.eql(u8, e1f[0].name, "A")); + testing.expect(mem.eql(u8, e2f[0].name, "A")); + testing.expect(mem.eql(u8, sf[0].name, "a")); + testing.expect(mem.eql(u8, uf[0].name, "a")); + testing.expect(comptime sf[0].field_type == u8); + testing.expect(comptime uf[0].field_type == u8); +} + +pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) { + TypeId.Struct => TypeInfo.StructField, + TypeId.Union => TypeInfo.UnionField, + TypeId.ErrorSet => TypeInfo.Error, + TypeId.Enum => TypeInfo.EnumField, + else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), +} { + inline for (comptime fields(T)) |field| { + if (comptime mem.eql(u8, field.name, field_name)) + return field; + } + + @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'"); +} + +test "std.meta.fieldInfo" { + const E1 = enum { + A, + }; + const E2 = error{A}; + const S1 = struct { + a: u8, + }; + const U1 = union { + a: u8, + }; + + const e1f = comptime fieldInfo(E1, "A"); + const e2f = comptime fieldInfo(E2, "A"); + const sf = comptime fieldInfo(S1, "a"); + const uf = comptime fieldInfo(U1, "a"); + + testing.expect(mem.eql(u8, e1f.name, "A")); + testing.expect(mem.eql(u8, e2f.name, "A")); + testing.expect(mem.eql(u8, sf.name, "a")); + testing.expect(mem.eql(u8, uf.name, "a")); + testing.expect(comptime sf.field_type == u8); + testing.expect(comptime uf.field_type == u8); +} + +pub fn TagType(comptime T: type) type { + return switch (@typeInfo(T)) { + TypeId.Enum => |info| info.tag_type, + TypeId.Union => |info| if (info.tag_type) |Tag| Tag else null, + else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"), + }; +} + +test "std.meta.TagType" { + const E = enum(u8) { + C = 33, + D, + }; + const U = union(E) { + C: u8, + D: u16, + }; + + testing.expect(TagType(E) == u8); + testing.expect(TagType(U) == E); +} + +///Returns the active tag of a tagged union +pub fn activeTag(u: var) @TagType(@typeOf(u)) { + const T = @typeOf(u); + return @TagType(T)(u); +} + +test "std.meta.activeTag" { + const UE = enum { + Int, + Float, + }; + + const U = union(UE) { + Int: u32, + Float: f32, + }; + + var u = U{ .Int = 32 }; + testing.expect(activeTag(u) == UE.Int); + + u = U{ .Float = 112.9876 }; + testing.expect(activeTag(u) == UE.Float); +} + +///Given a tagged union type, and an enum, return the type of the union +/// field corresponding to the enum tag. +pub fn TagPayloadType(comptime U: type, tag: var) type { + const Tag = @typeOf(tag); + testing.expect(trait.is(builtin.TypeId.Union)(U)); + testing.expect(trait.is(builtin.TypeId.Enum)(Tag)); + + const info = @typeInfo(U).Union; + + inline for (info.fields) |field_info| { + if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type; + } + unreachable; +} + +test "std.meta.TagPayloadType" { + const Event = union(enum) { + Moved: struct { + from: i32, + to: i32, + }, + }; + const MovedEvent = TagPayloadType(Event, Event.Moved); + var e: Event = undefined; + testing.expect(MovedEvent == @typeOf(e.Moved)); +} + +///Compares two of any type for equality. Containers are compared on a field-by-field basis, +/// where possible. Pointers are not followed. +pub fn eql(a: var, b: @typeOf(a)) bool { + const T = @typeOf(a); + + switch (@typeId(T)) { + builtin.TypeId.Struct => { + const info = @typeInfo(T).Struct; + + inline for (info.fields) |field_info| { + if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false; + } + return true; + }, + builtin.TypeId.ErrorUnion => { + if (a) |a_p| { + if (b) |b_p| return eql(a_p, b_p) else |_| return false; + } else |a_e| { + if (b) |_| return false else |b_e| return a_e == b_e; + } + }, + builtin.TypeId.Union => { + const info = @typeInfo(T).Union; + + if (info.tag_type) |_| { + const tag_a = activeTag(a); + const tag_b = activeTag(b); + if (tag_a != tag_b) return false; + + inline for (info.fields) |field_info| { + const enum_field = field_info.enum_field.?; + if (enum_field.value == @enumToInt(tag_a)) { + return eql(@field(a, enum_field.name), @field(b, enum_field.name)); + } + } + return false; + } + + @compileError("cannot compare untagged union type " ++ @typeName(T)); + }, + builtin.TypeId.Array => { + if (a.len != b.len) return false; + for (a) |e, i| + if (!eql(e, b[i])) return false; + return true; + }, + builtin.TypeId.Pointer => { + const info = @typeInfo(T).Pointer; + switch (info.size) { + builtin.TypeInfo.Pointer.Size.One, + builtin.TypeInfo.Pointer.Size.Many, + builtin.TypeInfo.Pointer.Size.C, + => return a == b, + builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len, + } + }, + builtin.TypeId.Optional => { + if (a == null and b == null) return true; + if (a == null or b == null) return false; + return eql(a.?, b.?); + }, + else => return a == b, + } +} + +test "std.meta.eql" { + const S = struct { + a: u32, + b: f64, + c: [5]u8, + }; + + const U = union(enum) { + s: S, + f: ?f32, + }; + + const s_1 = S{ + .a = 134, + .b = 123.3, + .c = "12345", + }; + + const s_2 = S{ + .a = 1, + .b = 123.3, + .c = "54321", + }; + + const s_3 = S{ + .a = 134, + .b = 123.3, + .c = "12345", + }; + + const u_1 = U{ .f = 24 }; + const u_2 = U{ .s = s_1 }; + const u_3 = U{ .f = 24 }; + + testing.expect(eql(s_1, s_3)); + testing.expect(eql(&s_1, &s_1)); + testing.expect(!eql(&s_1, &s_3)); + testing.expect(eql(u_1, u_3)); + testing.expect(!eql(u_1, u_2)); + + var a1 = "abcdef"; + var a2 = "abcdef"; + var a3 = "ghijkl"; + + testing.expect(eql(a1, a2)); + testing.expect(!eql(a1, a3)); + testing.expect(!eql(a1[0..], a2[0..])); + + const EU = struct { + fn tst(err: bool) !u8 { + if (err) return error.Error; + return u8(5); + } + }; + + testing.expect(eql(EU.tst(true), EU.tst(true))); + testing.expect(eql(EU.tst(false), EU.tst(false))); + testing.expect(!eql(EU.tst(false), EU.tst(true))); +} + +test "intToEnum with error return" { + const E1 = enum { + A, + }; + const E2 = enum { + A, + B, + }; + + var zero: u8 = 0; + var one: u16 = 1; + testing.expect(intToEnum(E1, zero) catch unreachable == E1.A); + testing.expect(intToEnum(E2, one) catch unreachable == E2.B); + testing.expectError(error.InvalidEnumTag, intToEnum(E1, one)); +} + +pub const IntToEnumError = error{InvalidEnumTag}; + +pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { + comptime var i = 0; + inline while (i != @memberCount(Tag)) : (i += 1) { + const this_tag_value = @field(Tag, @memberName(Tag, i)); + if (tag_int == @enumToInt(this_tag_value)) { + return this_tag_value; + } + } + return error.InvalidEnumTag; +} diff --git a/std/meta/index.zig b/std/meta/index.zig deleted file mode 100644 index 652e2d39ec..0000000000 --- a/std/meta/index.zig +++ /dev/null @@ -1,569 +0,0 @@ -const std = @import("../index.zig"); -const builtin = @import("builtin"); -const debug = std.debug; -const mem = std.mem; -const math = std.math; -const testing = std.testing; - -pub const trait = @import("trait.zig"); - -const TypeId = builtin.TypeId; -const TypeInfo = builtin.TypeInfo; - -pub fn tagName(v: var) []const u8 { - const T = @typeOf(v); - switch (@typeInfo(T)) { - TypeId.Enum => |info| { - const Tag = info.tag_type; - inline for (info.fields) |field| { - if (field.value == @enumToInt(v)) return field.name; - } - - unreachable; - }, - TypeId.Union => |info| { - const UnionTag = if (info.tag_type) |UT| UT else @compileError("union is untagged"); - const Tag = @typeInfo(UnionTag).Enum.tag_type; - inline for (info.fields) |field| { - if (field.enum_field.?.value == @enumToInt(UnionTag(v))) - return field.name; - } - - unreachable; - }, - TypeId.ErrorSet => |info| { - inline for (info.errors) |err| { - if (err.value == @errorToInt(v)) return err.name; - } - - unreachable; - }, - else => @compileError("expected enum, error set or union type, found '" ++ @typeName(T) ++ "'"), - } -} - -test "std.meta.tagName" { - const E1 = enum { - A, - B, - }; - const E2 = enum(u8) { - C = 33, - D, - }; - const U1 = union(enum) { - G: u8, - H: u16, - }; - const U2 = union(E2) { - C: u8, - D: u16, - }; - - var u1g = U1{ .G = 0 }; - var u1h = U1{ .H = 0 }; - var u2a = U2{ .C = 0 }; - var u2b = U2{ .D = 0 }; - - testing.expect(mem.eql(u8, tagName(E1.A), "A")); - testing.expect(mem.eql(u8, tagName(E1.B), "B")); - testing.expect(mem.eql(u8, tagName(E2.C), "C")); - testing.expect(mem.eql(u8, tagName(E2.D), "D")); - testing.expect(mem.eql(u8, tagName(error.E), "E")); - testing.expect(mem.eql(u8, tagName(error.F), "F")); - testing.expect(mem.eql(u8, tagName(u1g), "G")); - testing.expect(mem.eql(u8, tagName(u1h), "H")); - testing.expect(mem.eql(u8, tagName(u2a), "C")); - testing.expect(mem.eql(u8, tagName(u2b), "D")); -} - -pub fn stringToEnum(comptime T: type, str: []const u8) ?T { - inline for (@typeInfo(T).Enum.fields) |enumField| { - if (std.mem.eql(u8, str, enumField.name)) { - return @field(T, enumField.name); - } - } - return null; -} - -test "std.meta.stringToEnum" { - const E1 = enum { - A, - B, - }; - testing.expect(E1.A == stringToEnum(E1, "A").?); - testing.expect(E1.B == stringToEnum(E1, "B").?); - testing.expect(null == stringToEnum(E1, "C")); -} - -pub fn bitCount(comptime T: type) comptime_int { - return switch (@typeInfo(T)) { - TypeId.Int => |info| info.bits, - TypeId.Float => |info| info.bits, - else => @compileError("Expected int or float type, found '" ++ @typeName(T) ++ "'"), - }; -} - -test "std.meta.bitCount" { - testing.expect(bitCount(u8) == 8); - testing.expect(bitCount(f32) == 32); -} - -pub fn alignment(comptime T: type) comptime_int { - //@alignOf works on non-pointer types - const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T; - return @typeInfo(P).Pointer.alignment; -} - -test "std.meta.alignment" { - testing.expect(alignment(u8) == 1); - testing.expect(alignment(*align(1) u8) == 1); - testing.expect(alignment(*align(2) u8) == 2); - testing.expect(alignment([]align(1) u8) == 1); - testing.expect(alignment([]align(2) u8) == 2); -} - -pub fn Child(comptime T: type) type { - return switch (@typeInfo(T)) { - TypeId.Array => |info| info.child, - TypeId.Pointer => |info| info.child, - TypeId.Optional => |info| info.child, - TypeId.Promise => |info| if (info.child) |child| child else null, - else => @compileError("Expected promise, pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), - }; -} - -test "std.meta.Child" { - testing.expect(Child([1]u8) == u8); - testing.expect(Child(*u8) == u8); - testing.expect(Child([]u8) == u8); - testing.expect(Child(?u8) == u8); - testing.expect(Child(promise->u8) == u8); -} - -pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { - return switch (@typeInfo(T)) { - TypeId.Struct => |info| info.layout, - TypeId.Enum => |info| info.layout, - TypeId.Union => |info| info.layout, - else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), - }; -} - -test "std.meta.containerLayout" { - const E1 = enum { - A, - }; - const E2 = packed enum { - A, - }; - const E3 = extern enum { - A, - }; - const S1 = struct {}; - const S2 = packed struct {}; - const S3 = extern struct {}; - const U1 = union { - a: u8, - }; - const U2 = packed union { - a: u8, - }; - const U3 = extern union { - a: u8, - }; - - testing.expect(containerLayout(E1) == TypeInfo.ContainerLayout.Auto); - testing.expect(containerLayout(E2) == TypeInfo.ContainerLayout.Packed); - testing.expect(containerLayout(E3) == TypeInfo.ContainerLayout.Extern); - testing.expect(containerLayout(S1) == TypeInfo.ContainerLayout.Auto); - testing.expect(containerLayout(S2) == TypeInfo.ContainerLayout.Packed); - testing.expect(containerLayout(S3) == TypeInfo.ContainerLayout.Extern); - testing.expect(containerLayout(U1) == TypeInfo.ContainerLayout.Auto); - testing.expect(containerLayout(U2) == TypeInfo.ContainerLayout.Packed); - testing.expect(containerLayout(U3) == TypeInfo.ContainerLayout.Extern); -} - -pub fn definitions(comptime T: type) []TypeInfo.Definition { - return switch (@typeInfo(T)) { - TypeId.Struct => |info| info.defs, - TypeId.Enum => |info| info.defs, - TypeId.Union => |info| info.defs, - else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), - }; -} - -test "std.meta.definitions" { - const E1 = enum { - A, - - fn a() void {} - }; - const S1 = struct { - fn a() void {} - }; - const U1 = union { - a: u8, - - fn a() void {} - }; - - const defs = comptime [][]TypeInfo.Definition{ - definitions(E1), - definitions(S1), - definitions(U1), - }; - - inline for (defs) |def| { - testing.expect(def.len == 1); - testing.expect(comptime mem.eql(u8, def[0].name, "a")); - } -} - -pub fn definitionInfo(comptime T: type, comptime def_name: []const u8) TypeInfo.Definition { - inline for (comptime definitions(T)) |def| { - if (comptime mem.eql(u8, def.name, def_name)) - return def; - } - - @compileError("'" ++ @typeName(T) ++ "' has no definition '" ++ def_name ++ "'"); -} - -test "std.meta.definitionInfo" { - const E1 = enum { - A, - - fn a() void {} - }; - const S1 = struct { - fn a() void {} - }; - const U1 = union { - a: u8, - - fn a() void {} - }; - - const infos = comptime []TypeInfo.Definition{ - definitionInfo(E1, "a"), - definitionInfo(S1, "a"), - definitionInfo(U1, "a"), - }; - - inline for (infos) |info| { - testing.expect(comptime mem.eql(u8, info.name, "a")); - testing.expect(!info.is_pub); - } -} - -pub fn fields(comptime T: type) switch (@typeInfo(T)) { - TypeId.Struct => []TypeInfo.StructField, - TypeId.Union => []TypeInfo.UnionField, - TypeId.ErrorSet => []TypeInfo.Error, - TypeId.Enum => []TypeInfo.EnumField, - else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), -} { - return switch (@typeInfo(T)) { - TypeId.Struct => |info| info.fields, - TypeId.Union => |info| info.fields, - TypeId.Enum => |info| info.fields, - TypeId.ErrorSet => |info| info.errors, - else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), - }; -} - -test "std.meta.fields" { - const E1 = enum { - A, - }; - const E2 = error{A}; - const S1 = struct { - a: u8, - }; - const U1 = union { - a: u8, - }; - - const e1f = comptime fields(E1); - const e2f = comptime fields(E2); - const sf = comptime fields(S1); - const uf = comptime fields(U1); - - testing.expect(e1f.len == 1); - testing.expect(e2f.len == 1); - testing.expect(sf.len == 1); - testing.expect(uf.len == 1); - testing.expect(mem.eql(u8, e1f[0].name, "A")); - testing.expect(mem.eql(u8, e2f[0].name, "A")); - testing.expect(mem.eql(u8, sf[0].name, "a")); - testing.expect(mem.eql(u8, uf[0].name, "a")); - testing.expect(comptime sf[0].field_type == u8); - testing.expect(comptime uf[0].field_type == u8); -} - -pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) { - TypeId.Struct => TypeInfo.StructField, - TypeId.Union => TypeInfo.UnionField, - TypeId.ErrorSet => TypeInfo.Error, - TypeId.Enum => TypeInfo.EnumField, - else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), -} { - inline for (comptime fields(T)) |field| { - if (comptime mem.eql(u8, field.name, field_name)) - return field; - } - - @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'"); -} - -test "std.meta.fieldInfo" { - const E1 = enum { - A, - }; - const E2 = error{A}; - const S1 = struct { - a: u8, - }; - const U1 = union { - a: u8, - }; - - const e1f = comptime fieldInfo(E1, "A"); - const e2f = comptime fieldInfo(E2, "A"); - const sf = comptime fieldInfo(S1, "a"); - const uf = comptime fieldInfo(U1, "a"); - - testing.expect(mem.eql(u8, e1f.name, "A")); - testing.expect(mem.eql(u8, e2f.name, "A")); - testing.expect(mem.eql(u8, sf.name, "a")); - testing.expect(mem.eql(u8, uf.name, "a")); - testing.expect(comptime sf.field_type == u8); - testing.expect(comptime uf.field_type == u8); -} - -pub fn TagType(comptime T: type) type { - return switch (@typeInfo(T)) { - TypeId.Enum => |info| info.tag_type, - TypeId.Union => |info| if (info.tag_type) |Tag| Tag else null, - else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"), - }; -} - -test "std.meta.TagType" { - const E = enum(u8) { - C = 33, - D, - }; - const U = union(E) { - C: u8, - D: u16, - }; - - testing.expect(TagType(E) == u8); - testing.expect(TagType(U) == E); -} - -///Returns the active tag of a tagged union -pub fn activeTag(u: var) @TagType(@typeOf(u)) { - const T = @typeOf(u); - return @TagType(T)(u); -} - -test "std.meta.activeTag" { - const UE = enum { - Int, - Float, - }; - - const U = union(UE) { - Int: u32, - Float: f32, - }; - - var u = U{ .Int = 32 }; - testing.expect(activeTag(u) == UE.Int); - - u = U{ .Float = 112.9876 }; - testing.expect(activeTag(u) == UE.Float); -} - -///Given a tagged union type, and an enum, return the type of the union -/// field corresponding to the enum tag. -pub fn TagPayloadType(comptime U: type, tag: var) type { - const Tag = @typeOf(tag); - testing.expect(trait.is(builtin.TypeId.Union)(U)); - testing.expect(trait.is(builtin.TypeId.Enum)(Tag)); - - const info = @typeInfo(U).Union; - - inline for (info.fields) |field_info| { - if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type; - } - unreachable; -} - -test "std.meta.TagPayloadType" { - const Event = union(enum) { - Moved: struct { - from: i32, - to: i32, - }, - }; - const MovedEvent = TagPayloadType(Event, Event.Moved); - var e: Event = undefined; - testing.expect(MovedEvent == @typeOf(e.Moved)); -} - -///Compares two of any type for equality. Containers are compared on a field-by-field basis, -/// where possible. Pointers are not followed. -pub fn eql(a: var, b: @typeOf(a)) bool { - const T = @typeOf(a); - - switch (@typeId(T)) { - builtin.TypeId.Struct => { - const info = @typeInfo(T).Struct; - - inline for (info.fields) |field_info| { - if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false; - } - return true; - }, - builtin.TypeId.ErrorUnion => { - if (a) |a_p| { - if (b) |b_p| return eql(a_p, b_p) else |_| return false; - } else |a_e| { - if (b) |_| return false else |b_e| return a_e == b_e; - } - }, - builtin.TypeId.Union => { - const info = @typeInfo(T).Union; - - if (info.tag_type) |_| { - const tag_a = activeTag(a); - const tag_b = activeTag(b); - if (tag_a != tag_b) return false; - - inline for (info.fields) |field_info| { - const enum_field = field_info.enum_field.?; - if (enum_field.value == @enumToInt(tag_a)) { - return eql(@field(a, enum_field.name), @field(b, enum_field.name)); - } - } - return false; - } - - @compileError("cannot compare untagged union type " ++ @typeName(T)); - }, - builtin.TypeId.Array => { - if (a.len != b.len) return false; - for (a) |e, i| - if (!eql(e, b[i])) return false; - return true; - }, - builtin.TypeId.Pointer => { - const info = @typeInfo(T).Pointer; - switch (info.size) { - builtin.TypeInfo.Pointer.Size.One, - builtin.TypeInfo.Pointer.Size.Many, - builtin.TypeInfo.Pointer.Size.C, - => return a == b, - builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len, - } - }, - builtin.TypeId.Optional => { - if (a == null and b == null) return true; - if (a == null or b == null) return false; - return eql(a.?, b.?); - }, - else => return a == b, - } -} - -test "std.meta.eql" { - const S = struct { - a: u32, - b: f64, - c: [5]u8, - }; - - const U = union(enum) { - s: S, - f: ?f32, - }; - - const s_1 = S{ - .a = 134, - .b = 123.3, - .c = "12345", - }; - - const s_2 = S{ - .a = 1, - .b = 123.3, - .c = "54321", - }; - - const s_3 = S{ - .a = 134, - .b = 123.3, - .c = "12345", - }; - - const u_1 = U{ .f = 24 }; - const u_2 = U{ .s = s_1 }; - const u_3 = U{ .f = 24 }; - - testing.expect(eql(s_1, s_3)); - testing.expect(eql(&s_1, &s_1)); - testing.expect(!eql(&s_1, &s_3)); - testing.expect(eql(u_1, u_3)); - testing.expect(!eql(u_1, u_2)); - - var a1 = "abcdef"; - var a2 = "abcdef"; - var a3 = "ghijkl"; - - testing.expect(eql(a1, a2)); - testing.expect(!eql(a1, a3)); - testing.expect(!eql(a1[0..], a2[0..])); - - const EU = struct { - fn tst(err: bool) !u8 { - if (err) return error.Error; - return u8(5); - } - }; - - testing.expect(eql(EU.tst(true), EU.tst(true))); - testing.expect(eql(EU.tst(false), EU.tst(false))); - testing.expect(!eql(EU.tst(false), EU.tst(true))); -} - -test "intToEnum with error return" { - const E1 = enum { - A, - }; - const E2 = enum { - A, - B, - }; - - var zero: u8 = 0; - var one: u16 = 1; - testing.expect(intToEnum(E1, zero) catch unreachable == E1.A); - testing.expect(intToEnum(E2, one) catch unreachable == E2.B); - testing.expectError(error.InvalidEnumTag, intToEnum(E1, one)); -} - -pub const IntToEnumError = error{InvalidEnumTag}; - -pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { - comptime var i = 0; - inline while (i != @memberCount(Tag)) : (i += 1) { - const this_tag_value = @field(Tag, @memberName(Tag, i)); - if (tag_int == @enumToInt(this_tag_value)) { - return this_tag_value; - } - } - return error.InvalidEnumTag; -} diff --git a/std/meta/trait.zig b/std/meta/trait.zig index 7fca5f4dcd..f77619ec39 100644 --- a/std/meta/trait.zig +++ b/std/meta/trait.zig @@ -1,11 +1,11 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const mem = std.mem; const debug = std.debug; const testing = std.testing; const warn = debug.warn; -const meta = @import("index.zig"); +const meta = @import("../meta.zig"); //This is necessary if we want to return generic functions directly because of how the // the type erasure works. see: #1375 diff --git a/std/mutex.zig b/std/mutex.zig index a13b1c06c7..2b3ac4e366 100644 --- a/std/mutex.zig +++ b/std/mutex.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; const AtomicRmwOp = builtin.AtomicRmwOp; diff --git a/std/net.zig b/std/net.zig index 968c1f019f..bb292efd83 100644 --- a/std/net.zig +++ b/std/net.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const net = @This(); diff --git a/std/os.zig b/std/os.zig new file mode 100644 index 0000000000..c686c2a054 --- /dev/null +++ b/std/os.zig @@ -0,0 +1,3432 @@ +const std = @import("std.zig"); +const builtin = @import("builtin"); +const Os = builtin.Os; +const is_windows = builtin.os == Os.windows; +const is_posix = switch (builtin.os) { + builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, + else => false, +}; +const os = @This(); + +comptime { + assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir +} + +test "std.os" { + _ = @import("os/child_process.zig"); + _ = @import("os/darwin.zig"); + _ = @import("os/darwin/errno.zig"); + _ = @import("os/get_user_id.zig"); + _ = @import("os/linux.zig"); + _ = @import("os/path.zig"); + _ = @import("os/test.zig"); + _ = @import("os/time.zig"); + _ = @import("os/windows.zig"); + _ = @import("os/uefi.zig"); + _ = @import("os/get_app_data_dir.zig"); +} + +pub const windows = @import("os/windows.zig"); +pub const darwin = @import("os/darwin.zig"); +pub const linux = @import("os/linux.zig"); +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 posix = switch (builtin.os) { + Os.linux => linux, + Os.macosx, Os.ios => darwin, + Os.freebsd => freebsd, + Os.netbsd => netbsd, + Os.zen => zen, + else => @compileError("Unsupported OS"), +}; + +pub const net = @import("net.zig"); + +pub const ChildProcess = @import("os/child_process.zig").ChildProcess; +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 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. + // If it would require 4 UTF-8 bytes, then there would be a surrogate + // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. + // +1 for the null byte at the end, which can be encoded in 1 byte. + Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, + else => @compileError("Unsupported OS"), +}; + +pub const UserInfo = @import("os/get_user_id.zig").UserInfo; +pub const getUserInfo = @import("os/get_user_id.zig").getUserInfo; + +const windows_util = @import("os/windows/util.zig"); +pub const windowsWaitSingle = windows_util.windowsWaitSingle; +pub const windowsWrite = windows_util.windowsWrite; +pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; +pub const windowsOpen = windows_util.windowsOpen; +pub const windowsOpenW = windows_util.windowsOpenW; +pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; + +pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError; +pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort; + +pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError; +pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus; + +pub const WindowsWaitResult = windows_util.WindowsWaitResult; +pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus; + +pub const WindowsWaitError = windows_util.WaitError; +pub const WindowsOpenError = windows_util.OpenError; +pub const WindowsWriteError = windows_util.WriteError; +pub const WindowsReadError = windows_util.ReadError; + +pub const FileHandle = if (is_windows) windows.HANDLE else i32; + +pub const getAppDataDir = @import("os/get_app_data_dir.zig").getAppDataDir; +pub const GetAppDataDirError = @import("os/get_app_data_dir.zig").GetAppDataDirError; + +const debug = std.debug; +const assert = debug.assert; +const testing = std.testing; + +const c = std.c; + +const mem = std.mem; +const Allocator = mem.Allocator; + +const BufMap = std.BufMap; +const cstr = std.cstr; + +const io = std.io; +const base64 = std.base64; +const ArrayList = std.ArrayList; +const Buffer = std.Buffer; +const math = std.math; + +/// Fills `buf` with random bytes. If linking against libc, this calls the +/// appropriate OS-specific library call. Otherwise it uses the zig standard +/// library implementation. +pub fn getRandomBytes(buf: []u8) !void { + switch (builtin.os) { + Os.linux => while (true) { + // TODO check libc version and potentially call c.getrandom. + // See #397 + const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); + switch (errno) { + 0 => return, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EINTR => continue, + posix.ENOSYS => return getRandomBytesDevURandom(buf), + else => return unexpectedErrorPosix(errno), + } + }, + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), + Os.windows => { + // Call RtlGenRandom() instead of CryptGetRandom() on Windows + // https://github.com/rust-lang-nursery/rand/issues/111 + // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 + if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { + const err = windows.GetLastError(); + return switch (err) { + else => unexpectedErrorWindows(err), + }; + } + }, + Os.zen => { + const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; + var i: usize = 0; + while (i < buf.len) : (i += 1) { + if (i > randomness.len) return error.Unknown; + buf[i] = randomness[i]; + } + }, + else => @compileError("Unsupported OS"), + } +} + +fn getRandomBytesDevURandom(buf: []u8) !void { + const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); + defer close(fd); + + const stream = &File.openHandle(fd).inStream().stream; + stream.readNoEof(buf) catch |err| switch (err) { + error.EndOfStream => unreachable, + error.OperationAborted => unreachable, + error.BrokenPipe => unreachable, + error.Unexpected => return error.Unexpected, + error.InputOutput => return error.Unexpected, + error.SystemResources => return error.Unexpected, + error.IsDir => unreachable, + }; +} + +test "os.getRandomBytes" { + var buf_a: [50]u8 = undefined; + var buf_b: [50]u8 = undefined; + // Call Twice + try getRandomBytes(buf_a[0..]); + try getRandomBytes(buf_b[0..]); + + // Check if random (not 100% conclusive) + testing.expect(!mem.eql(u8, buf_a, buf_b)); +} + +/// Raises a signal in the current kernel thread, ending its execution. +/// If linking against libc, this calls the abort() libc function. Otherwise +/// it uses the zig standard library implementation. +pub fn abort() noreturn { + @setCold(true); + if (builtin.link_libc) { + c.abort(); + } + switch (builtin.os) { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + _ = posix.raise(posix.SIGABRT); + _ = posix.raise(posix.SIGKILL); + while (true) {} + }, + Os.windows => { + if (builtin.mode == builtin.Mode.Debug) { + @breakpoint(); + } + windows.ExitProcess(3); + }, + Os.uefi => { + // TODO there's gotta be a better thing to do here than loop forever + while (true) {} + }, + else => @compileError("Unsupported OS"), + } +} + +/// Exits the program cleanly with the specified status code. +pub fn exit(status: u8) noreturn { + @setCold(true); + if (builtin.link_libc) { + c.exit(status); + } + switch (builtin.os) { + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + posix.exit(status); + }, + Os.windows => { + windows.ExitProcess(status); + }, + else => @compileError("Unsupported OS"), + } +} + +/// When a file descriptor is closed on linux, it pops the first +/// node from this queue and resumes it. +/// Async functions which get the EMFILE error code can suspend, +/// putting their coroutine handle into this list. +/// TODO make this an atomic linked list +pub var emfile_promise_queue = std.LinkedList(promise).init(); + +/// Closes the file handle. Keeps trying if it gets interrupted by a signal. +pub fn close(handle: FileHandle) void { + if (is_windows) { + windows_util.windowsClose(handle); + } else { + while (true) { + const err = posix.getErrno(posix.close(handle)); + switch (err) { + posix.EINTR => continue, + else => { + if (emfile_promise_queue.popFirst()) |p| resume p.data; + return; + }, + } + } + } +} + +pub const PosixReadError = error{ + InputOutput, + SystemResources, + IsDir, + Unexpected, +}; + +/// Returns the number of bytes that were read, which can be less than +/// buf.len. If 0 bytes were read, that means EOF. +pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { + // Linux can return EINVAL when read amount is > 0x7ffff000 + // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 + const max_buf_len = 0x7ffff000; + + var index: usize = 0; + while (index < buf.len) { + const want_to_read = math.min(buf.len - index, usize(max_buf_len)); + const rc = posix.read(fd, buf.ptr + index, want_to_read); + const err = posix.getErrno(rc); + switch (err) { + 0 => { + index += rc; + if (rc == want_to_read) continue; + // Read returned less than buf.len. + return index; + }, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => unreachable, + posix.EBADF => unreachable, // always a race condition + posix.EIO => return error.InputOutput, + posix.EISDIR => return error.IsDir, + posix.ENOBUFS => return error.SystemResources, + posix.ENOMEM => return error.SystemResources, + else => return unexpectedErrorPosix(err), + } + } + return index; +} + +/// Number of bytes read is returned. Upon reading end-of-file, zero is returned. +pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { + switch (builtin.os) { + builtin.Os.macosx => { + // Darwin does not have preadv but it does have pread. + var off: usize = 0; + var iov_i: usize = 0; + var inner_off: usize = 0; + while (true) { + const v = iov[iov_i]; + const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); + const err = darwin.getErrno(rc); + switch (err) { + 0 => { + off += rc; + inner_off += rc; + if (inner_off == v.iov_len) { + iov_i += 1; + inner_off = 0; + if (iov_i == count) { + return off; + } + } + if (rc == 0) return off; // EOF + continue; + }, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.ESPIPE => unreachable, // fd is not seekable + posix.EAGAIN => unreachable, // this function is not for non blocking + posix.EBADF => unreachable, // always a race condition + posix.EIO => return error.InputOutput, + posix.EISDIR => return error.IsDir, + posix.ENOBUFS => return error.SystemResources, + posix.ENOMEM => return error.SystemResources, + else => return unexpectedErrorPosix(err), + } + } + }, + builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { + const rc = posix.preadv(fd, iov, count, offset); + const err = posix.getErrno(rc); + switch (err) { + 0 => return rc, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => unreachable, // don't call this function for non blocking + posix.EBADF => unreachable, // always a race condition + posix.EIO => return error.InputOutput, + posix.EISDIR => return error.IsDir, + posix.ENOBUFS => return error.SystemResources, + posix.ENOMEM => return error.SystemResources, + else => return unexpectedErrorPosix(err), + } + }, + else => @compileError("Unsupported OS"), + } +} + +pub const PosixWriteError = error{ + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + BrokenPipe, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +/// Calls POSIX write, and keeps trying if it gets interrupted. +pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { + // Linux can return EINVAL when write amount is > 0x7ffff000 + // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 + const max_bytes_len = 0x7ffff000; + + var index: usize = 0; + while (index < bytes.len) { + const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); + const rc = posix.write(fd, bytes.ptr + index, amt_to_write); + const write_err = posix.getErrno(rc); + switch (write_err) { + 0 => { + index += rc; + continue; + }, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking + posix.EBADF => unreachable, // always a race condition + posix.EDESTADDRREQ => unreachable, // connect was never called + posix.EDQUOT => return PosixWriteError.DiskQuota, + posix.EFBIG => return PosixWriteError.FileTooBig, + posix.EIO => return PosixWriteError.InputOutput, + posix.ENOSPC => return PosixWriteError.NoSpaceLeft, + posix.EPERM => return PosixWriteError.AccessDenied, + posix.EPIPE => return PosixWriteError.BrokenPipe, + else => return unexpectedErrorPosix(write_err), + } + } +} + +pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { + switch (builtin.os) { + builtin.Os.macosx => { + // Darwin does not have pwritev but it does have pwrite. + var off: usize = 0; + var iov_i: usize = 0; + var inner_off: usize = 0; + while (true) { + const v = iov[iov_i]; + const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); + const err = darwin.getErrno(rc); + switch (err) { + 0 => { + off += rc; + inner_off += rc; + if (inner_off == v.iov_len) { + iov_i += 1; + inner_off = 0; + if (iov_i == count) { + return; + } + } + continue; + }, + posix.EINTR => continue, + posix.ESPIPE => unreachable, // fd is not seekable + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking + posix.EBADF => unreachable, // always a race condition + posix.EDESTADDRREQ => unreachable, // connect was never called + posix.EDQUOT => return PosixWriteError.DiskQuota, + posix.EFBIG => return PosixWriteError.FileTooBig, + posix.EIO => return PosixWriteError.InputOutput, + posix.ENOSPC => return PosixWriteError.NoSpaceLeft, + posix.EPERM => return PosixWriteError.AccessDenied, + posix.EPIPE => return PosixWriteError.BrokenPipe, + else => return unexpectedErrorPosix(err), + } + } + }, + builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { + const rc = posix.pwritev(fd, iov, count, offset); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking + posix.EBADF => unreachable, // always a race condition + posix.EDESTADDRREQ => unreachable, // connect was never called + posix.EDQUOT => return PosixWriteError.DiskQuota, + posix.EFBIG => return PosixWriteError.FileTooBig, + posix.EIO => return PosixWriteError.InputOutput, + posix.ENOSPC => return PosixWriteError.NoSpaceLeft, + posix.EPERM => return PosixWriteError.AccessDenied, + posix.EPIPE => return PosixWriteError.BrokenPipe, + else => return unexpectedErrorPosix(err), + } + }, + else => @compileError("Unsupported OS"), + } +} + +pub const PosixOpenError = error{ + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + FileNotFound, + SystemResources, + NoSpaceLeft, + NotDir, + PathAlreadyExists, + DeviceBusy, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +/// ::file_path needs to be copied in memory to add a null terminating byte. +/// Calls POSIX open, keeps trying if it gets interrupted, and translates +/// the return value into zig errors. +pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { + const file_path_c = try toPosixPath(file_path); + return posixOpenC(&file_path_c, flags, perm); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { + while (true) { + const result = posix.open(file_path, flags, perm); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EINTR => continue, + + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EACCES => return PosixOpenError.AccessDenied, + posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, + posix.EISDIR => return PosixOpenError.IsDir, + posix.ELOOP => return PosixOpenError.SymLinkLoop, + posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, + posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, + posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, + posix.ENODEV => return PosixOpenError.NoDevice, + posix.ENOENT => return PosixOpenError.FileNotFound, + posix.ENOMEM => return PosixOpenError.SystemResources, + posix.ENOSPC => return PosixOpenError.NoSpaceLeft, + posix.ENOTDIR => return PosixOpenError.NotDir, + posix.EPERM => return PosixOpenError.AccessDenied, + posix.EEXIST => return PosixOpenError.PathAlreadyExists, + posix.EBUSY => return PosixOpenError.DeviceBusy, + else => return unexpectedErrorPosix(err), + } + } + return @intCast(i32, result); + } +} + +/// Used to convert a slice to a null terminated slice on the stack. +/// TODO well defined copy elision +pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { + var path_with_null: [posix.PATH_MAX]u8 = undefined; + if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; + mem.copy(u8, path_with_null[0..], file_path); + path_with_null[file_path.len] = 0; + return path_with_null; +} + +pub fn posixDup2(old_fd: i32, new_fd: i32) !void { + while (true) { + const err = posix.getErrno(posix.dup2(old_fd, new_fd)); + if (err > 0) { + return switch (err) { + posix.EBUSY, posix.EINTR => continue, + posix.EMFILE => error.ProcessFdQuotaExceeded, + posix.EINVAL => unreachable, + else => unexpectedErrorPosix(err), + }; + } + return; + } +} + +pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { + const envp_count = env_map.count(); + const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); + mem.set(?[*]u8, envp_buf, null); + errdefer freeNullDelimitedEnvMap(allocator, envp_buf); + { + var it = env_map.iterator(); + var i: usize = 0; + while (it.next()) |pair| : (i += 1) { + const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); + @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); + env_buf[pair.key.len] = '='; + @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); + env_buf[env_buf.len - 1] = 0; + + envp_buf[i] = env_buf.ptr; + } + assert(i == envp_count); + } + assert(envp_buf[envp_count] == null); + return envp_buf; +} + +pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { + for (envp_buf) |env| { + const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; + allocator.free(env_buf); + } + allocator.free(envp_buf); +} + +/// This function must allocate memory to add a null terminating bytes on path and each arg. +/// It must also convert to KEY=VALUE\0 format for environment variables, and include null +/// pointers after the args and after the environment variables. +/// `argv[0]` is the executable path. +/// This function also uses the PATH environment variable to get the full path to the executable. +pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { + const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); + mem.set(?[*]u8, argv_buf, null); + defer { + for (argv_buf) |arg| { + const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; + allocator.free(arg_buf); + } + allocator.free(argv_buf); + } + for (argv) |arg, i| { + const arg_buf = try allocator.alloc(u8, arg.len + 1); + @memcpy(arg_buf.ptr, arg.ptr, arg.len); + arg_buf[arg.len] = 0; + + argv_buf[i] = arg_buf.ptr; + } + argv_buf[argv.len] = null; + + const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); + defer freeNullDelimitedEnvMap(allocator, envp_buf); + + const exe_path = argv[0]; + if (mem.indexOfScalar(u8, exe_path, '/') != null) { + return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); + } + + const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; + // PATH.len because it is >= the largest search_path + // +1 for the / to join the search path and exe_path + // +1 for the null terminating byte + const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); + defer allocator.free(path_buf); + var it = mem.tokenize(PATH, ":"); + var seen_eacces = false; + var err: usize = undefined; + while (it.next()) |search_path| { + mem.copy(u8, path_buf, search_path); + path_buf[search_path.len] = '/'; + mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); + path_buf[search_path.len + exe_path.len + 1] = 0; + err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); + assert(err > 0); + if (err == posix.EACCES) { + seen_eacces = true; + } else if (err != posix.ENOENT) { + return posixExecveErrnoToErr(err); + } + } + if (seen_eacces) { + err = posix.EACCES; + } + return posixExecveErrnoToErr(err); +} + +pub const PosixExecveError = error{ + SystemResources, + AccessDenied, + InvalidExe, + FileSystem, + IsDir, + FileNotFound, + NotDir, + FileBusy, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +fn posixExecveErrnoToErr(err: usize) PosixExecveError { + assert(err > 0); + switch (err) { + posix.EFAULT => unreachable, + posix.E2BIG => return error.SystemResources, + posix.EMFILE => return error.SystemResources, + posix.ENAMETOOLONG => return error.SystemResources, + posix.ENFILE => return error.SystemResources, + posix.ENOMEM => return error.SystemResources, + posix.EACCES => return error.AccessDenied, + posix.EPERM => return error.AccessDenied, + posix.EINVAL => return error.InvalidExe, + posix.ENOEXEC => return error.InvalidExe, + posix.EIO => return error.FileSystem, + posix.ELOOP => return error.FileSystem, + posix.EISDIR => return error.IsDir, + posix.ENOENT => return error.FileNotFound, + posix.ENOTDIR => return error.NotDir, + posix.ETXTBSY => return error.FileBusy, + else => return unexpectedErrorPosix(err), + } +} + +pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; +pub var posix_environ_raw: [][*]u8 = undefined; + +/// See std.elf for the constants. +pub fn linuxGetAuxVal(index: usize) usize { + if (builtin.link_libc) { + return usize(std.c.getauxval(index)); + } else if (linux_elf_aux_maybe) |auxv| { + var i: usize = 0; + while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { + if (auxv[i].a_type == index) + return auxv[i].a_un.a_val; + } + } + return 0; +} + +pub fn getBaseAddress() usize { + switch (builtin.os) { + builtin.Os.linux => { + const base = linuxGetAuxVal(std.elf.AT_BASE); + if (base != 0) { + return base; + } + const phdr = linuxGetAuxVal(std.elf.AT_PHDR); + return phdr - @sizeOf(std.elf.Ehdr); + }, + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { + return @ptrToInt(&std.c._mh_execute_header); + }, + builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), + else => @compileError("Unsupported OS"), + } +} + +/// Caller must free result when done. +/// TODO make this go through libc when we have it +pub fn getEnvMap(allocator: *Allocator) !BufMap { + var result = BufMap.init(allocator); + errdefer result.deinit(); + + if (is_windows) { + const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; + defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); + + var i: usize = 0; + while (true) { + if (ptr[i] == 0) return result; + + const key_start = i; + + while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} + const key_w = ptr[key_start..i]; + const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); + errdefer allocator.free(key); + + if (ptr[i] == '=') i += 1; + + const value_start = i; + while (ptr[i] != 0) : (i += 1) {} + const value_w = ptr[value_start..i]; + const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); + errdefer allocator.free(value); + + i += 1; // skip over null byte + + try result.setMove(key, value); + } + } else { + for (posix_environ_raw) |ptr| { + var line_i: usize = 0; + while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} + const key = ptr[0..line_i]; + + var end_i: usize = line_i; + while (ptr[end_i] != 0) : (end_i += 1) {} + const value = ptr[line_i + 1 .. end_i]; + + try result.set(key, value); + } + return result; + } +} + +test "os.getEnvMap" { + var env = try getEnvMap(std.debug.global_allocator); + defer env.deinit(); +} + +/// TODO make this go through libc when we have it +pub fn getEnvPosix(key: []const u8) ?[]const u8 { + for (posix_environ_raw) |ptr| { + var line_i: usize = 0; + while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} + const this_key = ptr[0..line_i]; + if (!mem.eql(u8, key, this_key)) continue; + + var end_i: usize = line_i; + while (ptr[end_i] != 0) : (end_i += 1) {} + const this_value = ptr[line_i + 1 .. end_i]; + + return this_value; + } + return null; +} + +pub const GetEnvVarOwnedError = error{ + OutOfMemory, + EnvironmentVariableNotFound, + + /// See https://github.com/ziglang/zig/issues/1774 + InvalidUtf8, +}; + +/// Caller must free returned memory. +/// TODO make this go through libc when we have it +pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { + if (is_windows) { + const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); + defer allocator.free(key_with_null); + + var buf = try allocator.alloc(u16, 256); + defer allocator.free(buf); + + while (true) { + const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; + const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); + + if (result == 0) { + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, + else => { + _ = unexpectedErrorWindows(err); + return error.EnvironmentVariableNotFound; + }, + }; + } + + if (result > buf.len) { + buf = try allocator.realloc(u16, buf, result); + continue; + } + + return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { + error.DanglingSurrogateHalf => return error.InvalidUtf8, + error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, + error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, + error.OutOfMemory => return error.OutOfMemory, + }; + } + } else { + const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; + return mem.dupe(allocator, u8, result); + } +} + +test "os.getEnvVarOwned" { + var ga = debug.global_allocator; + testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); +} + +/// Caller must free the returned memory. +pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { + var buf: [MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try getCwd(&buf)); +} + +pub const GetCwdError = error{Unexpected}; + +/// The result is a slice of out_buffer. +pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { + switch (builtin.os) { + Os.windows => { + var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast + const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast + const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); + if (result == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(err), + } + } + assert(result <= utf16le_buf.len); + const utf16le_slice = utf16le_buf[0..result]; + // Trust that Windows gives us valid UTF-16LE. + const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; + return out_buffer[0..end_index]; + }, + else => { + const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); + switch (err) { + 0 => return cstr.toSlice(out_buffer), + posix.ERANGE => unreachable, + else => return unexpectedErrorPosix(err), + } + }, + } +} + +test "os.getCwd" { + // at least call it so it gets compiled + _ = getCwdAlloc(debug.global_allocator); + var buf: [MAX_PATH_BYTES]u8 = undefined; + _ = getCwd(&buf); +} + +pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; + +/// TODO add a symLinkC variant +pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { + if (is_windows) { + return symLinkWindows(existing_path, new_path); + } else { + return symLinkPosix(existing_path, new_path); + } +} + +pub const WindowsSymLinkError = error{ + NameTooLong, + InvalidUtf8, + BadPathName, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { + if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(err), + } + } +} + +pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { + const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); + const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); + return symLinkW(&existing_path_w, &new_path_w); +} + +pub const PosixSymLinkError = error{ + AccessDenied, + DiskQuota, + PathAlreadyExists, + FileSystem, + SymLinkLoop, + NameTooLong, + FileNotFound, + SystemResources, + NoSpaceLeft, + ReadOnlyFileSystem, + NotDir, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { + const err = posix.getErrno(posix.symlink(existing_path, new_path)); + switch (err) { + 0 => return, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EACCES => return error.AccessDenied, + posix.EPERM => return error.AccessDenied, + posix.EDQUOT => return error.DiskQuota, + posix.EEXIST => return error.PathAlreadyExists, + posix.EIO => return error.FileSystem, + posix.ELOOP => return error.SymLinkLoop, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ENOENT => return error.FileNotFound, + posix.ENOTDIR => return error.NotDir, + posix.ENOMEM => return error.SystemResources, + posix.ENOSPC => return error.NoSpaceLeft, + posix.EROFS => return error.ReadOnlyFileSystem, + else => return unexpectedErrorPosix(err), + } +} + +pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { + const existing_path_c = try toPosixPath(existing_path); + const new_path_c = try toPosixPath(new_path); + return symLinkPosixC(&existing_path_c, &new_path_c); +} + +// here we replace the standard +/ with -_ so that it can be used in a file name +const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); + +/// TODO remove the allocator requirement from this API +pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { + if (symLink(existing_path, new_path)) { + return; + } else |err| switch (err) { + error.PathAlreadyExists => {}, + else => return err, // TODO zig should know this set does not include PathAlreadyExists + } + + const dirname = os.path.dirname(new_path) orelse "."; + + var rand_buf: [12]u8 = undefined; + const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); + defer allocator.free(tmp_path); + mem.copy(u8, tmp_path[0..], dirname); + tmp_path[dirname.len] = os.path.sep; + while (true) { + try getRandomBytes(rand_buf[0..]); + b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); + + if (symLink(existing_path, tmp_path)) { + return rename(tmp_path, new_path); + } else |err| switch (err) { + error.PathAlreadyExists => continue, + else => return err, // TODO zig should know this set does not include PathAlreadyExists + } + } +} + +pub const DeleteFileError = error{ + FileNotFound, + AccessDenied, + FileBusy, + FileSystem, + IsDir, + SymLinkLoop, + NameTooLong, + NotDir, + SystemResources, + ReadOnlyFileSystem, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn deleteFile(file_path: []const u8) DeleteFileError!void { + if (builtin.os == Os.windows) { + const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); + return deleteFileW(&file_path_w); + } else { + const file_path_c = try toPosixPath(file_path); + return deleteFileC(&file_path_c); + } +} + +pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { + if (windows.DeleteFileW(file_path) == 0) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, + windows.ERROR.ACCESS_DENIED => return error.AccessDenied, + windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, + windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, + else => return unexpectedErrorWindows(err), + } + } +} + +pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { + if (is_windows) { + const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); + return deleteFileW(&file_path_w); + } else { + const err = posix.getErrno(posix.unlink(file_path)); + switch (err) { + 0 => return, + posix.EACCES => return error.AccessDenied, + posix.EPERM => return error.AccessDenied, + posix.EBUSY => return error.FileBusy, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EIO => return error.FileSystem, + posix.EISDIR => return error.IsDir, + posix.ELOOP => return error.SymLinkLoop, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ENOENT => return error.FileNotFound, + posix.ENOTDIR => return error.NotDir, + posix.ENOMEM => return error.SystemResources, + posix.EROFS => return error.ReadOnlyFileSystem, + else => return unexpectedErrorPosix(err), + } + } +} + +/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is +/// merged and readily available, +/// there is a possibility of power loss or application termination leaving temporary files present +/// in the same directory as dest_path. +/// Destination file will have the same mode as the source file. +pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { + var in_file = try os.File.openRead(source_path); + defer in_file.close(); + + const mode = try in_file.mode(); + + 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..]); + try atomic_file.file.write(buf[0..amt]); + if (amt != buf.len) { + return atomic_file.finish(); + } + } +} + +/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is +/// merged and readily available, +/// there is a possibility of power loss or application termination leaving temporary files present +pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { + var in_file = try os.File.openRead(source_path); + defer in_file.close(); + + 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.read(buf[0..]); + try atomic_file.file.write(buf[0..amt]); + if (amt != buf.len) { + return atomic_file.finish(); + } + } +} + +pub const AtomicFile = struct { + file: os.File, + tmp_path_buf: [MAX_PATH_BYTES]u8, + dest_path: []const u8, + finished: bool, + + const InitError = os.File.OpenError; + + /// dest_path must remain valid for the lifetime of AtomicFile + /// call finish to atomically replace dest_path with contents + /// TODO once we have null terminated pointers, use the + /// openWriteNoClobberN function + pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { + const dirname = os.path.dirname(dest_path); + var rand_buf: [12]u8 = undefined; + const dirname_component_len = if (dirname) |d| d.len + 1 else 0; + const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); + const tmp_path_len = dirname_component_len + encoded_rand_len; + var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; + if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; + + if (dirname) |dir| { + mem.copy(u8, tmp_path_buf[0..], dir); + tmp_path_buf[dir.len] = os.path.sep; + } + + tmp_path_buf[tmp_path_len] = 0; + + while (true) { + try getRandomBytes(rand_buf[0..]); + b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); + + const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) { + error.PathAlreadyExists => continue, + // TODO zig should figure out that this error set does not include PathAlreadyExists since + // it is handled in the above switch + else => return err, + }; + + return AtomicFile{ + .file = file, + .tmp_path_buf = tmp_path_buf, + .dest_path = dest_path, + .finished = false, + }; + } + } + + /// always call deinit, even after successful finish() + pub fn deinit(self: *AtomicFile) void { + if (!self.finished) { + self.file.close(); + deleteFileC(&self.tmp_path_buf) catch {}; + self.finished = true; + } + } + + pub fn finish(self: *AtomicFile) !void { + assert(!self.finished); + self.file.close(); + self.finished = true; + if (is_posix) { + const dest_path_c = try toPosixPath(self.dest_path); + return renameC(&self.tmp_path_buf, &dest_path_c); + } else if (is_windows) { + const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); + const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); + return renameW(&tmp_path_w, &dest_path_w); + } else { + @compileError("Unsupported OS"); + } + } +}; + +pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { + if (is_windows) { + const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); + const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); + return renameW(&old_path_w, &new_path_w); + } else { + const err = posix.getErrno(posix.rename(old_path, new_path)); + switch (err) { + 0 => return, + posix.EACCES => return error.AccessDenied, + posix.EPERM => return error.AccessDenied, + posix.EBUSY => return error.FileBusy, + posix.EDQUOT => return error.DiskQuota, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EISDIR => return error.IsDir, + posix.ELOOP => return error.SymLinkLoop, + posix.EMLINK => return error.LinkQuotaExceeded, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ENOENT => return error.FileNotFound, + posix.ENOTDIR => return error.NotDir, + posix.ENOMEM => return error.SystemResources, + posix.ENOSPC => return error.NoSpaceLeft, + posix.EEXIST => return error.PathAlreadyExists, + posix.ENOTEMPTY => return error.PathAlreadyExists, + posix.EROFS => return error.ReadOnlyFileSystem, + posix.EXDEV => return error.RenameAcrossMountPoints, + else => return unexpectedErrorPosix(err), + } + } +} + +pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { + const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; + if (windows.MoveFileExW(old_path, new_path, flags) == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(err), + } + } +} + +pub fn rename(old_path: []const u8, new_path: []const u8) !void { + if (is_windows) { + const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); + const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); + return renameW(&old_path_w, &new_path_w); + } else { + const old_path_c = try toPosixPath(old_path); + const new_path_c = try toPosixPath(new_path); + return renameC(&old_path_c, &new_path_c); + } +} + +pub fn makeDir(dir_path: []const u8) !void { + if (is_windows) { + return makeDirWindows(dir_path); + } else { + return makeDirPosix(dir_path); + } +} + +pub fn makeDirWindows(dir_path: []const u8) !void { + const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); + + if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { + const err = windows.GetLastError(); + return switch (err) { + windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, + windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, + else => unexpectedErrorWindows(err), + }; + } +} + +pub fn makeDirPosixC(dir_path: [*]const u8) !void { + const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); + switch (err) { + 0 => return, + posix.EACCES => return error.AccessDenied, + posix.EPERM => return error.AccessDenied, + posix.EDQUOT => return error.DiskQuota, + posix.EEXIST => return error.PathAlreadyExists, + posix.EFAULT => unreachable, + posix.ELOOP => return error.SymLinkLoop, + posix.EMLINK => return error.LinkQuotaExceeded, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ENOENT => return error.FileNotFound, + posix.ENOMEM => return error.SystemResources, + posix.ENOSPC => return error.NoSpaceLeft, + posix.ENOTDIR => return error.NotDir, + posix.EROFS => return error.ReadOnlyFileSystem, + else => return unexpectedErrorPosix(err), + } +} + +pub fn makeDirPosix(dir_path: []const u8) !void { + const dir_path_c = try toPosixPath(dir_path); + return makeDirPosixC(&dir_path_c); +} + +/// Calls makeDir recursively to make an entire path. Returns success if the path +/// already exists and is a directory. +/// TODO determine if we can remove the allocator requirement from this function +pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { + const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); + defer allocator.free(resolved_path); + + var end_index: usize = resolved_path.len; + while (true) { + makeDir(resolved_path[0..end_index]) catch |err| switch (err) { + error.PathAlreadyExists => { + // TODO stat the file and return an error if it's not a directory + // this is important because otherwise a dangling symlink + // could cause an infinite loop + if (end_index == resolved_path.len) return; + }, + error.FileNotFound => { + // march end_index backward until next path component + while (true) { + end_index -= 1; + if (os.path.isSep(resolved_path[end_index])) break; + } + continue; + }, + else => return err, + }; + if (end_index == resolved_path.len) return; + // march end_index forward until next path component + while (true) { + end_index += 1; + if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; + } + } +} + +pub const DeleteDirError = error{ + AccessDenied, + FileBusy, + SymLinkLoop, + NameTooLong, + FileNotFound, + SystemResources, + NotDir, + DirNotEmpty, + ReadOnlyFileSystem, + InvalidUtf8, + BadPathName, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { + switch (builtin.os) { + Os.windows => { + const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); + return deleteDirW(&dir_path_w); + }, + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + const err = posix.getErrno(posix.rmdir(dir_path)); + switch (err) { + 0 => return, + posix.EACCES => return error.AccessDenied, + posix.EPERM => return error.AccessDenied, + posix.EBUSY => return error.FileBusy, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.ELOOP => return error.SymLinkLoop, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ENOENT => return error.FileNotFound, + posix.ENOMEM => return error.SystemResources, + posix.ENOTDIR => return error.NotDir, + posix.EEXIST => return error.DirNotEmpty, + posix.ENOTEMPTY => return error.DirNotEmpty, + posix.EROFS => return error.ReadOnlyFileSystem, + else => return unexpectedErrorPosix(err), + } + }, + else => @compileError("unimplemented"), + } +} + +pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { + if (windows.RemoveDirectoryW(dir_path_w) == 0) { + const err = windows.GetLastError(); + switch (err) { + windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, + windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, + else => return unexpectedErrorWindows(err), + } + } +} + +/// Returns ::error.DirNotEmpty if the directory is not empty. +/// To delete a directory recursively, see ::deleteTree +pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { + switch (builtin.os) { + Os.windows => { + const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); + return deleteDirW(&dir_path_w); + }, + Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + const dir_path_c = try toPosixPath(dir_path); + return deleteDirC(&dir_path_c); + }, + else => @compileError("unimplemented"), + } +} + +/// Whether ::full_path describes a symlink, file, or directory, this function +/// removes it. If it cannot be removed because it is a non-empty directory, +/// this function recursively removes its entries and then tries again. +const DeleteTreeError = error{ + OutOfMemory, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + PathAlreadyExists, + ReadOnlyFileSystem, + NotDir, + FileNotFound, + FileSystem, + FileBusy, + DirNotEmpty, + DeviceBusy, + + /// On Windows, file paths must be valid Unicode. + InvalidUtf8, + + /// On Windows, file paths cannot contain these characters: + /// '/', '*', '?', '"', '<', '>', '|' + BadPathName, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +/// TODO determine if we can remove the allocator requirement +pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { + start_over: while (true) { + var got_access_denied = false; + // First, try deleting the item as a file. This way we don't follow sym links. + if (deleteFile(full_path)) { + return; + } else |err| switch (err) { + error.FileNotFound => return, + error.IsDir => {}, + error.AccessDenied => got_access_denied = true, + + error.InvalidUtf8, + error.SymLinkLoop, + error.NameTooLong, + error.SystemResources, + error.ReadOnlyFileSystem, + error.NotDir, + error.FileSystem, + error.FileBusy, + error.BadPathName, + error.Unexpected, + => return err, + } + { + var dir = Dir.open(allocator, full_path) catch |err| switch (err) { + error.NotDir => { + if (got_access_denied) { + return error.AccessDenied; + } + continue :start_over; + }, + + error.OutOfMemory, + error.AccessDenied, + error.FileTooBig, + error.IsDir, + error.SymLinkLoop, + error.ProcessFdQuotaExceeded, + error.NameTooLong, + error.SystemFdQuotaExceeded, + error.NoDevice, + error.FileNotFound, + error.SystemResources, + error.NoSpaceLeft, + error.PathAlreadyExists, + error.Unexpected, + error.InvalidUtf8, + error.BadPathName, + error.DeviceBusy, + => return err, + }; + defer dir.close(); + + var full_entry_buf = ArrayList(u8).init(allocator); + defer full_entry_buf.deinit(); + + while (try dir.next()) |entry| { + try full_entry_buf.resize(full_path.len + entry.name.len + 1); + const full_entry_path = full_entry_buf.toSlice(); + mem.copy(u8, full_entry_path, full_path); + full_entry_path[full_path.len] = path.sep; + mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); + + try deleteTree(allocator, full_entry_path); + } + } + return deleteDir(full_path); + } +} + +pub const Dir = struct { + handle: Handle, + allocator: *Allocator, + + pub const Handle = switch (builtin.os) { + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct { + fd: i32, + seek: i64, + buf: []u8, + index: usize, + end_index: usize, + }, + Os.linux => struct { + fd: i32, + buf: []u8, + index: usize, + end_index: usize, + }, + Os.windows => struct { + handle: windows.HANDLE, + find_file_data: windows.WIN32_FIND_DATAW, + first: bool, + name_data: [256]u8, + }, + else => @compileError("unimplemented"), + }; + + pub const Entry = struct { + name: []const u8, + kind: Kind, + + pub const Kind = enum { + BlockDevice, + CharacterDevice, + Directory, + NamedPipe, + SymLink, + File, + UnixDomainSocket, + Whiteout, + Unknown, + }; + }; + + pub const OpenError = error{ + FileNotFound, + NotDir, + AccessDenied, + FileTooBig, + IsDir, + SymLinkLoop, + ProcessFdQuotaExceeded, + NameTooLong, + SystemFdQuotaExceeded, + NoDevice, + SystemResources, + NoSpaceLeft, + PathAlreadyExists, + OutOfMemory, + InvalidUtf8, + BadPathName, + DeviceBusy, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, + }; + + /// TODO remove the allocator requirement from this API + pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { + return Dir{ + .allocator = allocator, + .handle = switch (builtin.os) { + Os.windows => blk: { + var find_file_data: windows.WIN32_FIND_DATAW = undefined; + const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data); + break :blk Handle{ + .handle = handle, + .find_file_data = find_file_data, // TODO guaranteed copy elision + .first = true, + .name_data = undefined, + }; + }, + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{ + .fd = try posixOpen( + dir_path, + posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, + 0, + ), + .seek = 0, + .index = 0, + .end_index = 0, + .buf = []u8{}, + }, + Os.linux => Handle{ + .fd = try posixOpen( + dir_path, + posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, + 0, + ), + .index = 0, + .end_index = 0, + .buf = []u8{}, + }, + else => @compileError("unimplemented"), + }, + }; + } + + pub fn close(self: *Dir) void { + switch (builtin.os) { + Os.windows => { + _ = windows.FindClose(self.handle.handle); + }, + Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { + self.allocator.free(self.handle.buf); + os.close(self.handle.fd); + }, + else => @compileError("unimplemented"), + } + } + + /// Memory such as file names referenced in this returned entry becomes invalid + /// with subsequent calls to next, as well as when this `Dir` is deinitialized. + pub fn next(self: *Dir) !?Entry { + switch (builtin.os) { + Os.linux => return self.nextLinux(), + Os.macosx, Os.ios => return self.nextDarwin(), + Os.windows => return self.nextWindows(), + Os.freebsd => return self.nextFreebsd(), + Os.netbsd => return self.nextFreebsd(), + else => @compileError("unimplemented"), + } + } + + fn nextDarwin(self: *Dir) !?Entry { + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, page_size); + } + + while (true) { + const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + } + } + if (result == 0) return null; + self.handle.index = 0; + self.handle.end_index = result; + break; + } + } + const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + darwin_entry.d_reclen; + self.handle.index = next_index; + + const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (darwin_entry.d_type) { + posix.DT_BLK => Entry.Kind.BlockDevice, + posix.DT_CHR => Entry.Kind.CharacterDevice, + posix.DT_DIR => Entry.Kind.Directory, + posix.DT_FIFO => Entry.Kind.NamedPipe, + posix.DT_LNK => Entry.Kind.SymLink, + posix.DT_REG => Entry.Kind.File, + posix.DT_SOCK => Entry.Kind.UnixDomainSocket, + posix.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + + fn nextWindows(self: *Dir) !?Entry { + while (true) { + if (self.handle.first) { + self.handle.first = false; + } else { + if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) + return null; + } + const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); + if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) + continue; + // Trust that Windows gives us valid UTF-16LE + const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; + const name_utf8 = self.handle.name_data[0..name_utf8_len]; + const kind = blk: { + const attrs = self.handle.find_file_data.dwFileAttributes; + if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; + if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; + if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File; + break :blk Entry.Kind.Unknown; + }; + return Entry{ + .name = name_utf8, + .kind = kind, + }; + } + } + + fn nextLinux(self: *Dir) !?Entry { + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, page_size); + } + + while (true) { + const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + } + } + if (result == 0) return null; + self.handle.index = 0; + self.handle.end_index = result; + break; + } + } + const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + linux_entry.d_reclen; + self.handle.index = next_index; + + const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); + + // skip . and .. entries + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (linux_entry.d_type) { + posix.DT_BLK => Entry.Kind.BlockDevice, + posix.DT_CHR => Entry.Kind.CharacterDevice, + posix.DT_DIR => Entry.Kind.Directory, + posix.DT_FIFO => Entry.Kind.NamedPipe, + posix.DT_LNK => Entry.Kind.SymLink, + posix.DT_REG => Entry.Kind.File, + posix.DT_SOCK => Entry.Kind.UnixDomainSocket, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } + + fn nextFreebsd(self: *Dir) !?Entry { + start_over: while (true) { + if (self.handle.index >= self.handle.end_index) { + if (self.handle.buf.len == 0) { + self.handle.buf = try self.allocator.alloc(u8, page_size); + } + + while (true) { + const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); + const err = posix.getErrno(result); + if (err > 0) { + switch (err) { + posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, + posix.EINVAL => { + self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); + continue; + }, + else => return unexpectedErrorPosix(err), + } + } + if (result == 0) return null; + self.handle.index = 0; + self.handle.end_index = result; + break; + } + } + const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); + const next_index = self.handle.index + freebsd_entry.d_reclen; + self.handle.index = next_index; + + const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; + + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { + continue :start_over; + } + + const entry_kind = switch (freebsd_entry.d_type) { + posix.DT_BLK => Entry.Kind.BlockDevice, + posix.DT_CHR => Entry.Kind.CharacterDevice, + posix.DT_DIR => Entry.Kind.Directory, + posix.DT_FIFO => Entry.Kind.NamedPipe, + posix.DT_LNK => Entry.Kind.SymLink, + posix.DT_REG => Entry.Kind.File, + posix.DT_SOCK => Entry.Kind.UnixDomainSocket, + posix.DT_WHT => Entry.Kind.Whiteout, + else => Entry.Kind.Unknown, + }; + return Entry{ + .name = name, + .kind = entry_kind, + }; + } + } +}; + +pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { + const path_buf = try allocator.alloc(u8, dir_path.len + 1); + defer allocator.free(path_buf); + + mem.copy(u8, path_buf, dir_path); + path_buf[dir_path.len] = 0; + + const err = posix.getErrno(posix.chdir(path_buf.ptr)); + if (err > 0) { + return switch (err) { + posix.EACCES => error.AccessDenied, + posix.EFAULT => unreachable, + posix.EIO => error.FileSystem, + posix.ELOOP => error.SymLinkLoop, + posix.ENAMETOOLONG => error.NameTooLong, + posix.ENOENT => error.FileNotFound, + posix.ENOMEM => error.SystemResources, + posix.ENOTDIR => error.NotDir, + else => unexpectedErrorPosix(err), + }; + } +} + +/// Read value of a symbolic link. +/// The return value is a slice of out_buffer. +pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { + const rc = posix.readlink(pathname, out_buffer, out_buffer.len); + const err = posix.getErrno(rc); + switch (err) { + 0 => return out_buffer[0..rc], + posix.EACCES => return error.AccessDenied, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EIO => return error.FileSystem, + posix.ELOOP => return error.SymLinkLoop, + posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX + posix.ENOENT => return error.FileNotFound, + posix.ENOMEM => return error.SystemResources, + posix.ENOTDIR => return error.NotDir, + else => return unexpectedErrorPosix(err), + } +} + +/// Read value of a symbolic link. +/// The return value is a slice of out_buffer. +pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { + const file_path_c = try toPosixPath(file_path); + return readLinkC(out_buffer, &file_path_c); +} + +pub fn posix_setuid(uid: u32) !void { + const err = posix.getErrno(posix.setuid(uid)); + if (err == 0) return; + return switch (err) { + posix.EAGAIN => error.ResourceLimitReached, + posix.EINVAL => error.InvalidUserId, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; +} + +pub fn posix_setreuid(ruid: u32, euid: u32) !void { + const err = posix.getErrno(posix.setreuid(ruid, euid)); + if (err == 0) return; + return switch (err) { + posix.EAGAIN => error.ResourceLimitReached, + posix.EINVAL => error.InvalidUserId, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; +} + +pub fn posix_setgid(gid: u32) !void { + const err = posix.getErrno(posix.setgid(gid)); + if (err == 0) return; + return switch (err) { + posix.EAGAIN => error.ResourceLimitReached, + posix.EINVAL => error.InvalidUserId, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; +} + +pub fn posix_setregid(rgid: u32, egid: u32) !void { + const err = posix.getErrno(posix.setregid(rgid, egid)); + if (err == 0) return; + return switch (err) { + posix.EAGAIN => error.ResourceLimitReached, + posix.EINVAL => error.InvalidUserId, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; +} + +pub const WindowsGetStdHandleErrs = error{ + NoStdHandles, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { + if (windows.GetStdHandle(handle_id)) |handle| { + if (handle == windows.INVALID_HANDLE_VALUE) { + const err = windows.GetLastError(); + return switch (err) { + else => os.unexpectedErrorWindows(err), + }; + } + return handle; + } else { + return error.NoStdHandles; + } +} + +pub const ArgIteratorPosix = struct { + index: usize, + count: usize, + + pub fn init() ArgIteratorPosix { + return ArgIteratorPosix{ + .index = 0, + .count = raw.len, + }; + } + + pub fn next(self: *ArgIteratorPosix) ?[]const u8 { + if (self.index == self.count) return null; + + const s = raw[self.index]; + self.index += 1; + return cstr.toSlice(s); + } + + pub fn skip(self: *ArgIteratorPosix) bool { + if (self.index == self.count) return false; + + self.index += 1; + return true; + } + + /// This is marked as public but actually it's only meant to be used + /// internally by zig's startup code. + pub var raw: [][*]u8 = undefined; +}; + +pub const ArgIteratorWindows = struct { + index: usize, + cmd_line: [*]const u8, + in_quote: bool, + quote_count: usize, + seen_quote_count: usize, + + pub const NextError = error{OutOfMemory}; + + pub fn init() ArgIteratorWindows { + return initWithCmdLine(windows.GetCommandLineA()); + } + + pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { + return ArgIteratorWindows{ + .index = 0, + .cmd_line = cmd_line, + .in_quote = false, + .quote_count = countQuotes(cmd_line), + .seen_quote_count = 0, + }; + } + + /// You must free the returned memory when done. + pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { + // march forward over whitespace + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return null, + ' ', '\t' => continue, + else => break, + } + } + + return self.internalNext(allocator); + } + + pub fn skip(self: *ArgIteratorWindows) bool { + // march forward over whitespace + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return false, + ' ', '\t' => continue, + else => break, + } + } + + var backslash_count: usize = 0; + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return true, + '"' => { + const quote_is_real = backslash_count % 2 == 0; + if (quote_is_real) { + self.seen_quote_count += 1; + } + }, + '\\' => { + backslash_count += 1; + }, + ' ', '\t' => { + if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { + return true; + } + backslash_count = 0; + }, + else => { + backslash_count = 0; + continue; + }, + } + } + } + + fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { + var buf = try Buffer.initSize(allocator, 0); + defer buf.deinit(); + + var backslash_count: usize = 0; + while (true) : (self.index += 1) { + const byte = self.cmd_line[self.index]; + switch (byte) { + 0 => return buf.toOwnedSlice(), + '"' => { + const quote_is_real = backslash_count % 2 == 0; + try self.emitBackslashes(&buf, backslash_count / 2); + backslash_count = 0; + + if (quote_is_real) { + self.seen_quote_count += 1; + if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) { + try buf.appendByte('"'); + } + } else { + try buf.appendByte('"'); + } + }, + '\\' => { + backslash_count += 1; + }, + ' ', '\t' => { + try self.emitBackslashes(&buf, backslash_count); + backslash_count = 0; + if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { + try buf.appendByte(byte); + } else { + return buf.toOwnedSlice(); + } + }, + else => { + try self.emitBackslashes(&buf, backslash_count); + backslash_count = 0; + try buf.appendByte(byte); + }, + } + } + } + + fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { + var i: usize = 0; + while (i < emit_count) : (i += 1) { + try buf.appendByte('\\'); + } + } + + fn countQuotes(cmd_line: [*]const u8) usize { + var result: usize = 0; + var backslash_count: usize = 0; + var index: usize = 0; + while (true) : (index += 1) { + const byte = cmd_line[index]; + switch (byte) { + 0 => return result, + '\\' => backslash_count += 1, + '"' => { + result += 1 - (backslash_count % 2); + backslash_count = 0; + }, + else => { + backslash_count = 0; + }, + } + } + } +}; + +pub const ArgIterator = struct { + const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix; + + inner: InnerType, + + pub fn init() ArgIterator { + return ArgIterator{ .inner = InnerType.init() }; + } + + pub const NextError = ArgIteratorWindows.NextError; + + /// You must free the returned memory when done. + pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { + if (builtin.os == Os.windows) { + return self.inner.next(allocator); + } else { + return mem.dupe(allocator, u8, self.inner.next() orelse return null); + } + } + + /// If you only are targeting posix you can call this and not need an allocator. + pub fn nextPosix(self: *ArgIterator) ?[]const u8 { + return self.inner.next(); + } + + /// Parse past 1 argument without capturing it. + /// Returns `true` if skipped an arg, `false` if we are at the end. + pub fn skip(self: *ArgIterator) bool { + return self.inner.skip(); + } +}; + +pub fn args() ArgIterator { + return ArgIterator.init(); +} + +/// Caller must call argsFree on result. +pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { + // TODO refactor to only make 1 allocation. + var it = args(); + var contents = try Buffer.initSize(allocator, 0); + defer contents.deinit(); + + var slice_list = ArrayList(usize).init(allocator); + defer slice_list.deinit(); + + while (it.next(allocator)) |arg_or_err| { + const arg = try arg_or_err; + defer allocator.free(arg); + try contents.append(arg); + try slice_list.append(arg.len); + } + + const contents_slice = contents.toSliceConst(); + const slice_sizes = slice_list.toSliceConst(); + const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); + const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); + const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); + errdefer allocator.free(buf); + + const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); + const result_contents = buf[slice_list_bytes..]; + mem.copy(u8, result_contents, contents_slice); + + var contents_index: usize = 0; + for (slice_sizes) |len, i| { + const new_index = contents_index + len; + result_slice_list[i] = result_contents[contents_index..new_index]; + contents_index = new_index; + } + + return result_slice_list; +} + +pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { + var total_bytes: usize = 0; + for (args_alloc) |arg| { + total_bytes += @sizeOf([]u8) + arg.len; + } + const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; + const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); + return allocator.free(aligned_allocated_buf); +} + +test "windows arg parsing" { + testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); + testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); + testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); + testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); + testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); + testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); + + testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ + ".\\..\\zig-cache\\build", + "bin\\zig.exe", + ".\\..", + ".\\..\\zig-cache", + "--help", + }); +} + +fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { + var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); + for (expected_args) |expected_arg| { + const arg = it.next(debug.global_allocator).? catch unreachable; + testing.expectEqualSlices(u8, expected_arg, arg); + } + testing.expect(it.next(debug.global_allocator) == null); +} + +// TODO make this a build variable that you can set +const unexpected_error_tracing = false; +const UnexpectedError = error{ + /// The Operating System returned an undocumented error code. + Unexpected, +}; + +/// Call this when you made a syscall or something that sets errno +/// and you get an unexpected error. +pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { + if (unexpected_error_tracing) { + debug.warn("unexpected errno: {}\n", errno); + debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} + +/// Call this when you made a windows DLL call or something that does SetLastError +/// and you get an unexpected error. +pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { + if (unexpected_error_tracing) { + debug.warn("unexpected GetLastError(): {}\n", err); + @breakpoint(); + debug.dumpCurrentStackTrace(null); + } + return error.Unexpected; +} + +pub fn openSelfExe() !os.File { + switch (builtin.os) { + Os.linux => return os.File.openReadC(c"/proc/self/exe"), + Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + var buf: [MAX_PATH_BYTES]u8 = undefined; + const self_exe_path = try selfExePath(&buf); + buf[self_exe_path.len] = 0; + return os.File.openReadC(self_exe_path.ptr); + }, + Os.windows => { + var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + const wide_slice = try selfExePathW(&buf); + return os.File.openReadW(wide_slice.ptr); + }, + else => @compileError("Unsupported OS"), + } +} + +test "openSelfExe" { + switch (builtin.os) { + Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(), + else => return error.SkipZigTest, // Unsupported OS. + } +} + +pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { + const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast + const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); + assert(rc <= out_buffer.len); + if (rc == 0) { + const err = windows.GetLastError(); + switch (err) { + else => return unexpectedErrorWindows(err), + } + } + return out_buffer[0..rc]; +} + +/// Get the path to the current executable. +/// If you only need the directory, use selfExeDirPath. +/// If you only want an open file handle, use openSelfExe. +/// This function may return an error if the current executable +/// was deleted after spawning. +/// Returned value is a slice of out_buffer. +/// +/// On Linux, depends on procfs being mounted. If the currently executing binary has +/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. +/// TODO make the return type of this a null terminated pointer +pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { + switch (builtin.os) { + Os.linux => return readLink(out_buffer, "/proc/self/exe"), + Os.freebsd => { + var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; + var out_len: usize = out_buffer.len; + const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); + + if (err == 0) return mem.toSlice(u8, out_buffer); + + return switch (err) { + posix.EFAULT => error.BadAdress, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; + }, + Os.netbsd => { + var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME }; + var out_len: usize = out_buffer.len; + const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); + + if (err == 0) return mem.toSlice(u8, out_buffer); + + return switch (err) { + posix.EFAULT => error.BadAdress, + posix.EPERM => error.PermissionDenied, + else => unexpectedErrorPosix(err), + }; + }, + Os.windows => { + var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; + const utf16le_slice = try selfExePathW(&utf16le_buf); + // Trust that Windows gives us valid UTF-16LE. + const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; + return out_buffer[0..end_index]; + }, + Os.macosx, Os.ios => { + var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast + const rc = c._NSGetExecutablePath(out_buffer, &u32_len); + if (rc != 0) return error.NameTooLong; + return mem.toSlice(u8, out_buffer); + }, + else => @compileError("Unsupported OS"), + } +} + +/// `selfExeDirPath` except allocates the result on the heap. +/// Caller owns returned memory. +pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { + var buf: [MAX_PATH_BYTES]u8 = undefined; + return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); +} + +/// Get the directory path that contains the current executable. +/// Returned value is a slice of out_buffer. +pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { + switch (builtin.os) { + Os.linux => { + // If the currently executing binary has been deleted, + // the file path looks something like `/a/b/c/exe (deleted)` + // This path cannot be opened, but it's valid for determining the directory + // the executable was in when it was run. + const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe"); + // Assume that /proc/self/exe has an absolute path, and therefore dirname + // will not return null. + return path.dirname(full_exe_path).?; + }, + Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { + const self_exe_path = try selfExePath(out_buffer); + // Assume that the OS APIs return absolute paths, and therefore dirname + // will not return null. + return path.dirname(self_exe_path).?; + }, + else => @compileError("Unsupported OS"), + } +} + +pub fn isTty(handle: FileHandle) bool { + if (is_windows) { + return windows_util.windowsIsTty(handle); + } else { + if (builtin.link_libc) { + return c.isatty(handle) != 0; + } else { + return posix.isatty(handle); + } + } +} + +pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { + if (is_windows) { + return windows_util.windowsIsCygwinPty(handle); + } else { + if (builtin.link_libc) { + return c.isatty(handle) != 0; + } else { + return posix.isatty(handle); + } + } +} + +pub const PosixSocketError = error{ + /// Permission to create a socket of the specified type and/or + /// pro‐tocol is denied. + PermissionDenied, + + /// The implementation does not support the specified address family. + AddressFamilyNotSupported, + + /// Unknown protocol, or protocol family not available. + ProtocolFamilyNotAvailable, + + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// Insufficient memory is available. The socket cannot be created until sufficient + /// resources are freed. + SystemResources, + + /// The protocol type or the specified protocol is not supported within this domain. + ProtocolNotSupported, +}; + +pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { + const rc = posix.socket(domain, socket_type, protocol); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EACCES => return PosixSocketError.PermissionDenied, + posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, + posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, + posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, + posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, + posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, + posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, + else => return unexpectedErrorPosix(err), + } +} + +pub const PosixBindError = error{ + /// The address is protected, and the user is not the superuser. + /// For UNIX domain sockets: Search permission is denied on a component + /// of the path prefix. + AccessDenied, + + /// The given address is already in use, or in the case of Internet domain sockets, + /// The port number was specified as zero in the socket + /// address structure, but, upon attempting to bind to an ephemeral port, it was + /// determined that all port numbers in the ephemeral port range are currently in + /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). + AddressInUse, + + /// A nonexistent interface was requested or the requested address was not local. + AddressNotAvailable, + + /// Too many symbolic links were encountered in resolving addr. + SymLinkLoop, + + /// addr is too long. + NameTooLong, + + /// A component in the directory prefix of the socket pathname does not exist. + FileNotFound, + + /// Insufficient kernel memory was available. + SystemResources, + + /// A component of the path prefix is not a directory. + NotDir, + + /// The socket inode would reside on a read-only filesystem. + ReadOnlyFileSystem, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +/// addr is `&const T` where T is one of the sockaddr +pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { + const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + posix.EACCES => return PosixBindError.AccessDenied, + posix.EADDRINUSE => return PosixBindError.AddressInUse, + posix.EBADF => unreachable, // always a race condition if this error is returned + posix.EINVAL => unreachable, + posix.ENOTSOCK => unreachable, + posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, + posix.EFAULT => unreachable, + posix.ELOOP => return PosixBindError.SymLinkLoop, + posix.ENAMETOOLONG => return PosixBindError.NameTooLong, + posix.ENOENT => return PosixBindError.FileNotFound, + posix.ENOMEM => return PosixBindError.SystemResources, + posix.ENOTDIR => return PosixBindError.NotDir, + posix.EROFS => return PosixBindError.ReadOnlyFileSystem, + else => return unexpectedErrorPosix(err), + } +} + +const PosixListenError = error{ + /// Another socket is already listening on the same port. + /// For Internet domain sockets, the socket referred to by sockfd had not previously + /// been bound to an address and, upon attempting to bind it to an ephemeral port, it + /// was determined that all port numbers in the ephemeral port range are currently in + /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). + AddressInUse, + + /// The file descriptor sockfd does not refer to a socket. + FileDescriptorNotASocket, + + /// The socket is not of a type that supports the listen() operation. + OperationNotSupported, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { + const rc = posix.listen(sockfd, backlog); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + posix.EADDRINUSE => return PosixListenError.AddressInUse, + posix.EBADF => unreachable, + posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, + posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, + else => return unexpectedErrorPosix(err), + } +} + +pub const PosixAcceptError = error{ + ConnectionAborted, + + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// Not enough free memory. This often means that the memory allocation is limited + /// by the socket buffer limits, not by the system memory. + SystemResources, + + /// The file descriptor sockfd does not refer to a socket. + FileDescriptorNotASocket, + + /// The referenced socket is not of type SOCK_STREAM. + OperationNotSupported, + + ProtocolFailure, + + /// Firewall rules forbid connection. + BlockedByFirewall, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { + while (true) { + var sockaddr_size = u32(@sizeOf(posix.sockaddr)); + const rc = posix.accept4(fd, addr, &sockaddr_size, flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EINTR => continue, + else => return unexpectedErrorPosix(err), + + posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking + posix.EBADF => unreachable, // always a race condition + posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, + posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, + posix.ENOBUFS => return PosixAcceptError.SystemResources, + posix.ENOMEM => return PosixAcceptError.SystemResources, + posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, + posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, + posix.EPROTO => return PosixAcceptError.ProtocolFailure, + posix.EPERM => return PosixAcceptError.BlockedByFirewall, + } + } +} + +/// Returns -1 if would block. +pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { + while (true) { + var sockaddr_size = u32(@sizeOf(posix.sockaddr)); + const rc = posix.accept4(fd, addr, &sockaddr_size, flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EINTR => continue, + else => return unexpectedErrorPosix(err), + + posix.EAGAIN => return -1, + posix.EBADF => unreachable, // always a race condition + posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, + posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, + posix.ENOBUFS => return PosixAcceptError.SystemResources, + posix.ENOMEM => return PosixAcceptError.SystemResources, + posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, + posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, + posix.EPROTO => return PosixAcceptError.ProtocolFailure, + posix.EPERM => return PosixAcceptError.BlockedByFirewall, + } + } +} + +pub const LinuxEpollCreateError = error{ + /// The per-user limit on the number of epoll instances imposed by + /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further + /// details. + /// Or, The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// There was insufficient memory to create the kernel object. + SystemResources, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { + const rc = posix.epoll_create1(flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + else => return unexpectedErrorPosix(err), + + posix.EINVAL => unreachable, + posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, + posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, + posix.ENOMEM => return LinuxEpollCreateError.SystemResources, + } +} + +pub const LinuxEpollCtlError = error{ + /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered + /// with this epoll instance. + FileDescriptorAlreadyPresentInSet, + + /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a + /// circular loop of epoll instances monitoring one another. + OperationCausesCircularLoop, + + /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll + /// instance. + FileDescriptorNotRegistered, + + /// There was insufficient memory to handle the requested op control operation. + SystemResources, + + /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while + /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. + /// See epoll(7) for further details. + UserResourceLimitReached, + + /// The target file fd does not support epoll. This error can occur if fd refers to, + /// for example, a regular file or a directory. + FileDescriptorIncompatibleWithEpoll, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { + const rc = posix.epoll_ctl(epfd, op, fd, event); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + else => return unexpectedErrorPosix(err), + + posix.EBADF => unreachable, // always a race condition if this happens + posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, + posix.EINVAL => unreachable, + posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, + posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, + posix.ENOMEM => return LinuxEpollCtlError.SystemResources, + posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, + posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, + } +} + +pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { + while (true) { + const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); + const err = posix.getErrno(rc); + switch (err) { + 0 => return rc, + posix.EINTR => continue, + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + else => unreachable, + } + } +} + +pub const LinuxEventFdError = error{ + InvalidFlagValue, + SystemResources, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { + const rc = posix.eventfd(initval, flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + else => return unexpectedErrorPosix(err), + + posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, + posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, + posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, + posix.ENODEV => return LinuxEventFdError.SystemResources, + posix.ENOMEM => return LinuxEventFdError.SystemResources, + } +} + +pub const PosixGetSockNameError = error{ + /// Insufficient resources were available in the system to perform the operation. + SystemResources, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { + var addr: posix.sockaddr = undefined; + var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); + const rc = posix.getsockname(sockfd, &addr, &addrlen); + const err = posix.getErrno(rc); + switch (err) { + 0 => return addr, + else => return unexpectedErrorPosix(err), + + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.ENOTSOCK => unreachable, + posix.ENOBUFS => return PosixGetSockNameError.SystemResources, + } +} + +pub const PosixConnectError = error{ + /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket + /// file, or search permission is denied for one of the directories in the path prefix. + /// or + /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or + /// the connection request failed because of a local firewall rule. + PermissionDenied, + + /// Local address is already in use. + AddressInUse, + + /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an + /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers + /// in the ephemeral port range are currently in use. See the discussion of + /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). + AddressNotAvailable, + + /// The passed address didn't have the correct address family in its sa_family field. + AddressFamilyNotSupported, + + /// Insufficient entries in the routing cache. + SystemResources, + + /// A connect() on a stream socket found no one listening on the remote address. + ConnectionRefused, + + /// Network is unreachable. + NetworkUnreachable, + + /// Timeout while attempting connection. The server may be too busy to accept new connections. Note + /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. + ConnectionTimedOut, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { + while (true) { + const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + else => return unexpectedErrorPosix(err), + + posix.EACCES => return PosixConnectError.PermissionDenied, + posix.EPERM => return PosixConnectError.PermissionDenied, + posix.EADDRINUSE => return PosixConnectError.AddressInUse, + posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, + posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, + posix.EAGAIN => return PosixConnectError.SystemResources, + posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. + posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, + posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. + posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. + posix.EINTR => continue, + posix.EISCONN => unreachable, // The socket is already connected. + posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + } + } +} + +/// Same as posixConnect except it is for blocking socket file descriptors. +/// It expects to receive EINPROGRESS. +pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { + while (true) { + const rc = posix.connect(sockfd, sockaddr, len); + const err = posix.getErrno(rc); + switch (err) { + 0, posix.EINPROGRESS => return, + else => return unexpectedErrorPosix(err), + + posix.EACCES => return PosixConnectError.PermissionDenied, + posix.EPERM => return PosixConnectError.PermissionDenied, + posix.EADDRINUSE => return PosixConnectError.AddressInUse, + posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, + posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, + posix.EAGAIN => return PosixConnectError.SystemResources, + posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. + posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, + posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. + posix.EINTR => continue, + posix.EISCONN => unreachable, // The socket is already connected. + posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + } + } +} + +pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { + var err_code: i32 = undefined; + var size: u32 = @sizeOf(i32); + const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); + assert(size == 4); + const err = posix.getErrno(rc); + switch (err) { + 0 => switch (err_code) { + 0 => return, + else => return unexpectedErrorPosix(err), + + posix.EACCES => return PosixConnectError.PermissionDenied, + posix.EPERM => return PosixConnectError.PermissionDenied, + posix.EADDRINUSE => return PosixConnectError.AddressInUse, + posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, + posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, + posix.EAGAIN => return PosixConnectError.SystemResources, + posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. + posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. + posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, + posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. + posix.EISCONN => unreachable, // The socket is already connected. + posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. + posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, + }, + else => return unexpectedErrorPosix(err), + posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. + posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. + posix.EINVAL => unreachable, + posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. + posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. + } +} + +pub const Thread = struct { + data: Data, + + pub const use_pthreads = is_posix and builtin.link_libc; + + /// Represents a kernel thread handle. + /// May be an integer or a pointer depending on the platform. + /// On Linux and POSIX, this is the same as Id. + pub const Handle = if (use_pthreads) + c.pthread_t + else switch (builtin.os) { + builtin.Os.linux => i32, + builtin.Os.windows => windows.HANDLE, + else => @compileError("Unsupported OS"), + }; + + /// Represents a unique ID per thread. + /// May be an integer or pointer depending on the platform. + /// On Linux and POSIX, this is the same as Handle. + pub const Id = switch (builtin.os) { + builtin.Os.windows => windows.DWORD, + else => Handle, + }; + + pub const Data = if (use_pthreads) + struct { + handle: Thread.Handle, + mmap_addr: usize, + mmap_len: usize, + } + else switch (builtin.os) { + builtin.Os.linux => struct { + handle: Thread.Handle, + mmap_addr: usize, + mmap_len: usize, + tls_end_addr: usize, + }, + builtin.Os.windows => struct { + handle: Thread.Handle, + alloc_start: *c_void, + heap_handle: windows.HANDLE, + }, + else => @compileError("Unsupported OS"), + }; + + /// Returns the ID of the calling thread. + /// Makes a syscall every time the function is called. + /// On Linux and POSIX, this Id is the same as a Handle. + pub fn getCurrentId() Id { + if (use_pthreads) { + return c.pthread_self(); + } else + return switch (builtin.os) { + builtin.Os.linux => linux.gettid(), + builtin.Os.windows => windows.GetCurrentThreadId(), + else => @compileError("Unsupported OS"), + }; + } + + /// Returns the handle of this thread. + /// On Linux and POSIX, this is the same as Id. + pub fn handle(self: Thread) Handle { + return self.data.handle; + } + + pub fn wait(self: *const Thread) void { + if (use_pthreads) { + const err = c.pthread_join(self.data.handle, null); + switch (err) { + 0 => {}, + posix.EINVAL => unreachable, + posix.ESRCH => unreachable, + posix.EDEADLK => unreachable, + else => unreachable, + } + assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); + } else switch (builtin.os) { + builtin.Os.linux => { + while (true) { + const pid_value = @atomicLoad(i32, &self.data.handle, builtin.AtomicOrder.SeqCst); + if (pid_value == 0) break; + const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null); + switch (linux.getErrno(rc)) { + 0 => continue, + posix.EINTR => continue, + posix.EAGAIN => continue, + else => unreachable, + } + } + assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); + }, + builtin.Os.windows => { + assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); + assert(windows.CloseHandle(self.data.handle) != 0); + assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); + }, + else => @compileError("Unsupported OS"), + } + } +}; + +pub const SpawnThreadError = error{ + /// A system-imposed limit on the number of threads was encountered. + /// There are a number of limits that may trigger this error: + /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), + /// which limits the number of processes and threads for a real + /// user ID, was reached; + /// * the kernel's system-wide limit on the number of processes and + /// threads, /proc/sys/kernel/threads-max, was reached (see + /// proc(5)); + /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was + /// reached (see proc(5)); or + /// * the PID limit (pids.max) imposed by the cgroup "process num‐ + /// ber" (PIDs) controller was reached. + ThreadQuotaExceeded, + + /// The kernel cannot allocate sufficient memory to allocate a task structure + /// for the child, or to copy those parts of the caller's context that need to + /// be copied. + SystemResources, + + /// Not enough userland memory to spawn the thread. + OutOfMemory, + + /// See https://github.com/ziglang/zig/issues/1396 + 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 +/// caller must call wait on the returned thread +pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { + if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); + // TODO compile-time call graph analysis to determine stack upper bound + // https://github.com/ziglang/zig/issues/157 + const default_stack_size = 8 * 1024 * 1024; + + const Context = @typeOf(context); + comptime assert(@ArgType(@typeOf(startFn), 0) == Context); + + if (builtin.os == builtin.Os.windows) { + const WinThread = struct { + const OuterContext = struct { + thread: Thread, + inner: Context, + }; + extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { + const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; + switch (@typeId(@typeOf(startFn).ReturnType)) { + builtin.TypeId.Int => { + return startFn(arg); + }, + builtin.TypeId.Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), + } + } + }; + + const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory; + const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); + const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; + errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); + const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; + const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; + outer_context.* = WinThread.OuterContext{ + .thread = Thread{ + .data = Thread.Data{ + .heap_handle = heap_handle, + .alloc_start = bytes_ptr, + .handle = undefined, + }, + }, + .inner = context, + }; + + const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); + outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { + const err = windows.GetLastError(); + return switch (err) { + else => os.unexpectedErrorWindows(err), + }; + }; + return &outer_context.thread; + } + + const MainFuncs = struct { + extern fn linuxThreadMain(ctx_addr: usize) u8 { + const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; + + switch (@typeId(@typeOf(startFn).ReturnType)) { + builtin.TypeId.Int => { + return startFn(arg); + }, + builtin.TypeId.Void => { + startFn(arg); + return 0; + }, + else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), + } + } + extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { + if (@sizeOf(Context) == 0) { + _ = startFn({}); + return null; + } else { + _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); + return null; + } + } + }; + + const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; + + var stack_end_offset: usize = undefined; + var thread_start_offset: usize = undefined; + var context_start_offset: usize = undefined; + var tls_start_offset: usize = undefined; + const mmap_len = blk: { + // First in memory will be the stack, which grows downwards. + var l: usize = mem.alignForward(default_stack_size, os.page_size); + stack_end_offset = l; + // Above the stack, so that it can be in the same mmap call, put the Thread object. + l = mem.alignForward(l, @alignOf(Thread)); + thread_start_offset = l; + l += @sizeOf(Thread); + // Next, the Context object. + if (@sizeOf(Context) != 0) { + l = mem.alignForward(l, @alignOf(Context)); + context_start_offset = l; + l += @sizeOf(Context); + } + // 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); + tls_start_offset = l; + l += tls_phdr.p_memsz; + } + } + break :blk l; + }; + const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); + if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; + errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); + + const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); + thread_ptr.data.mmap_addr = mmap_addr; + thread_ptr.data.mmap_len = mmap_len; + + var arg: usize = undefined; + if (@sizeOf(Context) != 0) { + arg = mmap_addr + context_start_offset; + const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); + context_ptr.* = context; + } + + if (Thread.use_pthreads) { + // use pthreads + var attr: c.pthread_attr_t = undefined; + if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; + defer assert(c.pthread_attr_destroy(&attr) == 0); + + assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); + + const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.SystemResources, + posix.EPERM => unreachable, + posix.EINVAL => unreachable, + else => return unexpectedErrorPosix(@intCast(usize, err)), + } + } else if (builtin.os == builtin.Os.linux) { + var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | + 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); + thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; + newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); + 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); + const err = posix.getErrno(rc); + switch (err) { + 0 => return thread_ptr, + posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, + posix.EINVAL => unreachable, + posix.ENOMEM => return SpawnThreadError.SystemResources, + posix.ENOSPC => unreachable, + posix.EPERM => unreachable, + posix.EUSERS => unreachable, + else => return unexpectedErrorPosix(err), + } + } else { + @compileError("Unsupported OS"); + } +} + +pub fn posixWait(pid: i32) i32 { + var status: i32 = undefined; + while (true) { + const err = posix.getErrno(posix.waitpid(pid, &status, 0)); + switch (err) { + 0 => return status, + posix.EINTR => continue, + posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. + posix.EINVAL => unreachable, // The options argument was invalid + else => unreachable, + } + } +} + +pub fn posixFStat(fd: i32) !posix.Stat { + var stat: posix.Stat = undefined; + const err = posix.getErrno(posix.fstat(fd, &stat)); + if (err > 0) { + return switch (err) { + // We do not make this an error code because if you get EBADF it's always a bug, + // since the fd could have been reused. + posix.EBADF => unreachable, + posix.ENOMEM => error.SystemResources, + else => os.unexpectedErrorPosix(err), + }; + } + + return stat; +} + +pub const CpuCountError = error{ + OutOfMemory, + PermissionDenied, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { + switch (builtin.os) { + builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { + var count: c_int = undefined; + var count_len: usize = @sizeOf(c_int); + const rc = posix.sysctlbyname(switch (builtin.os) { + builtin.Os.macosx => c"hw.logicalcpu", + else => c"hw.ncpu", + }, @ptrCast(*c_void, &count), &count_len, null, 0); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(usize, count), + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.ENOMEM => return CpuCountError.OutOfMemory, + posix.ENOTDIR => unreachable, + posix.EISDIR => unreachable, + posix.ENOENT => unreachable, + posix.EPERM => unreachable, + else => return os.unexpectedErrorPosix(err), + } + }, + builtin.Os.linux => { + const usize_count = 16; + const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); + + var set = try allocator.alloc(usize, usize_count); + defer allocator.free(set); + + while (true) { + const rc = posix.sched_getaffinity(0, set); + const err = posix.getErrno(rc); + switch (err) { + 0 => { + if (rc < set.len * @sizeOf(usize)) { + const result = set[0 .. rc / @sizeOf(usize)]; + var sum: usize = 0; + for (result) |x| { + sum += @popCount(x); + } + return sum; + } else { + set = try allocator.realloc(usize, set, set.len * 2); + continue; + } + }, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.EPERM => return CpuCountError.PermissionDenied, + posix.ESRCH => unreachable, + else => return os.unexpectedErrorPosix(err), + } + } + }, + builtin.Os.windows => { + var system_info: windows.SYSTEM_INFO = undefined; + windows.GetSystemInfo(&system_info); + return @intCast(usize, system_info.dwNumberOfProcessors); + }, + else => @compileError("unsupported OS"), + } +} + +pub const BsdKQueueError = error{ + /// The per-process limit on the number of open file descriptors has been reached. + ProcessFdQuotaExceeded, + + /// The system-wide limit on the total number of open files has been reached. + SystemFdQuotaExceeded, + + /// See https://github.com/ziglang/zig/issues/1396 + Unexpected, +}; + +pub fn bsdKQueue() BsdKQueueError!i32 { + const rc = posix.kqueue(); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, + posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, + else => return unexpectedErrorPosix(err), + } +} + +pub const BsdKEventError = error{ + /// The process does not have permission to register a filter. + AccessDenied, + + /// The event could not be found to be modified or deleted. + EventNotFound, + + /// No memory was available to register the event. + SystemResources, + + /// The specified process to attach to does not exist. + ProcessNotFound, +}; + +pub fn bsdKEvent( + kq: i32, + changelist: []const posix.Kevent, + eventlist: []posix.Kevent, + timeout: ?*const posix.timespec, +) BsdKEventError!usize { + while (true) { + const rc = posix.kevent(kq, changelist, eventlist, timeout); + const err = posix.getErrno(rc); + switch (err) { + 0 => return rc, + posix.EACCES => return BsdKEventError.AccessDenied, + posix.EFAULT => unreachable, + posix.EBADF => unreachable, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.ENOENT => return BsdKEventError.EventNotFound, + posix.ENOMEM => return BsdKEventError.SystemResources, + posix.ESRCH => return BsdKEventError.ProcessNotFound, + else => unreachable, + } + } +} + +pub fn linuxINotifyInit1(flags: u32) !i32 { + const rc = linux.inotify_init1(flags); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EINVAL => unreachable, + posix.EMFILE => return error.ProcessFdQuotaExceeded, + posix.ENFILE => return error.SystemFdQuotaExceeded, + posix.ENOMEM => return error.SystemResources, + else => return unexpectedErrorPosix(err), + } +} + +pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { + const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); + const err = posix.getErrno(rc); + switch (err) { + 0 => return @intCast(i32, rc), + posix.EACCES => return error.AccessDenied, + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EINVAL => unreachable, + posix.ENAMETOOLONG => return error.NameTooLong, + posix.ENOENT => return error.FileNotFound, + posix.ENOMEM => return error.SystemResources, + posix.ENOSPC => return error.UserResourceLimitReached, + else => return unexpectedErrorPosix(err), + } +} + +pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { + const rc = linux.inotify_rm_watch(inotify_fd, wd); + const err = posix.getErrno(rc); + switch (err) { + 0 => return rc, + posix.EBADF => unreachable, + posix.EINVAL => unreachable, + else => unreachable, + } +} + +pub const MProtectError = error{ + AccessDenied, + OutOfMemory, + Unexpected, +}; + +/// address and length must be page-aligned +pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { + const negative_page_size = @bitCast(usize, -isize(page_size)); + const aligned_address = address & negative_page_size; + const aligned_end = (address + length + page_size - 1) & negative_page_size; + assert(address == aligned_address); + assert(length == aligned_end - aligned_address); + const rc = posix.mprotect(address, length, protection); + const err = posix.getErrno(rc); + switch (err) { + 0 => return, + posix.EINVAL => unreachable, + posix.EACCES => return error.AccessDenied, + posix.ENOMEM => return error.OutOfMemory, + else => return unexpectedErrorPosix(err), + } +} diff --git a/std/os/child_process.zig b/std/os/child_process.zig index 1da0b3492b..653c776239 100644 --- a/std/os/child_process.zig +++ b/std/os/child_process.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const cstr = std.cstr; const unicode = std.unicode; const io = std.io; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index 3e883abbab..4ce631c6ab 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const c = std.c; const assert = std.debug.assert; const maxInt = std.math.maxInt; diff --git a/std/os/file.zig b/std/os/file.zig index 23469a3c37..c9a0e2b696 100644 --- a/std/os/file.zig +++ b/std/os/file.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const os = std.os; const io = std.io; diff --git a/std/os/freebsd.zig b/std/os/freebsd.zig new file mode 100644 index 0000000000..90e8de1557 --- /dev/null +++ b/std/os/freebsd.zig @@ -0,0 +1,845 @@ +const builtin = @import("builtin"); + +pub use @import("freebsd/errno.zig"); + +const std = @import("../std.zig"); +const c = std.c; + +const assert = std.debug.assert; +const maxInt = std.math.maxInt; +pub const Kevent = c.Kevent; + +pub const CTL_KERN = 1; +pub const CTL_DEBUG = 5; + +pub const KERN_PROC = 14; // struct: process entries +pub const KERN_PROC_PATHNAME = 12; // path to executable + +pub const PATH_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_VIRTUAL = 1; +pub const CLOCK_PROF = 2; +pub const CLOCK_MONOTONIC = 4; +pub const CLOCK_UPTIME = 5; +pub const CLOCK_UPTIME_PRECISE = 7; +pub const CLOCK_UPTIME_FAST = 8; +pub const CLOCK_REALTIME_PRECISE = 9; +pub const CLOCK_REALTIME_FAST = 10; +pub const CLOCK_MONOTONIC_PRECISE = 11; +pub const CLOCK_MONOTONIC_FAST = 12; +pub const CLOCK_SECOND = 13; +pub const CLOCK_THREAD_CPUTIME_ID = 14; +pub const CLOCK_PROCESS_CPUTIME_ID = 15; + +pub const MAP_FAILED = maxInt(usize); +pub const MAP_SHARED = 0x0001; +pub const MAP_PRIVATE = 0x0002; +pub const MAP_FIXED = 0x0010; +pub const MAP_STACK = 0x0400; +pub const MAP_NOSYNC = 0x0800; +pub const MAP_ANON = 0x1000; +pub const MAP_ANONYMOUS = MAP_ANON; +pub const MAP_FILE = 0; +pub const MAP_NORESERVE = 0; + +pub const MAP_GUARD = 0x00002000; +pub const MAP_EXCL = 0x00004000; +pub const MAP_NOCORE = 0x00020000; +pub const MAP_PREFAULT_READ = 0x00040000; +pub const MAP_32BIT = 0x00080000; + +pub const WNOHANG = 1; +pub const WUNTRACED = 2; +pub const WSTOPPED = WUNTRACED; +pub const WCONTINUED = 4; +pub const WNOWAIT = 8; +pub const WEXITED = 16; +pub const WTRAPPED = 32; + +pub const SA_ONSTACK = 0x0001; +pub const SA_RESTART = 0x0002; +pub const SA_RESETHAND = 0x0004; +pub const SA_NOCLDSTOP = 0x0008; +pub const SA_NODEFER = 0x0010; +pub const SA_NOCLDWAIT = 0x0020; +pub const SA_SIGINFO = 0x0040; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGEMT = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGBUS = 10; +pub const SIGSEGV = 11; +pub const SIGSYS = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGURG = 16; +pub const SIGSTOP = 17; +pub const SIGTSTP = 18; +pub const SIGCONT = 19; +pub const SIGCHLD = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGIO = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGINFO = 29; +pub const SIGUSR1 = 30; +pub const SIGUSR2 = 31; +pub const SIGTHR = 32; +pub const SIGLWP = SIGTHR; +pub const SIGLIBRT = 33; + +pub const SIGRTMIN = 65; +pub const SIGRTMAX = 126; + +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission + +pub const O_RDONLY = 0x0000; +pub const O_WRONLY = 0x0001; +pub const O_RDWR = 0x0002; +pub const O_ACCMODE = 0x0003; + +pub const O_CREAT = 0x0200; +pub const O_EXCL = 0x0800; +pub const O_NOCTTY = 0x8000; +pub const O_TRUNC = 0x0400; +pub const O_APPEND = 0x0008; +pub const O_NONBLOCK = 0x0004; +pub const O_DSYNC = 0o10000; +pub const O_SYNC = 0x0080; +pub const O_RSYNC = 0o4010000; +pub const O_DIRECTORY = 0o200000; +pub const O_NOFOLLOW = 0x0100; +pub const O_CLOEXEC = 0x00100000; + +pub const O_ASYNC = 0x0040; +pub const O_DIRECT = 0x00010000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0o1000000; +pub const O_PATH = 0o10000000; +pub const O_TMPFILE = 0o20200000; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_SETOWN = 8; +pub const F_GETOWN = 9; +pub const F_SETSIG = 10; +pub const F_GETSIG = 11; + +pub const F_GETLK = 5; +pub const F_SETLK = 6; +pub const F_SETLKW = 7; + +pub const F_SETOWN_EX = 15; +pub const F_GETOWN_EX = 16; + +pub const F_GETOWNER_UIDS = 17; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 1; +pub const SIG_UNBLOCK = 2; +pub const SIG_SETMASK = 3; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; + +pub const SOCK_CLOEXEC = 0x10000000; +pub const SOCK_NONBLOCK = 0x20000000; + +pub const PROTO_ip = 0o000; +pub const PROTO_icmp = 0o001; +pub const PROTO_igmp = 0o002; +pub const PROTO_ggp = 0o003; +pub const PROTO_ipencap = 0o004; +pub const PROTO_st = 0o005; +pub const PROTO_tcp = 0o006; +pub const PROTO_egp = 0o010; +pub const PROTO_pup = 0o014; +pub const PROTO_udp = 0o021; +pub const PROTO_hmp = 0o024; +pub const PROTO_xns_idp = 0o026; +pub const PROTO_rdp = 0o033; +pub const PROTO_iso_tp4 = 0o035; +pub const PROTO_xtp = 0o044; +pub const PROTO_ddp = 0o045; +pub const PROTO_idpr_cmtp = 0o046; +pub const PROTO_ipv6 = 0o051; +pub const PROTO_ipv6_route = 0o053; +pub const PROTO_ipv6_frag = 0o054; +pub const PROTO_idrp = 0o055; +pub const PROTO_rsvp = 0o056; +pub const PROTO_gre = 0o057; +pub const PROTO_esp = 0o062; +pub const PROTO_ah = 0o063; +pub const PROTO_skip = 0o071; +pub const PROTO_ipv6_icmp = 0o072; +pub const PROTO_ipv6_nonxt = 0o073; +pub const PROTO_ipv6_opts = 0o074; +pub const PROTO_rspf = 0o111; +pub const PROTO_vmtp = 0o121; +pub const PROTO_ospf = 0o131; +pub const PROTO_ipip = 0o136; +pub const PROTO_encap = 0o142; +pub const PROTO_pim = 0o147; +pub const PROTO_raw = 0o377; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_AX25 = 3; +pub const PF_IPX = 4; +pub const PF_APPLETALK = 5; +pub const PF_NETROM = 6; +pub const PF_BRIDGE = 7; +pub const PF_ATMPVC = 8; +pub const PF_X25 = 9; +pub const PF_INET6 = 10; +pub const PF_ROSE = 11; +pub const PF_DECnet = 12; +pub const PF_NETBEUI = 13; +pub const PF_SECURITY = 14; +pub const PF_KEY = 15; +pub const PF_NETLINK = 16; +pub const PF_ROUTE = PF_NETLINK; +pub const PF_PACKET = 17; +pub const PF_ASH = 18; +pub const PF_ECONET = 19; +pub const PF_ATMSVC = 20; +pub const PF_RDS = 21; +pub const PF_SNA = 22; +pub const PF_IRDA = 23; +pub const PF_PPPOX = 24; +pub const PF_WANPIPE = 25; +pub const PF_LLC = 26; +pub const PF_IB = 27; +pub const PF_MPLS = 28; +pub const PF_CAN = 29; +pub const PF_TIPC = 30; +pub const PF_BLUETOOTH = 31; +pub const PF_IUCV = 32; +pub const PF_RXRPC = 33; +pub const PF_ISDN = 34; +pub const PF_PHONET = 35; +pub const PF_IEEE802154 = 36; +pub const PF_CAIF = 37; +pub const PF_ALG = 38; +pub const PF_NFC = 39; +pub const PF_VSOCK = 40; +pub const PF_MAX = 41; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_AX25 = PF_AX25; +pub const AF_IPX = PF_IPX; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_NETROM = PF_NETROM; +pub const AF_BRIDGE = PF_BRIDGE; +pub const AF_ATMPVC = PF_ATMPVC; +pub const AF_X25 = PF_X25; +pub const AF_INET6 = PF_INET6; +pub const AF_ROSE = PF_ROSE; +pub const AF_DECnet = PF_DECnet; +pub const AF_NETBEUI = PF_NETBEUI; +pub const AF_SECURITY = PF_SECURITY; +pub const AF_KEY = PF_KEY; +pub const AF_NETLINK = PF_NETLINK; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_PACKET = PF_PACKET; +pub const AF_ASH = PF_ASH; +pub const AF_ECONET = PF_ECONET; +pub const AF_ATMSVC = PF_ATMSVC; +pub const AF_RDS = PF_RDS; +pub const AF_SNA = PF_SNA; +pub const AF_IRDA = PF_IRDA; +pub const AF_PPPOX = PF_PPPOX; +pub const AF_WANPIPE = PF_WANPIPE; +pub const AF_LLC = PF_LLC; +pub const AF_IB = PF_IB; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_TIPC = PF_TIPC; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_IUCV = PF_IUCV; +pub const AF_RXRPC = PF_RXRPC; +pub const AF_ISDN = PF_ISDN; +pub const AF_PHONET = PF_PHONET; +pub const AF_IEEE802154 = PF_IEEE802154; +pub const AF_CAIF = PF_CAIF; +pub const AF_ALG = PF_ALG; +pub const AF_NFC = PF_NFC; +pub const AF_VSOCK = PF_VSOCK; +pub const AF_MAX = PF_MAX; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = -1; +pub const EVFILT_WRITE = -2; + +/// attached to aio requests +pub const EVFILT_AIO = -3; + +/// attached to vnodes +pub const EVFILT_VNODE = -4; + +/// attached to struct proc +pub const EVFILT_PROC = -5; + +/// attached to struct proc +pub const EVFILT_SIGNAL = -6; + +/// timers +pub const EVFILT_TIMER = -7; + +/// Process descriptors +pub const EVFILT_PROCDESC = -8; + +/// Filesystem events +pub const EVFILT_FS = -9; + +pub const EVFILT_LIO = -10; + +/// User events +pub const EVFILT_USER = -11; + +/// Sendfile events +pub const EVFILT_SENDFILE = -12; + +pub const EVFILT_EMPTY = -13; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x01000000; + +/// ignore input fflags +pub const NOTE_FFNOP = 0x00000000; + +/// and fflags +pub const NOTE_FFAND = 0x40000000; + +/// or fflags +pub const NOTE_FFOR = 0x80000000; + +/// copy fflags +pub const NOTE_FFCOPY = 0xc0000000; + +/// mask for operations +pub const NOTE_FFCTRLMASK = 0xc0000000; +pub const NOTE_FFLAGSMASK = 0x00ffffff; + +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// behave like poll() +pub const NOTE_FILE_POLL = 0x00000002; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// vnode was opened +pub const NOTE_OPEN = 0x00000080; + +/// file closed, fd did not allow write +pub const NOTE_CLOSE = 0x00000100; + +/// file closed, fd did allow write +pub const NOTE_CLOSE_WRITE = 0x00000200; + +/// file was read +pub const NOTE_READ = 0x00000400; + +/// process exited +pub const NOTE_EXIT = 0x80000000; + +/// process forked +pub const NOTE_FORK = 0x40000000; + +/// process exec'd +pub const NOTE_EXEC = 0x20000000; + +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); + +/// data is seconds +pub const NOTE_SECONDS = 0x00000001; + +/// data is milliseconds +pub const NOTE_MSECONDS = 0x00000002; + +/// data is microseconds +pub const NOTE_USECONDS = 0x00000004; + +/// data is nanoseconds +pub const NOTE_NSECONDS = 0x00000008; + +/// timeout is absolute +pub const NOTE_ABSTIME = 0x00000010; + +pub const TCGETS = 0x5401; +pub const TCSETS = 0x5402; +pub const TCSETSW = 0x5403; +pub const TCSETSF = 0x5404; +pub const TCGETA = 0x5405; +pub const TCSETA = 0x5406; +pub const TCSETAW = 0x5407; +pub const TCSETAF = 0x5408; +pub const TCSBRK = 0x5409; +pub const TCXONC = 0x540A; +pub const TCFLSH = 0x540B; +pub const TIOCEXCL = 0x540C; +pub const TIOCNXCL = 0x540D; +pub const TIOCSCTTY = 0x540E; +pub const TIOCGPGRP = 0x540F; +pub const TIOCSPGRP = 0x5410; +pub const TIOCOUTQ = 0x5411; +pub const TIOCSTI = 0x5412; +pub const TIOCGWINSZ = 0x5413; +pub const TIOCSWINSZ = 0x5414; +pub const TIOCMGET = 0x5415; +pub const TIOCMBIS = 0x5416; +pub const TIOCMBIC = 0x5417; +pub const TIOCMSET = 0x5418; +pub const TIOCGSOFTCAR = 0x5419; +pub const TIOCSSOFTCAR = 0x541A; +pub const FIONREAD = 0x541B; +pub const TIOCINQ = FIONREAD; +pub const TIOCLINUX = 0x541C; +pub const TIOCCONS = 0x541D; +pub const TIOCGSERIAL = 0x541E; +pub const TIOCSSERIAL = 0x541F; +pub const TIOCPKT = 0x5420; +pub const FIONBIO = 0x5421; +pub const TIOCNOTTY = 0x5422; +pub const TIOCSETD = 0x5423; +pub const TIOCGETD = 0x5424; +pub const TCSBRKP = 0x5425; +pub const TIOCSBRK = 0x5427; +pub const TIOCCBRK = 0x5428; +pub const TIOCGSID = 0x5429; +pub const TIOCGRS485 = 0x542E; +pub const TIOCSRS485 = 0x542F; +pub const TIOCGPTN = 0x80045430; +pub const TIOCSPTLCK = 0x40045431; +pub const TIOCGDEV = 0x80045432; +pub const TCGETX = 0x5432; +pub const TCSETX = 0x5433; +pub const TCSETXF = 0x5434; +pub const TCSETXW = 0x5435; +pub const TIOCSIG = 0x40045436; +pub const TIOCVHANGUP = 0x5437; +pub const TIOCGPKT = 0x80045438; +pub const TIOCGPTLCK = 0x80045439; +pub const TIOCGEXCL = 0x80045440; + +pub const sockaddr = c.sockaddr; +pub const sockaddr_in = c.sockaddr_in; +pub const sockaddr_in6 = c.sockaddr_in6; + +fn unsigned(s: i32) u32 { + return @bitCast(u32, s); +} +fn signed(s: u32) i32 { + return @bitCast(i32, s); +} +pub fn WEXITSTATUS(s: i32) i32 { + return signed((unsigned(s) & 0xff00) >> 8); +} +pub fn WTERMSIG(s: i32) i32 { + return signed(unsigned(s) & 0x7f); +} +pub fn WSTOPSIG(s: i32) i32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: i32) bool { + return WTERMSIG(s) == 0; +} +pub fn WIFSTOPPED(s: i32) bool { + return @intCast(u16, (((unsigned(s) & 0xffff) *% 0x10001) >> 8)) > 0x7f00; +} +pub fn WIFSIGNALED(s: i32) bool { + return (unsigned(s) & 0xffff) -% 1 < 0xff; +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +/// Get the errno from a syscall return value, or 0 for no error. +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 dup2(old: i32, new: i32) usize { + return errnoWrap(c.dup2(old, new)); +} + +pub fn chdir(path: [*]const u8) usize { + return errnoWrap(c.chdir(path)); +} + +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { + return errnoWrap(c.execve(path, argv, envp)); +} + +pub fn fork() usize { + return errnoWrap(c.fork()); +} + +pub fn access(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.access(path, mode)); +} + +pub fn getcwd(buf: [*]u8, size: usize) usize { + return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; +} + +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { + return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); +} + +pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { + return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); +} + +pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { + return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; +} + +pub fn isatty(fd: i32) bool { + return c.isatty(fd) != 0; +} + +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return errnoWrap(c.readlink(path, buf_ptr, buf_len)); +} + +pub fn mkdir(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.mkdir(path, mode)); +} + +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap( + @ptrCast(?*c_void, address), + length, + @bitCast(c_int, @intCast(c_uint, prot)), + @bitCast(c_int, c_uint(flags)), + fd, + offset, + ); + const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); + return errnoWrap(isize_result); +} + +pub fn munmap(address: usize, length: usize) usize { + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); +} + +pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); +} + +pub fn rmdir(path: [*]const u8) usize { + return errnoWrap(c.rmdir(path)); +} + +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { + return errnoWrap(c.symlink(existing, new)); +} + +pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); +} + +pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { + return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); +} + +pub fn pipe(fd: *[2]i32) usize { + return pipe2(fd, 0); +} + +pub fn pipe2(fd: *[2]i32, flags: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); +} + +pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); +} + +pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); +} + +pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { + return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); +} + +pub fn rename(old: [*]const u8, new: [*]const u8) usize { + return errnoWrap(c.rename(old, new)); +} + +pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { + return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); +} + +pub fn create(path: [*]const u8, perm: usize) usize { + return arch.syscall2(SYS_creat, @ptrToInt(path), perm); +} + +pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { + return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); +} + +pub fn close(fd: i32) usize { + return errnoWrap(c.close(fd)); +} + +pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { + return errnoWrap(c.lseek(fd, offset, whence)); +} + +pub fn exit(code: i32) noreturn { + c.exit(code); +} + +pub fn kill(pid: i32, sig: i32) usize { + return errnoWrap(c.kill(pid, sig)); +} + +pub fn unlink(path: [*]const u8) usize { + return errnoWrap(c.unlink(path)); +} + +pub fn waitpid(pid: i32, status: *i32, options: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); +} + +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { + return errnoWrap(c.nanosleep(req, rem)); +} + +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_gettime(clk_id, tp)); +} + +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_getres(clk_id, tp)); +} + +pub fn setuid(uid: u32) usize { + return errnoWrap(c.setuid(uid)); +} + +pub fn setgid(gid: u32) usize { + return errnoWrap(c.setgid(gid)); +} + +pub fn setreuid(ruid: u32, euid: u32) usize { + return errnoWrap(c.setreuid(ruid, euid)); +} + +pub fn setregid(rgid: u32, egid: u32) usize { + return errnoWrap(c.setregid(rgid, egid)); +} + +const NSIG = 32; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + /// signal handler + __sigaction_u: extern union { + __sa_handler: extern fn (i32) void, + __sa_sigaction: extern fn (i32, *__siginfo, usize) void, + }, + + /// see signal options + sa_flags: u32, + + /// signal mask to apply + sa_mask: sigset_t, +}; + +pub const _SIG_WORDS = 4; +pub const _SIG_MAXSIG = 128; + +pub inline fn _SIG_IDX(sig: usize) usize { + return sig - 1; +} +pub inline fn _SIG_WORD(sig: usize) usize { + return_SIG_IDX(sig) >> 5; +} +pub inline fn _SIG_BIT(sig: usize) usize { + return 1 << (_SIG_IDX(sig) & 31); +} +pub inline fn _SIG_VALID(sig: usize) usize { + return sig <= _SIG_MAXSIG and sig > 0; +} + +pub const sigset_t = extern struct { + __bits: [_SIG_WORDS]u32, +}; + +pub fn raise(sig: i32) usize { + return errnoWrap(c.raise(sig)); +} + +pub const Stat = c.Stat; +pub const dirent = c.dirent; +pub const timespec = c.timespec; + +pub fn fstat(fd: i32, buf: *c.Stat) usize { + return errnoWrap(c.fstat(fd, buf)); +} +pub const iovec = extern struct { + iov_base: [*]u8, + iov_len: usize, +}; + +pub const iovec_const = extern struct { + iov_base: [*]const u8, + iov_len: usize, +}; + +// TODO avoid libc dependency +pub fn kqueue() usize { + return errnoWrap(c.kqueue()); +} + +// TODO avoid libc dependency +pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { + return errnoWrap(c.kevent( + kq, + changelist.ptr, + @intCast(c_int, changelist.len), + eventlist.ptr, + @intCast(c_int, eventlist.len), + timeout, + )); +} + +// TODO avoid libc dependency +pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); +} + +// TODO avoid libc dependency +pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); +} + +// TODO avoid libc dependency +pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { + return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); +} + +// TODO avoid libc dependency + +/// Takes the return value from a syscall and formats it back in the way +/// that the kernel represents it to libc. Errno was a mistake, let's make +/// it go away forever. +fn errnoWrap(value: isize) usize { + return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); +} diff --git a/std/os/freebsd/index.zig b/std/os/freebsd/index.zig deleted file mode 100644 index 48c0ea5b1e..0000000000 --- a/std/os/freebsd/index.zig +++ /dev/null @@ -1,846 +0,0 @@ -const builtin = @import("builtin"); - -pub use @import("errno.zig"); - -const std = @import("../../index.zig"); -const c = std.c; - -const assert = std.debug.assert; -const maxInt = std.math.maxInt; -pub const Kevent = c.Kevent; - -pub const CTL_KERN = 1; -pub const CTL_DEBUG = 5; - -pub const KERN_PROC = 14; // struct: process entries -pub const KERN_PROC_PATHNAME = 12; // path to executable - -pub const PATH_MAX = 1024; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; - -pub const CLOCK_REALTIME = 0; -pub const CLOCK_VIRTUAL = 1; -pub const CLOCK_PROF = 2; -pub const CLOCK_MONOTONIC = 4; -pub const CLOCK_UPTIME = 5; -pub const CLOCK_UPTIME_PRECISE = 7; -pub const CLOCK_UPTIME_FAST = 8; -pub const CLOCK_REALTIME_PRECISE = 9; -pub const CLOCK_REALTIME_FAST = 10; -pub const CLOCK_MONOTONIC_PRECISE = 11; -pub const CLOCK_MONOTONIC_FAST = 12; -pub const CLOCK_SECOND = 13; -pub const CLOCK_THREAD_CPUTIME_ID = 14; -pub const CLOCK_PROCESS_CPUTIME_ID = 15; - -pub const MAP_FAILED = maxInt(usize); -pub const MAP_SHARED = 0x0001; -pub const MAP_PRIVATE = 0x0002; -pub const MAP_FIXED = 0x0010; -pub const MAP_STACK = 0x0400; -pub const MAP_NOSYNC = 0x0800; -pub const MAP_ANON = 0x1000; -pub const MAP_ANONYMOUS = MAP_ANON; -pub const MAP_FILE = 0; -pub const MAP_NORESERVE = 0; - -pub const MAP_GUARD = 0x00002000; -pub const MAP_EXCL = 0x00004000; -pub const MAP_NOCORE = 0x00020000; -pub const MAP_PREFAULT_READ = 0x00040000; -pub const MAP_32BIT = 0x00080000; - -pub const WNOHANG = 1; -pub const WUNTRACED = 2; -pub const WSTOPPED = WUNTRACED; -pub const WCONTINUED = 4; -pub const WNOWAIT = 8; -pub const WEXITED = 16; -pub const WTRAPPED = 32; - -pub const SA_ONSTACK = 0x0001; -pub const SA_RESTART = 0x0002; -pub const SA_RESETHAND = 0x0004; -pub const SA_NOCLDSTOP = 0x0008; -pub const SA_NODEFER = 0x0010; -pub const SA_NOCLDWAIT = 0x0020; -pub const SA_SIGINFO = 0x0040; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGEMT = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGBUS = 10; -pub const SIGSEGV = 11; -pub const SIGSYS = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGURG = 16; -pub const SIGSTOP = 17; -pub const SIGTSTP = 18; -pub const SIGCONT = 19; -pub const SIGCHLD = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGIO = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGINFO = 29; -pub const SIGUSR1 = 30; -pub const SIGUSR2 = 31; -pub const SIGTHR = 32; -pub const SIGLWP = SIGTHR; -pub const SIGLIBRT = 33; - -pub const SIGRTMIN = 65; -pub const SIGRTMAX = 126; - -// access function -pub const F_OK = 0; // test for existence of file -pub const X_OK = 1; // test for execute or search permission -pub const W_OK = 2; // test for write permission -pub const R_OK = 4; // test for read permission - - -pub const O_RDONLY = 0x0000; -pub const O_WRONLY = 0x0001; -pub const O_RDWR = 0x0002; -pub const O_ACCMODE = 0x0003; - -pub const O_CREAT = 0x0200; -pub const O_EXCL = 0x0800; -pub const O_NOCTTY = 0x8000; -pub const O_TRUNC = 0x0400; -pub const O_APPEND = 0x0008; -pub const O_NONBLOCK = 0x0004; -pub const O_DSYNC = 0o10000; -pub const O_SYNC = 0x0080; -pub const O_RSYNC = 0o4010000; -pub const O_DIRECTORY = 0o200000; -pub const O_NOFOLLOW = 0x0100; -pub const O_CLOEXEC = 0x00100000; - -pub const O_ASYNC = 0x0040; -pub const O_DIRECT = 0x00010000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0o1000000; -pub const O_PATH = 0o10000000; -pub const O_TMPFILE = 0o20200000; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_SETOWN = 8; -pub const F_GETOWN = 9; -pub const F_SETSIG = 10; -pub const F_GETSIG = 11; - -pub const F_GETLK = 5; -pub const F_SETLK = 6; -pub const F_SETLKW = 7; - -pub const F_SETOWN_EX = 15; -pub const F_GETOWN_EX = 16; - -pub const F_GETOWNER_UIDS = 17; - -pub const SEEK_SET = 0; -pub const SEEK_CUR = 1; -pub const SEEK_END = 2; - -pub const SIG_BLOCK = 1; -pub const SIG_UNBLOCK = 2; -pub const SIG_SETMASK = 3; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; - -pub const SOCK_CLOEXEC = 0x10000000; -pub const SOCK_NONBLOCK = 0x20000000; - -pub const PROTO_ip = 0o000; -pub const PROTO_icmp = 0o001; -pub const PROTO_igmp = 0o002; -pub const PROTO_ggp = 0o003; -pub const PROTO_ipencap = 0o004; -pub const PROTO_st = 0o005; -pub const PROTO_tcp = 0o006; -pub const PROTO_egp = 0o010; -pub const PROTO_pup = 0o014; -pub const PROTO_udp = 0o021; -pub const PROTO_hmp = 0o024; -pub const PROTO_xns_idp = 0o026; -pub const PROTO_rdp = 0o033; -pub const PROTO_iso_tp4 = 0o035; -pub const PROTO_xtp = 0o044; -pub const PROTO_ddp = 0o045; -pub const PROTO_idpr_cmtp = 0o046; -pub const PROTO_ipv6 = 0o051; -pub const PROTO_ipv6_route = 0o053; -pub const PROTO_ipv6_frag = 0o054; -pub const PROTO_idrp = 0o055; -pub const PROTO_rsvp = 0o056; -pub const PROTO_gre = 0o057; -pub const PROTO_esp = 0o062; -pub const PROTO_ah = 0o063; -pub const PROTO_skip = 0o071; -pub const PROTO_ipv6_icmp = 0o072; -pub const PROTO_ipv6_nonxt = 0o073; -pub const PROTO_ipv6_opts = 0o074; -pub const PROTO_rspf = 0o111; -pub const PROTO_vmtp = 0o121; -pub const PROTO_ospf = 0o131; -pub const PROTO_ipip = 0o136; -pub const PROTO_encap = 0o142; -pub const PROTO_pim = 0o147; -pub const PROTO_raw = 0o377; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; -pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_AX25 = 3; -pub const PF_IPX = 4; -pub const PF_APPLETALK = 5; -pub const PF_NETROM = 6; -pub const PF_BRIDGE = 7; -pub const PF_ATMPVC = 8; -pub const PF_X25 = 9; -pub const PF_INET6 = 10; -pub const PF_ROSE = 11; -pub const PF_DECnet = 12; -pub const PF_NETBEUI = 13; -pub const PF_SECURITY = 14; -pub const PF_KEY = 15; -pub const PF_NETLINK = 16; -pub const PF_ROUTE = PF_NETLINK; -pub const PF_PACKET = 17; -pub const PF_ASH = 18; -pub const PF_ECONET = 19; -pub const PF_ATMSVC = 20; -pub const PF_RDS = 21; -pub const PF_SNA = 22; -pub const PF_IRDA = 23; -pub const PF_PPPOX = 24; -pub const PF_WANPIPE = 25; -pub const PF_LLC = 26; -pub const PF_IB = 27; -pub const PF_MPLS = 28; -pub const PF_CAN = 29; -pub const PF_TIPC = 30; -pub const PF_BLUETOOTH = 31; -pub const PF_IUCV = 32; -pub const PF_RXRPC = 33; -pub const PF_ISDN = 34; -pub const PF_PHONET = 35; -pub const PF_IEEE802154 = 36; -pub const PF_CAIF = 37; -pub const PF_ALG = 38; -pub const PF_NFC = 39; -pub const PF_VSOCK = 40; -pub const PF_MAX = 41; - -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; -pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_AX25 = PF_AX25; -pub const AF_IPX = PF_IPX; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_NETROM = PF_NETROM; -pub const AF_BRIDGE = PF_BRIDGE; -pub const AF_ATMPVC = PF_ATMPVC; -pub const AF_X25 = PF_X25; -pub const AF_INET6 = PF_INET6; -pub const AF_ROSE = PF_ROSE; -pub const AF_DECnet = PF_DECnet; -pub const AF_NETBEUI = PF_NETBEUI; -pub const AF_SECURITY = PF_SECURITY; -pub const AF_KEY = PF_KEY; -pub const AF_NETLINK = PF_NETLINK; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_PACKET = PF_PACKET; -pub const AF_ASH = PF_ASH; -pub const AF_ECONET = PF_ECONET; -pub const AF_ATMSVC = PF_ATMSVC; -pub const AF_RDS = PF_RDS; -pub const AF_SNA = PF_SNA; -pub const AF_IRDA = PF_IRDA; -pub const AF_PPPOX = PF_PPPOX; -pub const AF_WANPIPE = PF_WANPIPE; -pub const AF_LLC = PF_LLC; -pub const AF_IB = PF_IB; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_TIPC = PF_TIPC; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_IUCV = PF_IUCV; -pub const AF_RXRPC = PF_RXRPC; -pub const AF_ISDN = PF_ISDN; -pub const AF_PHONET = PF_PHONET; -pub const AF_IEEE802154 = PF_IEEE802154; -pub const AF_CAIF = PF_CAIF; -pub const AF_ALG = PF_ALG; -pub const AF_NFC = PF_NFC; -pub const AF_VSOCK = PF_VSOCK; -pub const AF_MAX = PF_MAX; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -pub const EVFILT_READ = -1; -pub const EVFILT_WRITE = -2; - -/// attached to aio requests -pub const EVFILT_AIO = -3; - -/// attached to vnodes -pub const EVFILT_VNODE = -4; - -/// attached to struct proc -pub const EVFILT_PROC = -5; - -/// attached to struct proc -pub const EVFILT_SIGNAL = -6; - -/// timers -pub const EVFILT_TIMER = -7; - -/// Process descriptors -pub const EVFILT_PROCDESC = -8; - -/// Filesystem events -pub const EVFILT_FS = -9; - -pub const EVFILT_LIO = -10; - -/// User events -pub const EVFILT_USER = -11; - -/// Sendfile events -pub const EVFILT_SENDFILE = -12; - -pub const EVFILT_EMPTY = -13; - -/// On input, NOTE_TRIGGER causes the event to be triggered for output. -pub const NOTE_TRIGGER = 0x01000000; - -/// ignore input fflags -pub const NOTE_FFNOP = 0x00000000; - -/// and fflags -pub const NOTE_FFAND = 0x40000000; - -/// or fflags -pub const NOTE_FFOR = 0x80000000; - -/// copy fflags -pub const NOTE_FFCOPY = 0xc0000000; - -/// mask for operations -pub const NOTE_FFCTRLMASK = 0xc0000000; -pub const NOTE_FFLAGSMASK = 0x00ffffff; - -/// low water mark -pub const NOTE_LOWAT = 0x00000001; - -/// behave like poll() -pub const NOTE_FILE_POLL = 0x00000002; - -/// vnode was removed -pub const NOTE_DELETE = 0x00000001; - -/// data contents changed -pub const NOTE_WRITE = 0x00000002; - -/// size increased -pub const NOTE_EXTEND = 0x00000004; - -/// attributes changed -pub const NOTE_ATTRIB = 0x00000008; - -/// link count changed -pub const NOTE_LINK = 0x00000010; - -/// vnode was renamed -pub const NOTE_RENAME = 0x00000020; - -/// vnode access was revoked -pub const NOTE_REVOKE = 0x00000040; - -/// vnode was opened -pub const NOTE_OPEN = 0x00000080; - -/// file closed, fd did not allow write -pub const NOTE_CLOSE = 0x00000100; - -/// file closed, fd did allow write -pub const NOTE_CLOSE_WRITE = 0x00000200; - -/// file was read -pub const NOTE_READ = 0x00000400; - -/// process exited -pub const NOTE_EXIT = 0x80000000; - -/// process forked -pub const NOTE_FORK = 0x40000000; - -/// process exec'd -pub const NOTE_EXEC = 0x20000000; - -/// mask for signal & exit status -pub const NOTE_PDATAMASK = 0x000fffff; -pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); - -/// data is seconds -pub const NOTE_SECONDS = 0x00000001; - -/// data is milliseconds -pub const NOTE_MSECONDS = 0x00000002; - -/// data is microseconds -pub const NOTE_USECONDS = 0x00000004; - -/// data is nanoseconds -pub const NOTE_NSECONDS = 0x00000008; - -/// timeout is absolute -pub const NOTE_ABSTIME = 0x00000010; - -pub const TCGETS = 0x5401; -pub const TCSETS = 0x5402; -pub const TCSETSW = 0x5403; -pub const TCSETSF = 0x5404; -pub const TCGETA = 0x5405; -pub const TCSETA = 0x5406; -pub const TCSETAW = 0x5407; -pub const TCSETAF = 0x5408; -pub const TCSBRK = 0x5409; -pub const TCXONC = 0x540A; -pub const TCFLSH = 0x540B; -pub const TIOCEXCL = 0x540C; -pub const TIOCNXCL = 0x540D; -pub const TIOCSCTTY = 0x540E; -pub const TIOCGPGRP = 0x540F; -pub const TIOCSPGRP = 0x5410; -pub const TIOCOUTQ = 0x5411; -pub const TIOCSTI = 0x5412; -pub const TIOCGWINSZ = 0x5413; -pub const TIOCSWINSZ = 0x5414; -pub const TIOCMGET = 0x5415; -pub const TIOCMBIS = 0x5416; -pub const TIOCMBIC = 0x5417; -pub const TIOCMSET = 0x5418; -pub const TIOCGSOFTCAR = 0x5419; -pub const TIOCSSOFTCAR = 0x541A; -pub const FIONREAD = 0x541B; -pub const TIOCINQ = FIONREAD; -pub const TIOCLINUX = 0x541C; -pub const TIOCCONS = 0x541D; -pub const TIOCGSERIAL = 0x541E; -pub const TIOCSSERIAL = 0x541F; -pub const TIOCPKT = 0x5420; -pub const FIONBIO = 0x5421; -pub const TIOCNOTTY = 0x5422; -pub const TIOCSETD = 0x5423; -pub const TIOCGETD = 0x5424; -pub const TCSBRKP = 0x5425; -pub const TIOCSBRK = 0x5427; -pub const TIOCCBRK = 0x5428; -pub const TIOCGSID = 0x5429; -pub const TIOCGRS485 = 0x542E; -pub const TIOCSRS485 = 0x542F; -pub const TIOCGPTN = 0x80045430; -pub const TIOCSPTLCK = 0x40045431; -pub const TIOCGDEV = 0x80045432; -pub const TCGETX = 0x5432; -pub const TCSETX = 0x5433; -pub const TCSETXF = 0x5434; -pub const TCSETXW = 0x5435; -pub const TIOCSIG = 0x40045436; -pub const TIOCVHANGUP = 0x5437; -pub const TIOCGPKT = 0x80045438; -pub const TIOCGPTLCK = 0x80045439; -pub const TIOCGEXCL = 0x80045440; - -pub const sockaddr = c.sockaddr; -pub const sockaddr_in = c.sockaddr_in; -pub const sockaddr_in6 = c.sockaddr_in6; - -fn unsigned(s: i32) u32 { - return @bitCast(u32, s); -} -fn signed(s: u32) i32 { - return @bitCast(i32, s); -} -pub fn WEXITSTATUS(s: i32) i32 { - return signed((unsigned(s) & 0xff00) >> 8); -} -pub fn WTERMSIG(s: i32) i32 { - return signed(unsigned(s) & 0x7f); -} -pub fn WSTOPSIG(s: i32) i32 { - return WEXITSTATUS(s); -} -pub fn WIFEXITED(s: i32) bool { - return WTERMSIG(s) == 0; -} -pub fn WIFSTOPPED(s: i32) bool { - return @intCast(u16, (((unsigned(s) & 0xffff) *% 0x10001) >> 8)) > 0x7f00; -} -pub fn WIFSIGNALED(s: i32) bool { - return (unsigned(s) & 0xffff) -% 1 < 0xff; -} - -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, -}; - -/// Get the errno from a syscall return value, or 0 for no error. -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 dup2(old: i32, new: i32) usize { - return errnoWrap(c.dup2(old, new)); -} - -pub fn chdir(path: [*]const u8) usize { - return errnoWrap(c.chdir(path)); -} - -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return errnoWrap(c.execve(path, argv, envp)); -} - -pub fn fork() usize { - return errnoWrap(c.fork()); -} - -pub fn access(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.access(path, mode)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); -} - -pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { - return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); -} - -pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { - return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn isatty(fd: i32) bool { - return c.isatty(fd) != 0; -} - -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return errnoWrap(c.readlink(path, buf_ptr, buf_len)); -} - -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.mkdir(path, mode)); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap( - @ptrCast(?*c_void, address), - length, - @bitCast(c_int, @intCast(c_uint, prot)), - @bitCast(c_int, c_uint(flags)), - fd, - offset, - ); - const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); - return errnoWrap(isize_result); -} - -pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); -} - -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); -} - -pub fn rmdir(path: [*]const u8) usize { - return errnoWrap(c.rmdir(path)); -} - -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.symlink(existing, new)); -} - -pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); -} - -pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { - return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); -} - -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); -} - -pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); -} - -pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { - return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.rename(old, new)); -} - -pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { - return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); -} - -pub fn create(path: [*]const u8, perm: usize) usize { - return arch.syscall2(SYS_creat, @ptrToInt(path), perm); -} - -pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); -} - -pub fn close(fd: i32) usize { - return errnoWrap(c.close(fd)); -} - -pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { - return errnoWrap(c.lseek(fd, offset, whence)); -} - -pub fn exit(code: i32) noreturn { - c.exit(code); -} - -pub fn kill(pid: i32, sig: i32) usize { - return errnoWrap(c.kill(pid, sig)); -} - -pub fn unlink(path: [*]const u8) usize { - return errnoWrap(c.unlink(path)); -} - -pub fn waitpid(pid: i32, status: *i32, options: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return errnoWrap(c.nanosleep(req, rem)); -} - -pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_gettime(clk_id, tp)); -} - -pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_getres(clk_id, tp)); -} - -pub fn setuid(uid: u32) usize { - return errnoWrap(c.setuid(uid)); -} - -pub fn setgid(gid: u32) usize { - return errnoWrap(c.setgid(gid)); -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return errnoWrap(c.setreuid(ruid, euid)); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return errnoWrap(c.setregid(rgid, egid)); -} - -const NSIG = 32; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { - /// signal handler - __sigaction_u: extern union { - __sa_handler: extern fn (i32) void, - __sa_sigaction: extern fn (i32, *__siginfo, usize) void, - }, - - /// see signal options - sa_flags: u32, - - /// signal mask to apply - sa_mask: sigset_t, -}; - -pub const _SIG_WORDS = 4; -pub const _SIG_MAXSIG = 128; - -pub inline fn _SIG_IDX(sig: usize) usize { - return sig - 1; -} -pub inline fn _SIG_WORD(sig: usize) usize { - return_SIG_IDX(sig) >> 5; -} -pub inline fn _SIG_BIT(sig: usize) usize { - return 1 << (_SIG_IDX(sig) & 31); -} -pub inline fn _SIG_VALID(sig: usize) usize { - return sig <= _SIG_MAXSIG and sig > 0; -} - -pub const sigset_t = extern struct { - __bits: [_SIG_WORDS]u32, -}; - -pub fn raise(sig: i32) usize { - return errnoWrap(c.raise(sig)); -} - -pub const Stat = c.Stat; -pub const dirent = c.dirent; -pub const timespec = c.timespec; - -pub fn fstat(fd: i32, buf: *c.Stat) usize { - return errnoWrap(c.fstat(fd, buf)); -} -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, -}; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -// TODO avoid libc dependency -pub fn kqueue() usize { - return errnoWrap(c.kqueue()); -} - -// TODO avoid libc dependency -pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { - return errnoWrap(c.kevent( - kq, - changelist.ptr, - @intCast(c_int, changelist.len), - eventlist.ptr, - @intCast(c_int, eventlist.len), - timeout, - )); -} - -// TODO avoid libc dependency -pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { - return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); -} - -// TODO avoid libc dependency - -/// Takes the return value from a syscall and formats it back in the way -/// that the kernel represents it to libc. Errno was a mistake, let's make -/// it go away forever. -fn errnoWrap(value: isize) usize { - return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); -} diff --git a/std/os/get_app_data_dir.zig b/std/os/get_app_data_dir.zig index a49d164f46..e69c03edb9 100644 --- a/std/os/get_app_data_dir.zig +++ b/std/os/get_app_data_dir.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const unicode = std.unicode; const mem = std.mem; diff --git a/std/os/get_user_id.zig b/std/os/get_user_id.zig index 6747cc4557..db44c6838f 100644 --- a/std/os/get_user_id.zig +++ b/std/os/get_user_id.zig @@ -1,6 +1,6 @@ const builtin = @import("builtin"); const Os = builtin.Os; -const os = @import("index.zig"); +const os = @import("../os.zig"); const io = @import("../io.zig"); pub const UserInfo = struct { diff --git a/std/os/index.zig b/std/os/index.zig deleted file mode 100644 index e483bf2dbd..0000000000 --- a/std/os/index.zig +++ /dev/null @@ -1,3432 +0,0 @@ -const std = @import("../index.zig"); -const builtin = @import("builtin"); -const Os = builtin.Os; -const is_windows = builtin.os == Os.windows; -const is_posix = switch (builtin.os) { - builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, - else => false, -}; -const os = @This(); - -comptime { - assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir -} - -test "std.os" { - _ = @import("child_process.zig"); - _ = @import("darwin.zig"); - _ = @import("darwin/errno.zig"); - _ = @import("get_user_id.zig"); - _ = @import("linux/index.zig"); - _ = @import("path.zig"); - _ = @import("test.zig"); - _ = @import("time.zig"); - _ = @import("windows/index.zig"); - _ = @import("uefi.zig"); - _ = @import("get_app_data_dir.zig"); -} - -pub const windows = @import("windows/index.zig"); -pub const darwin = @import("darwin.zig"); -pub const linux = @import("linux/index.zig"); -pub const freebsd = @import("freebsd/index.zig"); -pub const netbsd = @import("netbsd/index.zig"); -pub const zen = @import("zen.zig"); -pub const uefi = @import("uefi.zig"); - -pub const posix = switch (builtin.os) { - Os.linux => linux, - Os.macosx, Os.ios => darwin, - Os.freebsd => freebsd, - Os.netbsd => netbsd, - Os.zen => zen, - else => @compileError("Unsupported OS"), -}; - -pub const net = @import("net.zig"); - -pub const ChildProcess = @import("child_process.zig").ChildProcess; -pub const path = @import("path.zig"); -pub const File = @import("file.zig").File; -pub const time = @import("time.zig"); - -pub const page_size = 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. - // If it would require 4 UTF-8 bytes, then there would be a surrogate - // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. - // +1 for the null byte at the end, which can be encoded in 1 byte. - Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, - else => @compileError("Unsupported OS"), -}; - -pub const UserInfo = @import("get_user_id.zig").UserInfo; -pub const getUserInfo = @import("get_user_id.zig").getUserInfo; - -const windows_util = @import("windows/util.zig"); -pub const windowsWaitSingle = windows_util.windowsWaitSingle; -pub const windowsWrite = windows_util.windowsWrite; -pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; -pub const windowsOpen = windows_util.windowsOpen; -pub const windowsOpenW = windows_util.windowsOpenW; -pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; - -pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError; -pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort; - -pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError; -pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus; - -pub const WindowsWaitResult = windows_util.WindowsWaitResult; -pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus; - -pub const WindowsWaitError = windows_util.WaitError; -pub const WindowsOpenError = windows_util.OpenError; -pub const WindowsWriteError = windows_util.WriteError; -pub const WindowsReadError = windows_util.ReadError; - -pub const FileHandle = if (is_windows) windows.HANDLE else i32; - -pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir; -pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError; - -const debug = std.debug; -const assert = debug.assert; -const testing = std.testing; - -const c = std.c; - -const mem = std.mem; -const Allocator = mem.Allocator; - -const BufMap = std.BufMap; -const cstr = std.cstr; - -const io = std.io; -const base64 = std.base64; -const ArrayList = std.ArrayList; -const Buffer = std.Buffer; -const math = std.math; - -/// Fills `buf` with random bytes. If linking against libc, this calls the -/// appropriate OS-specific library call. Otherwise it uses the zig standard -/// library implementation. -pub fn getRandomBytes(buf: []u8) !void { - switch (builtin.os) { - Os.linux => while (true) { - // TODO check libc version and potentially call c.getrandom. - // See #397 - const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); - switch (errno) { - 0 => return, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EINTR => continue, - posix.ENOSYS => return getRandomBytesDevURandom(buf), - else => return unexpectedErrorPosix(errno), - } - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), - Os.windows => { - // Call RtlGenRandom() instead of CryptGetRandom() on Windows - // https://github.com/rust-lang-nursery/rand/issues/111 - // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 - if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { - const err = windows.GetLastError(); - return switch (err) { - else => unexpectedErrorWindows(err), - }; - } - }, - Os.zen => { - const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; - var i: usize = 0; - while (i < buf.len) : (i += 1) { - if (i > randomness.len) return error.Unknown; - buf[i] = randomness[i]; - } - }, - else => @compileError("Unsupported OS"), - } -} - -fn getRandomBytesDevURandom(buf: []u8) !void { - const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); - defer close(fd); - - const stream = &File.openHandle(fd).inStream().stream; - stream.readNoEof(buf) catch |err| switch (err) { - error.EndOfStream => unreachable, - error.OperationAborted => unreachable, - error.BrokenPipe => unreachable, - error.Unexpected => return error.Unexpected, - error.InputOutput => return error.Unexpected, - error.SystemResources => return error.Unexpected, - error.IsDir => unreachable, - }; -} - -test "os.getRandomBytes" { - var buf_a: [50]u8 = undefined; - var buf_b: [50]u8 = undefined; - // Call Twice - try getRandomBytes(buf_a[0..]); - try getRandomBytes(buf_b[0..]); - - // Check if random (not 100% conclusive) - testing.expect(!mem.eql(u8, buf_a, buf_b)); -} - -/// Raises a signal in the current kernel thread, ending its execution. -/// If linking against libc, this calls the abort() libc function. Otherwise -/// it uses the zig standard library implementation. -pub fn abort() noreturn { - @setCold(true); - if (builtin.link_libc) { - c.abort(); - } - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - _ = posix.raise(posix.SIGABRT); - _ = posix.raise(posix.SIGKILL); - while (true) {} - }, - Os.windows => { - if (builtin.mode == builtin.Mode.Debug) { - @breakpoint(); - } - windows.ExitProcess(3); - }, - Os.uefi => { - // TODO there's gotta be a better thing to do here than loop forever - while (true) {} - }, - else => @compileError("Unsupported OS"), - } -} - -/// Exits the program cleanly with the specified status code. -pub fn exit(status: u8) noreturn { - @setCold(true); - if (builtin.link_libc) { - c.exit(status); - } - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - posix.exit(status); - }, - Os.windows => { - windows.ExitProcess(status); - }, - else => @compileError("Unsupported OS"), - } -} - -/// When a file descriptor is closed on linux, it pops the first -/// node from this queue and resumes it. -/// Async functions which get the EMFILE error code can suspend, -/// putting their coroutine handle into this list. -/// TODO make this an atomic linked list -pub var emfile_promise_queue = std.LinkedList(promise).init(); - -/// Closes the file handle. Keeps trying if it gets interrupted by a signal. -pub fn close(handle: FileHandle) void { - if (is_windows) { - windows_util.windowsClose(handle); - } else { - while (true) { - const err = posix.getErrno(posix.close(handle)); - switch (err) { - posix.EINTR => continue, - else => { - if (emfile_promise_queue.popFirst()) |p| resume p.data; - return; - }, - } - } - } -} - -pub const PosixReadError = error{ - InputOutput, - SystemResources, - IsDir, - Unexpected, -}; - -/// Returns the number of bytes that were read, which can be less than -/// buf.len. If 0 bytes were read, that means EOF. -pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { - // Linux can return EINVAL when read amount is > 0x7ffff000 - // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 - const max_buf_len = 0x7ffff000; - - var index: usize = 0; - while (index < buf.len) { - const want_to_read = math.min(buf.len - index, usize(max_buf_len)); - const rc = posix.read(fd, buf.ptr + index, want_to_read); - const err = posix.getErrno(rc); - switch (err) { - 0 => { - index += rc; - if (rc == want_to_read) continue; - // Read returned less than buf.len. - return index; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - } - return index; -} - -/// Number of bytes read is returned. Upon reading end-of-file, zero is returned. -pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { - switch (builtin.os) { - builtin.Os.macosx => { - // Darwin does not have preadv but it does have pread. - var off: usize = 0; - var iov_i: usize = 0; - var inner_off: usize = 0; - while (true) { - const v = iov[iov_i]; - const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); - const err = darwin.getErrno(rc); - switch (err) { - 0 => { - off += rc; - inner_off += rc; - if (inner_off == v.iov_len) { - iov_i += 1; - inner_off = 0; - if (iov_i == count) { - return off; - } - } - if (rc == 0) return off; // EOF - continue; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.ESPIPE => unreachable, // fd is not seekable - posix.EAGAIN => unreachable, // this function is not for non blocking - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - } - }, - builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { - const rc = posix.preadv(fd, iov, count, offset); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // don't call this function for non blocking - posix.EBADF => unreachable, // always a race condition - posix.EIO => return error.InputOutput, - posix.EISDIR => return error.IsDir, - posix.ENOBUFS => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("Unsupported OS"), - } -} - -pub const PosixWriteError = error{ - DiskQuota, - FileTooBig, - InputOutput, - NoSpaceLeft, - AccessDenied, - BrokenPipe, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// Calls POSIX write, and keeps trying if it gets interrupted. -pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { - // Linux can return EINVAL when write amount is > 0x7ffff000 - // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 - const max_bytes_len = 0x7ffff000; - - var index: usize = 0; - while (index < bytes.len) { - const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); - const rc = posix.write(fd, bytes.ptr + index, amt_to_write); - const write_err = posix.getErrno(rc); - switch (write_err) { - 0 => { - index += rc; - continue; - }, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(write_err), - } - } -} - -pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { - switch (builtin.os) { - builtin.Os.macosx => { - // Darwin does not have pwritev but it does have pwrite. - var off: usize = 0; - var iov_i: usize = 0; - var inner_off: usize = 0; - while (true) { - const v = iov[iov_i]; - const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); - const err = darwin.getErrno(rc); - switch (err) { - 0 => { - off += rc; - inner_off += rc; - if (inner_off == v.iov_len) { - iov_i += 1; - inner_off = 0; - if (iov_i == count) { - return; - } - } - continue; - }, - posix.EINTR => continue, - posix.ESPIPE => unreachable, // fd is not seekable - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(err), - } - } - }, - builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { - const rc = posix.pwritev(fd, iov, count, offset); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.EFAULT => unreachable, - posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.EDESTADDRREQ => unreachable, // connect was never called - posix.EDQUOT => return PosixWriteError.DiskQuota, - posix.EFBIG => return PosixWriteError.FileTooBig, - posix.EIO => return PosixWriteError.InputOutput, - posix.ENOSPC => return PosixWriteError.NoSpaceLeft, - posix.EPERM => return PosixWriteError.AccessDenied, - posix.EPIPE => return PosixWriteError.BrokenPipe, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("Unsupported OS"), - } -} - -pub const PosixOpenError = error{ - AccessDenied, - FileTooBig, - IsDir, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - FileNotFound, - SystemResources, - NoSpaceLeft, - NotDir, - PathAlreadyExists, - DeviceBusy, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// ::file_path needs to be copied in memory to add a null terminating byte. -/// Calls POSIX open, keeps trying if it gets interrupted, and translates -/// the return value into zig errors. -pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { - const file_path_c = try toPosixPath(file_path); - return posixOpenC(&file_path_c, flags, perm); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { - while (true) { - const result = posix.open(file_path, flags, perm); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EINTR => continue, - - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EACCES => return PosixOpenError.AccessDenied, - posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, - posix.EISDIR => return PosixOpenError.IsDir, - posix.ELOOP => return PosixOpenError.SymLinkLoop, - posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, - posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, - posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, - posix.ENODEV => return PosixOpenError.NoDevice, - posix.ENOENT => return PosixOpenError.FileNotFound, - posix.ENOMEM => return PosixOpenError.SystemResources, - posix.ENOSPC => return PosixOpenError.NoSpaceLeft, - posix.ENOTDIR => return PosixOpenError.NotDir, - posix.EPERM => return PosixOpenError.AccessDenied, - posix.EEXIST => return PosixOpenError.PathAlreadyExists, - posix.EBUSY => return PosixOpenError.DeviceBusy, - else => return unexpectedErrorPosix(err), - } - } - return @intCast(i32, result); - } -} - -/// Used to convert a slice to a null terminated slice on the stack. -/// TODO well defined copy elision -pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { - var path_with_null: [posix.PATH_MAX]u8 = undefined; - if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; - mem.copy(u8, path_with_null[0..], file_path); - path_with_null[file_path.len] = 0; - return path_with_null; -} - -pub fn posixDup2(old_fd: i32, new_fd: i32) !void { - while (true) { - const err = posix.getErrno(posix.dup2(old_fd, new_fd)); - if (err > 0) { - return switch (err) { - posix.EBUSY, posix.EINTR => continue, - posix.EMFILE => error.ProcessFdQuotaExceeded, - posix.EINVAL => unreachable, - else => unexpectedErrorPosix(err), - }; - } - return; - } -} - -pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { - const envp_count = env_map.count(); - const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); - mem.set(?[*]u8, envp_buf, null); - errdefer freeNullDelimitedEnvMap(allocator, envp_buf); - { - var it = env_map.iterator(); - var i: usize = 0; - while (it.next()) |pair| : (i += 1) { - const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); - @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); - env_buf[pair.key.len] = '='; - @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); - env_buf[env_buf.len - 1] = 0; - - envp_buf[i] = env_buf.ptr; - } - assert(i == envp_count); - } - assert(envp_buf[envp_count] == null); - return envp_buf; -} - -pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { - for (envp_buf) |env| { - const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; - allocator.free(env_buf); - } - allocator.free(envp_buf); -} - -/// This function must allocate memory to add a null terminating bytes on path and each arg. -/// It must also convert to KEY=VALUE\0 format for environment variables, and include null -/// pointers after the args and after the environment variables. -/// `argv[0]` is the executable path. -/// This function also uses the PATH environment variable to get the full path to the executable. -pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { - const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); - mem.set(?[*]u8, argv_buf, null); - defer { - for (argv_buf) |arg| { - const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; - allocator.free(arg_buf); - } - allocator.free(argv_buf); - } - for (argv) |arg, i| { - const arg_buf = try allocator.alloc(u8, arg.len + 1); - @memcpy(arg_buf.ptr, arg.ptr, arg.len); - arg_buf[arg.len] = 0; - - argv_buf[i] = arg_buf.ptr; - } - argv_buf[argv.len] = null; - - const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); - defer freeNullDelimitedEnvMap(allocator, envp_buf); - - const exe_path = argv[0]; - if (mem.indexOfScalar(u8, exe_path, '/') != null) { - return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); - } - - const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; - // PATH.len because it is >= the largest search_path - // +1 for the / to join the search path and exe_path - // +1 for the null terminating byte - const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); - defer allocator.free(path_buf); - var it = mem.tokenize(PATH, ":"); - var seen_eacces = false; - var err: usize = undefined; - while (it.next()) |search_path| { - mem.copy(u8, path_buf, search_path); - path_buf[search_path.len] = '/'; - mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); - path_buf[search_path.len + exe_path.len + 1] = 0; - err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); - assert(err > 0); - if (err == posix.EACCES) { - seen_eacces = true; - } else if (err != posix.ENOENT) { - return posixExecveErrnoToErr(err); - } - } - if (seen_eacces) { - err = posix.EACCES; - } - return posixExecveErrnoToErr(err); -} - -pub const PosixExecveError = error{ - SystemResources, - AccessDenied, - InvalidExe, - FileSystem, - IsDir, - FileNotFound, - NotDir, - FileBusy, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -fn posixExecveErrnoToErr(err: usize) PosixExecveError { - assert(err > 0); - switch (err) { - posix.EFAULT => unreachable, - posix.E2BIG => return error.SystemResources, - posix.EMFILE => return error.SystemResources, - posix.ENAMETOOLONG => return error.SystemResources, - posix.ENFILE => return error.SystemResources, - posix.ENOMEM => return error.SystemResources, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EINVAL => return error.InvalidExe, - posix.ENOEXEC => return error.InvalidExe, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.FileSystem, - posix.EISDIR => return error.IsDir, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ETXTBSY => return error.FileBusy, - else => return unexpectedErrorPosix(err), - } -} - -pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; -pub var posix_environ_raw: [][*]u8 = undefined; - -/// See std.elf for the constants. -pub fn linuxGetAuxVal(index: usize) usize { - if (builtin.link_libc) { - return usize(std.c.getauxval(index)); - } else if (linux_elf_aux_maybe) |auxv| { - var i: usize = 0; - while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { - if (auxv[i].a_type == index) - return auxv[i].a_un.a_val; - } - } - return 0; -} - -pub fn getBaseAddress() usize { - switch (builtin.os) { - builtin.Os.linux => { - const base = linuxGetAuxVal(std.elf.AT_BASE); - if (base != 0) { - return base; - } - const phdr = linuxGetAuxVal(std.elf.AT_PHDR); - return phdr - @sizeOf(std.elf.Ehdr); - }, - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - return @ptrToInt(&std.c._mh_execute_header); - }, - builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), - else => @compileError("Unsupported OS"), - } -} - -/// Caller must free result when done. -/// TODO make this go through libc when we have it -pub fn getEnvMap(allocator: *Allocator) !BufMap { - var result = BufMap.init(allocator); - errdefer result.deinit(); - - if (is_windows) { - const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; - defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); - - var i: usize = 0; - while (true) { - if (ptr[i] == 0) return result; - - const key_start = i; - - while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} - const key_w = ptr[key_start..i]; - const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); - errdefer allocator.free(key); - - if (ptr[i] == '=') i += 1; - - const value_start = i; - while (ptr[i] != 0) : (i += 1) {} - const value_w = ptr[value_start..i]; - const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); - errdefer allocator.free(value); - - i += 1; // skip over null byte - - try result.setMove(key, value); - } - } else { - for (posix_environ_raw) |ptr| { - var line_i: usize = 0; - while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} - const key = ptr[0..line_i]; - - var end_i: usize = line_i; - while (ptr[end_i] != 0) : (end_i += 1) {} - const value = ptr[line_i + 1 .. end_i]; - - try result.set(key, value); - } - return result; - } -} - -test "os.getEnvMap" { - var env = try getEnvMap(std.debug.global_allocator); - defer env.deinit(); -} - -/// TODO make this go through libc when we have it -pub fn getEnvPosix(key: []const u8) ?[]const u8 { - for (posix_environ_raw) |ptr| { - var line_i: usize = 0; - while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} - const this_key = ptr[0..line_i]; - if (!mem.eql(u8, key, this_key)) continue; - - var end_i: usize = line_i; - while (ptr[end_i] != 0) : (end_i += 1) {} - const this_value = ptr[line_i + 1 .. end_i]; - - return this_value; - } - return null; -} - -pub const GetEnvVarOwnedError = error{ - OutOfMemory, - EnvironmentVariableNotFound, - - /// See https://github.com/ziglang/zig/issues/1774 - InvalidUtf8, -}; - -/// Caller must free returned memory. -/// TODO make this go through libc when we have it -pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { - if (is_windows) { - const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); - defer allocator.free(key_with_null); - - var buf = try allocator.alloc(u16, 256); - defer allocator.free(buf); - - while (true) { - const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; - const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); - - if (result == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, - else => { - _ = unexpectedErrorWindows(err); - return error.EnvironmentVariableNotFound; - }, - }; - } - - if (result > buf.len) { - buf = try allocator.realloc(u16, buf, result); - continue; - } - - return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { - error.DanglingSurrogateHalf => return error.InvalidUtf8, - error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, - error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, - error.OutOfMemory => return error.OutOfMemory, - }; - } - } else { - const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; - return mem.dupe(allocator, u8, result); - } -} - -test "os.getEnvVarOwned" { - var ga = debug.global_allocator; - testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); -} - -/// Caller must free the returned memory. -pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { - var buf: [MAX_PATH_BYTES]u8 = undefined; - return mem.dupe(allocator, u8, try getCwd(&buf)); -} - -pub const GetCwdError = error{Unexpected}; - -/// The result is a slice of out_buffer. -pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { - switch (builtin.os) { - Os.windows => { - var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast - const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast - const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); - if (result == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } - assert(result <= utf16le_buf.len); - const utf16le_slice = utf16le_buf[0..result]; - // Trust that Windows gives us valid UTF-16LE. - const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; - return out_buffer[0..end_index]; - }, - else => { - const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); - switch (err) { - 0 => return cstr.toSlice(out_buffer), - posix.ERANGE => unreachable, - else => return unexpectedErrorPosix(err), - } - }, - } -} - -test "os.getCwd" { - // at least call it so it gets compiled - _ = getCwdAlloc(debug.global_allocator); - var buf: [MAX_PATH_BYTES]u8 = undefined; - _ = getCwd(&buf); -} - -pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; - -/// TODO add a symLinkC variant -pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { - if (is_windows) { - return symLinkWindows(existing_path, new_path); - } else { - return symLinkPosix(existing_path, new_path); - } -} - -pub const WindowsSymLinkError = error{ - NameTooLong, - InvalidUtf8, - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { - if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { - const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); - const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); - return symLinkW(&existing_path_w, &new_path_w); -} - -pub const PosixSymLinkError = error{ - AccessDenied, - DiskQuota, - PathAlreadyExists, - FileSystem, - SymLinkLoop, - NameTooLong, - FileNotFound, - SystemResources, - NoSpaceLeft, - ReadOnlyFileSystem, - NotDir, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { - const err = posix.getErrno(posix.symlink(existing_path, new_path)); - switch (err) { - 0 => return, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EDQUOT => return error.DiskQuota, - posix.EEXIST => return error.PathAlreadyExists, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { - const existing_path_c = try toPosixPath(existing_path); - const new_path_c = try toPosixPath(new_path); - return symLinkPosixC(&existing_path_c, &new_path_c); -} - -// here we replace the standard +/ with -_ so that it can be used in a file name -const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); - -/// TODO remove the allocator requirement from this API -pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { - if (symLink(existing_path, new_path)) { - return; - } else |err| switch (err) { - error.PathAlreadyExists => {}, - else => return err, // TODO zig should know this set does not include PathAlreadyExists - } - - const dirname = os.path.dirname(new_path) orelse "."; - - var rand_buf: [12]u8 = undefined; - const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); - defer allocator.free(tmp_path); - mem.copy(u8, tmp_path[0..], dirname); - tmp_path[dirname.len] = os.path.sep; - while (true) { - try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); - - if (symLink(existing_path, tmp_path)) { - return rename(tmp_path, new_path); - } else |err| switch (err) { - error.PathAlreadyExists => continue, - else => return err, // TODO zig should know this set does not include PathAlreadyExists - } - } -} - -pub const DeleteFileError = error{ - FileNotFound, - AccessDenied, - FileBusy, - FileSystem, - IsDir, - SymLinkLoop, - NameTooLong, - NotDir, - SystemResources, - ReadOnlyFileSystem, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn deleteFile(file_path: []const u8) DeleteFileError!void { - if (builtin.os == Os.windows) { - const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); - return deleteFileW(&file_path_w); - } else { - const file_path_c = try toPosixPath(file_path); - return deleteFileC(&file_path_c); - } -} - -pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { - if (windows.DeleteFileW(file_path) == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, - windows.ERROR.ACCESS_DENIED => return error.AccessDenied, - windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, - windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { - if (is_windows) { - const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); - return deleteFileW(&file_path_w); - } else { - const err = posix.getErrno(posix.unlink(file_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EIO => return error.FileSystem, - posix.EISDIR => return error.IsDir, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } - } -} - -/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is -/// merged and readily available, -/// there is a possibility of power loss or application termination leaving temporary files present -/// in the same directory as dest_path. -/// Destination file will have the same mode as the source file. -pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { - var in_file = try os.File.openRead(source_path); - defer in_file.close(); - - const mode = try in_file.mode(); - - 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..]); - try atomic_file.file.write(buf[0..amt]); - if (amt != buf.len) { - return atomic_file.finish(); - } - } -} - -/// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is -/// merged and readily available, -/// there is a possibility of power loss or application termination leaving temporary files present -pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { - var in_file = try os.File.openRead(source_path); - defer in_file.close(); - - 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.read(buf[0..]); - try atomic_file.file.write(buf[0..amt]); - if (amt != buf.len) { - return atomic_file.finish(); - } - } -} - -pub const AtomicFile = struct { - file: os.File, - tmp_path_buf: [MAX_PATH_BYTES]u8, - dest_path: []const u8, - finished: bool, - - const InitError = os.File.OpenError; - - /// dest_path must remain valid for the lifetime of AtomicFile - /// call finish to atomically replace dest_path with contents - /// TODO once we have null terminated pointers, use the - /// openWriteNoClobberN function - pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { - const dirname = os.path.dirname(dest_path); - var rand_buf: [12]u8 = undefined; - const dirname_component_len = if (dirname) |d| d.len + 1 else 0; - const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); - const tmp_path_len = dirname_component_len + encoded_rand_len; - var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; - if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; - - if (dirname) |dir| { - mem.copy(u8, tmp_path_buf[0..], dir); - tmp_path_buf[dir.len] = os.path.sep; - } - - tmp_path_buf[tmp_path_len] = 0; - - while (true) { - try getRandomBytes(rand_buf[0..]); - b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); - - const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) { - error.PathAlreadyExists => continue, - // TODO zig should figure out that this error set does not include PathAlreadyExists since - // it is handled in the above switch - else => return err, - }; - - return AtomicFile{ - .file = file, - .tmp_path_buf = tmp_path_buf, - .dest_path = dest_path, - .finished = false, - }; - } - } - - /// always call deinit, even after successful finish() - pub fn deinit(self: *AtomicFile) void { - if (!self.finished) { - self.file.close(); - deleteFileC(&self.tmp_path_buf) catch {}; - self.finished = true; - } - } - - pub fn finish(self: *AtomicFile) !void { - assert(!self.finished); - self.file.close(); - self.finished = true; - if (is_posix) { - const dest_path_c = try toPosixPath(self.dest_path); - return renameC(&self.tmp_path_buf, &dest_path_c); - } else if (is_windows) { - const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); - const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); - return renameW(&tmp_path_w, &dest_path_w); - } else { - @compileError("Unsupported OS"); - } - } -}; - -pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { - if (is_windows) { - const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); - const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); - return renameW(&old_path_w, &new_path_w); - } else { - const err = posix.getErrno(posix.rename(old_path, new_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EDQUOT => return error.DiskQuota, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EISDIR => return error.IsDir, - posix.ELOOP => return error.SymLinkLoop, - posix.EMLINK => return error.LinkQuotaExceeded, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOTDIR => return error.NotDir, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.EEXIST => return error.PathAlreadyExists, - posix.ENOTEMPTY => return error.PathAlreadyExists, - posix.EROFS => return error.ReadOnlyFileSystem, - posix.EXDEV => return error.RenameAcrossMountPoints, - else => return unexpectedErrorPosix(err), - } - } -} - -pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { - const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; - if (windows.MoveFileExW(old_path, new_path, flags) == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } -} - -pub fn rename(old_path: []const u8, new_path: []const u8) !void { - if (is_windows) { - const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); - const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); - return renameW(&old_path_w, &new_path_w); - } else { - const old_path_c = try toPosixPath(old_path); - const new_path_c = try toPosixPath(new_path); - return renameC(&old_path_c, &new_path_c); - } -} - -pub fn makeDir(dir_path: []const u8) !void { - if (is_windows) { - return makeDirWindows(dir_path); - } else { - return makeDirPosix(dir_path); - } -} - -pub fn makeDirWindows(dir_path: []const u8) !void { - const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); - - if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { - const err = windows.GetLastError(); - return switch (err) { - windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, - windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, - else => unexpectedErrorWindows(err), - }; - } -} - -pub fn makeDirPosixC(dir_path: [*]const u8) !void { - const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EDQUOT => return error.DiskQuota, - posix.EEXIST => return error.PathAlreadyExists, - posix.EFAULT => unreachable, - posix.ELOOP => return error.SymLinkLoop, - posix.EMLINK => return error.LinkQuotaExceeded, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.NoSpaceLeft, - posix.ENOTDIR => return error.NotDir, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -pub fn makeDirPosix(dir_path: []const u8) !void { - const dir_path_c = try toPosixPath(dir_path); - return makeDirPosixC(&dir_path_c); -} - -/// Calls makeDir recursively to make an entire path. Returns success if the path -/// already exists and is a directory. -/// TODO determine if we can remove the allocator requirement from this function -pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { - const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); - defer allocator.free(resolved_path); - - var end_index: usize = resolved_path.len; - while (true) { - makeDir(resolved_path[0..end_index]) catch |err| switch (err) { - error.PathAlreadyExists => { - // TODO stat the file and return an error if it's not a directory - // this is important because otherwise a dangling symlink - // could cause an infinite loop - if (end_index == resolved_path.len) return; - }, - error.FileNotFound => { - // march end_index backward until next path component - while (true) { - end_index -= 1; - if (os.path.isSep(resolved_path[end_index])) break; - } - continue; - }, - else => return err, - }; - if (end_index == resolved_path.len) return; - // march end_index forward until next path component - while (true) { - end_index += 1; - if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; - } - } -} - -pub const DeleteDirError = error{ - AccessDenied, - FileBusy, - SymLinkLoop, - NameTooLong, - FileNotFound, - SystemResources, - NotDir, - DirNotEmpty, - ReadOnlyFileSystem, - InvalidUtf8, - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { - switch (builtin.os) { - Os.windows => { - const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); - return deleteDirW(&dir_path_w); - }, - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const err = posix.getErrno(posix.rmdir(dir_path)); - switch (err) { - 0 => return, - posix.EACCES => return error.AccessDenied, - posix.EPERM => return error.AccessDenied, - posix.EBUSY => return error.FileBusy, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - posix.EEXIST => return error.DirNotEmpty, - posix.ENOTEMPTY => return error.DirNotEmpty, - posix.EROFS => return error.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } - }, - else => @compileError("unimplemented"), - } -} - -pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { - if (windows.RemoveDirectoryW(dir_path_w) == 0) { - const err = windows.GetLastError(); - switch (err) { - windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, - windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, - else => return unexpectedErrorWindows(err), - } - } -} - -/// Returns ::error.DirNotEmpty if the directory is not empty. -/// To delete a directory recursively, see ::deleteTree -pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { - switch (builtin.os) { - Os.windows => { - const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); - return deleteDirW(&dir_path_w); - }, - Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const dir_path_c = try toPosixPath(dir_path); - return deleteDirC(&dir_path_c); - }, - else => @compileError("unimplemented"), - } -} - -/// Whether ::full_path describes a symlink, file, or directory, this function -/// removes it. If it cannot be removed because it is a non-empty directory, -/// this function recursively removes its entries and then tries again. -const DeleteTreeError = error{ - OutOfMemory, - AccessDenied, - FileTooBig, - IsDir, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - SystemResources, - NoSpaceLeft, - PathAlreadyExists, - ReadOnlyFileSystem, - NotDir, - FileNotFound, - FileSystem, - FileBusy, - DirNotEmpty, - DeviceBusy, - - /// On Windows, file paths must be valid Unicode. - InvalidUtf8, - - /// On Windows, file paths cannot contain these characters: - /// '/', '*', '?', '"', '<', '>', '|' - BadPathName, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// TODO determine if we can remove the allocator requirement -pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { - start_over: while (true) { - var got_access_denied = false; - // First, try deleting the item as a file. This way we don't follow sym links. - if (deleteFile(full_path)) { - return; - } else |err| switch (err) { - error.FileNotFound => return, - error.IsDir => {}, - error.AccessDenied => got_access_denied = true, - - error.InvalidUtf8, - error.SymLinkLoop, - error.NameTooLong, - error.SystemResources, - error.ReadOnlyFileSystem, - error.NotDir, - error.FileSystem, - error.FileBusy, - error.BadPathName, - error.Unexpected, - => return err, - } - { - var dir = Dir.open(allocator, full_path) catch |err| switch (err) { - error.NotDir => { - if (got_access_denied) { - return error.AccessDenied; - } - continue :start_over; - }, - - error.OutOfMemory, - error.AccessDenied, - error.FileTooBig, - error.IsDir, - error.SymLinkLoop, - error.ProcessFdQuotaExceeded, - error.NameTooLong, - error.SystemFdQuotaExceeded, - error.NoDevice, - error.FileNotFound, - error.SystemResources, - error.NoSpaceLeft, - error.PathAlreadyExists, - error.Unexpected, - error.InvalidUtf8, - error.BadPathName, - error.DeviceBusy, - => return err, - }; - defer dir.close(); - - var full_entry_buf = ArrayList(u8).init(allocator); - defer full_entry_buf.deinit(); - - while (try dir.next()) |entry| { - try full_entry_buf.resize(full_path.len + entry.name.len + 1); - const full_entry_path = full_entry_buf.toSlice(); - mem.copy(u8, full_entry_path, full_path); - full_entry_path[full_path.len] = path.sep; - mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); - - try deleteTree(allocator, full_entry_path); - } - } - return deleteDir(full_path); - } -} - -pub const Dir = struct { - handle: Handle, - allocator: *Allocator, - - pub const Handle = switch (builtin.os) { - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct { - fd: i32, - seek: i64, - buf: []u8, - index: usize, - end_index: usize, - }, - Os.linux => struct { - fd: i32, - buf: []u8, - index: usize, - end_index: usize, - }, - Os.windows => struct { - handle: windows.HANDLE, - find_file_data: windows.WIN32_FIND_DATAW, - first: bool, - name_data: [256]u8, - }, - else => @compileError("unimplemented"), - }; - - pub const Entry = struct { - name: []const u8, - kind: Kind, - - pub const Kind = enum { - BlockDevice, - CharacterDevice, - Directory, - NamedPipe, - SymLink, - File, - UnixDomainSocket, - Whiteout, - Unknown, - }; - }; - - pub const OpenError = error{ - FileNotFound, - NotDir, - AccessDenied, - FileTooBig, - IsDir, - SymLinkLoop, - ProcessFdQuotaExceeded, - NameTooLong, - SystemFdQuotaExceeded, - NoDevice, - SystemResources, - NoSpaceLeft, - PathAlreadyExists, - OutOfMemory, - InvalidUtf8, - BadPathName, - DeviceBusy, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, - }; - - /// TODO remove the allocator requirement from this API - pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { - return Dir{ - .allocator = allocator, - .handle = switch (builtin.os) { - Os.windows => blk: { - var find_file_data: windows.WIN32_FIND_DATAW = undefined; - const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data); - break :blk Handle{ - .handle = handle, - .find_file_data = find_file_data, // TODO guaranteed copy elision - .first = true, - .name_data = undefined, - }; - }, - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{ - .fd = try posixOpen( - dir_path, - posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, - 0, - ), - .seek = 0, - .index = 0, - .end_index = 0, - .buf = []u8{}, - }, - Os.linux => Handle{ - .fd = try posixOpen( - dir_path, - posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, - 0, - ), - .index = 0, - .end_index = 0, - .buf = []u8{}, - }, - else => @compileError("unimplemented"), - }, - }; - } - - pub fn close(self: *Dir) void { - switch (builtin.os) { - Os.windows => { - _ = windows.FindClose(self.handle.handle); - }, - Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { - self.allocator.free(self.handle.buf); - os.close(self.handle.fd); - }, - else => @compileError("unimplemented"), - } - } - - /// Memory such as file names referenced in this returned entry becomes invalid - /// with subsequent calls to next, as well as when this `Dir` is deinitialized. - pub fn next(self: *Dir) !?Entry { - switch (builtin.os) { - Os.linux => return self.nextLinux(), - Os.macosx, Os.ios => return self.nextDarwin(), - Os.windows => return self.nextWindows(), - Os.freebsd => return self.nextFreebsd(), - Os.netbsd => return self.nextFreebsd(), - else => @compileError("unimplemented"), - } - } - - fn nextDarwin(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - } - } - if (result == 0) return null; - self.handle.index = 0; - self.handle.end_index = result; - break; - } - } - const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + darwin_entry.d_reclen; - self.handle.index = next_index; - - const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (darwin_entry.d_type) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - posix.DT_WHT => Entry.Kind.Whiteout, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } - - fn nextWindows(self: *Dir) !?Entry { - while (true) { - if (self.handle.first) { - self.handle.first = false; - } else { - if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) - return null; - } - const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); - if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) - continue; - // Trust that Windows gives us valid UTF-16LE - const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; - const name_utf8 = self.handle.name_data[0..name_utf8_len]; - const kind = blk: { - const attrs = self.handle.find_file_data.dwFileAttributes; - if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; - if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; - if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File; - break :blk Entry.Kind.Unknown; - }; - return Entry{ - .name = name_utf8, - .kind = kind, - }; - } - } - - fn nextLinux(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - } - } - if (result == 0) return null; - self.handle.index = 0; - self.handle.end_index = result; - break; - } - } - const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + linux_entry.d_reclen; - self.handle.index = next_index; - - const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); - - // skip . and .. entries - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (linux_entry.d_type) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } - - fn nextFreebsd(self: *Dir) !?Entry { - start_over: while (true) { - if (self.handle.index >= self.handle.end_index) { - if (self.handle.buf.len == 0) { - self.handle.buf = try self.allocator.alloc(u8, page_size); - } - - while (true) { - const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); - const err = posix.getErrno(result); - if (err > 0) { - switch (err) { - posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, - posix.EINVAL => { - self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); - continue; - }, - else => return unexpectedErrorPosix(err), - } - } - if (result == 0) return null; - self.handle.index = 0; - self.handle.end_index = result; - break; - } - } - const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); - const next_index = self.handle.index + freebsd_entry.d_reclen; - self.handle.index = next_index; - - const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { - continue :start_over; - } - - const entry_kind = switch (freebsd_entry.d_type) { - posix.DT_BLK => Entry.Kind.BlockDevice, - posix.DT_CHR => Entry.Kind.CharacterDevice, - posix.DT_DIR => Entry.Kind.Directory, - posix.DT_FIFO => Entry.Kind.NamedPipe, - posix.DT_LNK => Entry.Kind.SymLink, - posix.DT_REG => Entry.Kind.File, - posix.DT_SOCK => Entry.Kind.UnixDomainSocket, - posix.DT_WHT => Entry.Kind.Whiteout, - else => Entry.Kind.Unknown, - }; - return Entry{ - .name = name, - .kind = entry_kind, - }; - } - } -}; - -pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { - const path_buf = try allocator.alloc(u8, dir_path.len + 1); - defer allocator.free(path_buf); - - mem.copy(u8, path_buf, dir_path); - path_buf[dir_path.len] = 0; - - const err = posix.getErrno(posix.chdir(path_buf.ptr)); - if (err > 0) { - return switch (err) { - posix.EACCES => error.AccessDenied, - posix.EFAULT => unreachable, - posix.EIO => error.FileSystem, - posix.ELOOP => error.SymLinkLoop, - posix.ENAMETOOLONG => error.NameTooLong, - posix.ENOENT => error.FileNotFound, - posix.ENOMEM => error.SystemResources, - posix.ENOTDIR => error.NotDir, - else => unexpectedErrorPosix(err), - }; - } -} - -/// Read value of a symbolic link. -/// The return value is a slice of out_buffer. -pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { - const rc = posix.readlink(pathname, out_buffer, out_buffer.len); - const err = posix.getErrno(rc); - switch (err) { - 0 => return out_buffer[0..rc], - posix.EACCES => return error.AccessDenied, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EIO => return error.FileSystem, - posix.ELOOP => return error.SymLinkLoop, - posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOTDIR => return error.NotDir, - else => return unexpectedErrorPosix(err), - } -} - -/// Read value of a symbolic link. -/// The return value is a slice of out_buffer. -pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { - const file_path_c = try toPosixPath(file_path); - return readLinkC(out_buffer, &file_path_c); -} - -pub fn posix_setuid(uid: u32) !void { - const err = posix.getErrno(posix.setuid(uid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setreuid(ruid: u32, euid: u32) !void { - const err = posix.getErrno(posix.setreuid(ruid, euid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setgid(gid: u32) !void { - const err = posix.getErrno(posix.setgid(gid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub fn posix_setregid(rgid: u32, egid: u32) !void { - const err = posix.getErrno(posix.setregid(rgid, egid)); - if (err == 0) return; - return switch (err) { - posix.EAGAIN => error.ResourceLimitReached, - posix.EINVAL => error.InvalidUserId, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; -} - -pub const WindowsGetStdHandleErrs = error{ - NoStdHandles, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { - if (windows.GetStdHandle(handle_id)) |handle| { - if (handle == windows.INVALID_HANDLE_VALUE) { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - } - return handle; - } else { - return error.NoStdHandles; - } -} - -pub const ArgIteratorPosix = struct { - index: usize, - count: usize, - - pub fn init() ArgIteratorPosix { - return ArgIteratorPosix{ - .index = 0, - .count = raw.len, - }; - } - - pub fn next(self: *ArgIteratorPosix) ?[]const u8 { - if (self.index == self.count) return null; - - const s = raw[self.index]; - self.index += 1; - return cstr.toSlice(s); - } - - pub fn skip(self: *ArgIteratorPosix) bool { - if (self.index == self.count) return false; - - self.index += 1; - return true; - } - - /// This is marked as public but actually it's only meant to be used - /// internally by zig's startup code. - pub var raw: [][*]u8 = undefined; -}; - -pub const ArgIteratorWindows = struct { - index: usize, - cmd_line: [*]const u8, - in_quote: bool, - quote_count: usize, - seen_quote_count: usize, - - pub const NextError = error{OutOfMemory}; - - pub fn init() ArgIteratorWindows { - return initWithCmdLine(windows.GetCommandLineA()); - } - - pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { - return ArgIteratorWindows{ - .index = 0, - .cmd_line = cmd_line, - .in_quote = false, - .quote_count = countQuotes(cmd_line), - .seen_quote_count = 0, - }; - } - - /// You must free the returned memory when done. - pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { - // march forward over whitespace - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return null, - ' ', '\t' => continue, - else => break, - } - } - - return self.internalNext(allocator); - } - - pub fn skip(self: *ArgIteratorWindows) bool { - // march forward over whitespace - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return false, - ' ', '\t' => continue, - else => break, - } - } - - var backslash_count: usize = 0; - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return true, - '"' => { - const quote_is_real = backslash_count % 2 == 0; - if (quote_is_real) { - self.seen_quote_count += 1; - } - }, - '\\' => { - backslash_count += 1; - }, - ' ', '\t' => { - if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { - return true; - } - backslash_count = 0; - }, - else => { - backslash_count = 0; - continue; - }, - } - } - } - - fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { - var buf = try Buffer.initSize(allocator, 0); - defer buf.deinit(); - - var backslash_count: usize = 0; - while (true) : (self.index += 1) { - const byte = self.cmd_line[self.index]; - switch (byte) { - 0 => return buf.toOwnedSlice(), - '"' => { - const quote_is_real = backslash_count % 2 == 0; - try self.emitBackslashes(&buf, backslash_count / 2); - backslash_count = 0; - - if (quote_is_real) { - self.seen_quote_count += 1; - if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) { - try buf.appendByte('"'); - } - } else { - try buf.appendByte('"'); - } - }, - '\\' => { - backslash_count += 1; - }, - ' ', '\t' => { - try self.emitBackslashes(&buf, backslash_count); - backslash_count = 0; - if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { - try buf.appendByte(byte); - } else { - return buf.toOwnedSlice(); - } - }, - else => { - try self.emitBackslashes(&buf, backslash_count); - backslash_count = 0; - try buf.appendByte(byte); - }, - } - } - } - - fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { - var i: usize = 0; - while (i < emit_count) : (i += 1) { - try buf.appendByte('\\'); - } - } - - fn countQuotes(cmd_line: [*]const u8) usize { - var result: usize = 0; - var backslash_count: usize = 0; - var index: usize = 0; - while (true) : (index += 1) { - const byte = cmd_line[index]; - switch (byte) { - 0 => return result, - '\\' => backslash_count += 1, - '"' => { - result += 1 - (backslash_count % 2); - backslash_count = 0; - }, - else => { - backslash_count = 0; - }, - } - } - } -}; - -pub const ArgIterator = struct { - const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix; - - inner: InnerType, - - pub fn init() ArgIterator { - return ArgIterator{ .inner = InnerType.init() }; - } - - pub const NextError = ArgIteratorWindows.NextError; - - /// You must free the returned memory when done. - pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { - if (builtin.os == Os.windows) { - return self.inner.next(allocator); - } else { - return mem.dupe(allocator, u8, self.inner.next() orelse return null); - } - } - - /// If you only are targeting posix you can call this and not need an allocator. - pub fn nextPosix(self: *ArgIterator) ?[]const u8 { - return self.inner.next(); - } - - /// Parse past 1 argument without capturing it. - /// Returns `true` if skipped an arg, `false` if we are at the end. - pub fn skip(self: *ArgIterator) bool { - return self.inner.skip(); - } -}; - -pub fn args() ArgIterator { - return ArgIterator.init(); -} - -/// Caller must call argsFree on result. -pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { - // TODO refactor to only make 1 allocation. - var it = args(); - var contents = try Buffer.initSize(allocator, 0); - defer contents.deinit(); - - var slice_list = ArrayList(usize).init(allocator); - defer slice_list.deinit(); - - while (it.next(allocator)) |arg_or_err| { - const arg = try arg_or_err; - defer allocator.free(arg); - try contents.append(arg); - try slice_list.append(arg.len); - } - - const contents_slice = contents.toSliceConst(); - const slice_sizes = slice_list.toSliceConst(); - const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); - const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); - const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); - errdefer allocator.free(buf); - - const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); - const result_contents = buf[slice_list_bytes..]; - mem.copy(u8, result_contents, contents_slice); - - var contents_index: usize = 0; - for (slice_sizes) |len, i| { - const new_index = contents_index + len; - result_slice_list[i] = result_contents[contents_index..new_index]; - contents_index = new_index; - } - - return result_slice_list; -} - -pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { - var total_bytes: usize = 0; - for (args_alloc) |arg| { - total_bytes += @sizeOf([]u8) + arg.len; - } - const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; - const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); - return allocator.free(aligned_allocated_buf); -} - -test "windows arg parsing" { - testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); - testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); - testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); - testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); - testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); - testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); - - testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ - ".\\..\\zig-cache\\build", - "bin\\zig.exe", - ".\\..", - ".\\..\\zig-cache", - "--help", - }); -} - -fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { - var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); - for (expected_args) |expected_arg| { - const arg = it.next(debug.global_allocator).? catch unreachable; - testing.expectEqualSlices(u8, expected_arg, arg); - } - testing.expect(it.next(debug.global_allocator) == null); -} - -// TODO make this a build variable that you can set -const unexpected_error_tracing = false; -const UnexpectedError = error{ - /// The Operating System returned an undocumented error code. - Unexpected, -}; - -/// Call this when you made a syscall or something that sets errno -/// and you get an unexpected error. -pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { - if (unexpected_error_tracing) { - debug.warn("unexpected errno: {}\n", errno); - debug.dumpCurrentStackTrace(null); - } - return error.Unexpected; -} - -/// Call this when you made a windows DLL call or something that does SetLastError -/// and you get an unexpected error. -pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { - if (unexpected_error_tracing) { - debug.warn("unexpected GetLastError(): {}\n", err); - @breakpoint(); - debug.dumpCurrentStackTrace(null); - } - return error.Unexpected; -} - -pub fn openSelfExe() !os.File { - switch (builtin.os) { - Os.linux => return os.File.openReadC(c"/proc/self/exe"), - Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - var buf: [MAX_PATH_BYTES]u8 = undefined; - const self_exe_path = try selfExePath(&buf); - buf[self_exe_path.len] = 0; - return os.File.openReadC(self_exe_path.ptr); - }, - Os.windows => { - var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const wide_slice = try selfExePathW(&buf); - return os.File.openReadW(wide_slice.ptr); - }, - else => @compileError("Unsupported OS"), - } -} - -test "openSelfExe" { - switch (builtin.os) { - Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(), - else => return error.SkipZigTest, // Unsupported OS. - } -} - -pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { - const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast - const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); - assert(rc <= out_buffer.len); - if (rc == 0) { - const err = windows.GetLastError(); - switch (err) { - else => return unexpectedErrorWindows(err), - } - } - return out_buffer[0..rc]; -} - -/// Get the path to the current executable. -/// If you only need the directory, use selfExeDirPath. -/// If you only want an open file handle, use openSelfExe. -/// This function may return an error if the current executable -/// was deleted after spawning. -/// Returned value is a slice of out_buffer. -/// -/// On Linux, depends on procfs being mounted. If the currently executing binary has -/// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. -/// TODO make the return type of this a null terminated pointer -pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { - switch (builtin.os) { - Os.linux => return readLink(out_buffer, "/proc/self/exe"), - Os.freebsd => { - var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; - var out_len: usize = out_buffer.len; - const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); - - if (err == 0) return mem.toSlice(u8, out_buffer); - - return switch (err) { - posix.EFAULT => error.BadAdress, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; - }, - Os.netbsd => { - var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME }; - var out_len: usize = out_buffer.len; - const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); - - if (err == 0) return mem.toSlice(u8, out_buffer); - - return switch (err) { - posix.EFAULT => error.BadAdress, - posix.EPERM => error.PermissionDenied, - else => unexpectedErrorPosix(err), - }; - }, - Os.windows => { - var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; - const utf16le_slice = try selfExePathW(&utf16le_buf); - // Trust that Windows gives us valid UTF-16LE. - const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; - return out_buffer[0..end_index]; - }, - Os.macosx, Os.ios => { - var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast - const rc = c._NSGetExecutablePath(out_buffer, &u32_len); - if (rc != 0) return error.NameTooLong; - return mem.toSlice(u8, out_buffer); - }, - else => @compileError("Unsupported OS"), - } -} - -/// `selfExeDirPath` except allocates the result on the heap. -/// Caller owns returned memory. -pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { - var buf: [MAX_PATH_BYTES]u8 = undefined; - return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); -} - -/// Get the directory path that contains the current executable. -/// Returned value is a slice of out_buffer. -pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { - switch (builtin.os) { - Os.linux => { - // If the currently executing binary has been deleted, - // the file path looks something like `/a/b/c/exe (deleted)` - // This path cannot be opened, but it's valid for determining the directory - // the executable was in when it was run. - const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe"); - // Assume that /proc/self/exe has an absolute path, and therefore dirname - // will not return null. - return path.dirname(full_exe_path).?; - }, - Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { - const self_exe_path = try selfExePath(out_buffer); - // Assume that the OS APIs return absolute paths, and therefore dirname - // will not return null. - return path.dirname(self_exe_path).?; - }, - else => @compileError("Unsupported OS"), - } -} - -pub fn isTty(handle: FileHandle) bool { - if (is_windows) { - return windows_util.windowsIsTty(handle); - } else { - if (builtin.link_libc) { - return c.isatty(handle) != 0; - } else { - return posix.isatty(handle); - } - } -} - -pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { - if (is_windows) { - return windows_util.windowsIsCygwinPty(handle); - } else { - if (builtin.link_libc) { - return c.isatty(handle) != 0; - } else { - return posix.isatty(handle); - } - } -} - -pub const PosixSocketError = error{ - /// Permission to create a socket of the specified type and/or - /// pro‐tocol is denied. - PermissionDenied, - - /// The implementation does not support the specified address family. - AddressFamilyNotSupported, - - /// Unknown protocol, or protocol family not available. - ProtocolFamilyNotAvailable, - - /// The per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, - - /// The system-wide limit on the total number of open files has been reached. - SystemFdQuotaExceeded, - - /// Insufficient memory is available. The socket cannot be created until sufficient - /// resources are freed. - SystemResources, - - /// The protocol type or the specified protocol is not supported within this domain. - ProtocolNotSupported, -}; - -pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { - const rc = posix.socket(domain, socket_type, protocol); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EACCES => return PosixSocketError.PermissionDenied, - posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, - posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, - posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, - posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, - posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, - else => return unexpectedErrorPosix(err), - } -} - -pub const PosixBindError = error{ - /// The address is protected, and the user is not the superuser. - /// For UNIX domain sockets: Search permission is denied on a component - /// of the path prefix. - AccessDenied, - - /// The given address is already in use, or in the case of Internet domain sockets, - /// The port number was specified as zero in the socket - /// address structure, but, upon attempting to bind to an ephemeral port, it was - /// determined that all port numbers in the ephemeral port range are currently in - /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). - AddressInUse, - - /// A nonexistent interface was requested or the requested address was not local. - AddressNotAvailable, - - /// Too many symbolic links were encountered in resolving addr. - SymLinkLoop, - - /// addr is too long. - NameTooLong, - - /// A component in the directory prefix of the socket pathname does not exist. - FileNotFound, - - /// Insufficient kernel memory was available. - SystemResources, - - /// A component of the path prefix is not a directory. - NotDir, - - /// The socket inode would reside on a read-only filesystem. - ReadOnlyFileSystem, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -/// addr is `&const T` where T is one of the sockaddr -pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { - const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EACCES => return PosixBindError.AccessDenied, - posix.EADDRINUSE => return PosixBindError.AddressInUse, - posix.EBADF => unreachable, // always a race condition if this error is returned - posix.EINVAL => unreachable, - posix.ENOTSOCK => unreachable, - posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, - posix.EFAULT => unreachable, - posix.ELOOP => return PosixBindError.SymLinkLoop, - posix.ENAMETOOLONG => return PosixBindError.NameTooLong, - posix.ENOENT => return PosixBindError.FileNotFound, - posix.ENOMEM => return PosixBindError.SystemResources, - posix.ENOTDIR => return PosixBindError.NotDir, - posix.EROFS => return PosixBindError.ReadOnlyFileSystem, - else => return unexpectedErrorPosix(err), - } -} - -const PosixListenError = error{ - /// Another socket is already listening on the same port. - /// For Internet domain sockets, the socket referred to by sockfd had not previously - /// been bound to an address and, upon attempting to bind it to an ephemeral port, it - /// was determined that all port numbers in the ephemeral port range are currently in - /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). - AddressInUse, - - /// The file descriptor sockfd does not refer to a socket. - FileDescriptorNotASocket, - - /// The socket is not of a type that supports the listen() operation. - OperationNotSupported, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { - const rc = posix.listen(sockfd, backlog); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EADDRINUSE => return PosixListenError.AddressInUse, - posix.EBADF => unreachable, - posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, - else => return unexpectedErrorPosix(err), - } -} - -pub const PosixAcceptError = error{ - ConnectionAborted, - - /// The per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, - - /// The system-wide limit on the total number of open files has been reached. - SystemFdQuotaExceeded, - - /// Not enough free memory. This often means that the memory allocation is limited - /// by the socket buffer limits, not by the system memory. - SystemResources, - - /// The file descriptor sockfd does not refer to a socket. - FileDescriptorNotASocket, - - /// The referenced socket is not of type SOCK_STREAM. - OperationNotSupported, - - ProtocolFailure, - - /// Firewall rules forbid connection. - BlockedByFirewall, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { - while (true) { - var sockaddr_size = u32(@sizeOf(posix.sockaddr)); - const rc = posix.accept4(fd, addr, &sockaddr_size, flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINTR => continue, - else => return unexpectedErrorPosix(err), - - posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking - posix.EBADF => unreachable, // always a race condition - posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS => return PosixAcceptError.SystemResources, - posix.ENOMEM => return PosixAcceptError.SystemResources, - posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, - posix.EPROTO => return PosixAcceptError.ProtocolFailure, - posix.EPERM => return PosixAcceptError.BlockedByFirewall, - } - } -} - -/// Returns -1 if would block. -pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { - while (true) { - var sockaddr_size = u32(@sizeOf(posix.sockaddr)); - const rc = posix.accept4(fd, addr, &sockaddr_size, flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINTR => continue, - else => return unexpectedErrorPosix(err), - - posix.EAGAIN => return -1, - posix.EBADF => unreachable, // always a race condition - posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, - posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, - posix.ENOBUFS => return PosixAcceptError.SystemResources, - posix.ENOMEM => return PosixAcceptError.SystemResources, - posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, - posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, - posix.EPROTO => return PosixAcceptError.ProtocolFailure, - posix.EPERM => return PosixAcceptError.BlockedByFirewall, - } - } -} - -pub const LinuxEpollCreateError = error{ - /// The per-user limit on the number of epoll instances imposed by - /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further - /// details. - /// Or, The per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, - - /// The system-wide limit on the total number of open files has been reached. - SystemFdQuotaExceeded, - - /// There was insufficient memory to create the kernel object. - SystemResources, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { - const rc = posix.epoll_create1(flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - else => return unexpectedErrorPosix(err), - - posix.EINVAL => unreachable, - posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, - posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, - posix.ENOMEM => return LinuxEpollCreateError.SystemResources, - } -} - -pub const LinuxEpollCtlError = error{ - /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered - /// with this epoll instance. - FileDescriptorAlreadyPresentInSet, - - /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a - /// circular loop of epoll instances monitoring one another. - OperationCausesCircularLoop, - - /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll - /// instance. - FileDescriptorNotRegistered, - - /// There was insufficient memory to handle the requested op control operation. - SystemResources, - - /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while - /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. - /// See epoll(7) for further details. - UserResourceLimitReached, - - /// The target file fd does not support epoll. This error can occur if fd refers to, - /// for example, a regular file or a directory. - FileDescriptorIncompatibleWithEpoll, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { - const rc = posix.epoll_ctl(epfd, op, fd, event); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EBADF => unreachable, // always a race condition if this happens - posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, - posix.EINVAL => unreachable, - posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, - posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, - posix.ENOMEM => return LinuxEpollCtlError.SystemResources, - posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, - posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, - } -} - -pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { - while (true) { - const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EINTR => continue, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - else => unreachable, - } - } -} - -pub const LinuxEventFdError = error{ - InvalidFlagValue, - SystemResources, - ProcessFdQuotaExceeded, - SystemFdQuotaExceeded, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { - const rc = posix.eventfd(initval, flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - else => return unexpectedErrorPosix(err), - - posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, - posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, - posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, - posix.ENODEV => return LinuxEventFdError.SystemResources, - posix.ENOMEM => return LinuxEventFdError.SystemResources, - } -} - -pub const PosixGetSockNameError = error{ - /// Insufficient resources were available in the system to perform the operation. - SystemResources, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { - var addr: posix.sockaddr = undefined; - var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); - const rc = posix.getsockname(sockfd, &addr, &addrlen); - const err = posix.getErrno(rc); - switch (err) { - 0 => return addr, - else => return unexpectedErrorPosix(err), - - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENOTSOCK => unreachable, - posix.ENOBUFS => return PosixGetSockNameError.SystemResources, - } -} - -pub const PosixConnectError = error{ - /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket - /// file, or search permission is denied for one of the directories in the path prefix. - /// or - /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or - /// the connection request failed because of a local firewall rule. - PermissionDenied, - - /// Local address is already in use. - AddressInUse, - - /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an - /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers - /// in the ephemeral port range are currently in use. See the discussion of - /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). - AddressNotAvailable, - - /// The passed address didn't have the correct address family in its sa_family field. - AddressFamilyNotSupported, - - /// Insufficient entries in the routing cache. - SystemResources, - - /// A connect() on a stream socket found no one listening on the remote address. - ConnectionRefused, - - /// Network is unreachable. - NetworkUnreachable, - - /// Timeout while attempting connection. The server may be too busy to accept new connections. Note - /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. - ConnectionTimedOut, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { - while (true) { - const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. - posix.EINTR => continue, - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - } - } -} - -/// Same as posixConnect except it is for blocking socket file descriptors. -/// It expects to receive EINPROGRESS. -pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { - while (true) { - const rc = posix.connect(sockfd, sockaddr, len); - const err = posix.getErrno(rc); - switch (err) { - 0, posix.EINPROGRESS => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EINTR => continue, - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - } - } -} - -pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { - var err_code: i32 = undefined; - var size: u32 = @sizeOf(i32); - const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); - assert(size == 4); - const err = posix.getErrno(rc); - switch (err) { - 0 => switch (err_code) { - 0 => return, - else => return unexpectedErrorPosix(err), - - posix.EACCES => return PosixConnectError.PermissionDenied, - posix.EPERM => return PosixConnectError.PermissionDenied, - posix.EADDRINUSE => return PosixConnectError.AddressInUse, - posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, - posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, - posix.EAGAIN => return PosixConnectError.SystemResources, - posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. - posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. - posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, - posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. - posix.EISCONN => unreachable, // The socket is already connected. - posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. - posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, - }, - else => return unexpectedErrorPosix(err), - posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. - posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. - posix.EINVAL => unreachable, - posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. - posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. - } -} - -pub const Thread = struct { - data: Data, - - pub const use_pthreads = is_posix and builtin.link_libc; - - /// Represents a kernel thread handle. - /// May be an integer or a pointer depending on the platform. - /// On Linux and POSIX, this is the same as Id. - pub const Handle = if (use_pthreads) - c.pthread_t - else switch (builtin.os) { - builtin.Os.linux => i32, - builtin.Os.windows => windows.HANDLE, - else => @compileError("Unsupported OS"), - }; - - /// Represents a unique ID per thread. - /// May be an integer or pointer depending on the platform. - /// On Linux and POSIX, this is the same as Handle. - pub const Id = switch (builtin.os) { - builtin.Os.windows => windows.DWORD, - else => Handle, - }; - - pub const Data = if (use_pthreads) - struct { - handle: Thread.Handle, - mmap_addr: usize, - mmap_len: usize, - } - else switch (builtin.os) { - builtin.Os.linux => struct { - handle: Thread.Handle, - mmap_addr: usize, - mmap_len: usize, - tls_end_addr: usize, - }, - builtin.Os.windows => struct { - handle: Thread.Handle, - alloc_start: *c_void, - heap_handle: windows.HANDLE, - }, - else => @compileError("Unsupported OS"), - }; - - /// Returns the ID of the calling thread. - /// Makes a syscall every time the function is called. - /// On Linux and POSIX, this Id is the same as a Handle. - pub fn getCurrentId() Id { - if (use_pthreads) { - return c.pthread_self(); - } else - return switch (builtin.os) { - builtin.Os.linux => linux.gettid(), - builtin.Os.windows => windows.GetCurrentThreadId(), - else => @compileError("Unsupported OS"), - }; - } - - /// Returns the handle of this thread. - /// On Linux and POSIX, this is the same as Id. - pub fn handle(self: Thread) Handle { - return self.data.handle; - } - - pub fn wait(self: *const Thread) void { - if (use_pthreads) { - const err = c.pthread_join(self.data.handle, null); - switch (err) { - 0 => {}, - posix.EINVAL => unreachable, - posix.ESRCH => unreachable, - posix.EDEADLK => unreachable, - else => unreachable, - } - assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); - } else switch (builtin.os) { - builtin.Os.linux => { - while (true) { - const pid_value = @atomicLoad(i32, &self.data.handle, builtin.AtomicOrder.SeqCst); - if (pid_value == 0) break; - const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null); - switch (linux.getErrno(rc)) { - 0 => continue, - posix.EINTR => continue, - posix.EAGAIN => continue, - else => unreachable, - } - } - assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); - }, - builtin.Os.windows => { - assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); - assert(windows.CloseHandle(self.data.handle) != 0); - assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); - }, - else => @compileError("Unsupported OS"), - } - } -}; - -pub const SpawnThreadError = error{ - /// A system-imposed limit on the number of threads was encountered. - /// There are a number of limits that may trigger this error: - /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), - /// which limits the number of processes and threads for a real - /// user ID, was reached; - /// * the kernel's system-wide limit on the number of processes and - /// threads, /proc/sys/kernel/threads-max, was reached (see - /// proc(5)); - /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was - /// reached (see proc(5)); or - /// * the PID limit (pids.max) imposed by the cgroup "process num‐ - /// ber" (PIDs) controller was reached. - ThreadQuotaExceeded, - - /// The kernel cannot allocate sufficient memory to allocate a task structure - /// for the child, or to copy those parts of the caller's context that need to - /// be copied. - SystemResources, - - /// Not enough userland memory to spawn the thread. - OutOfMemory, - - /// See https://github.com/ziglang/zig/issues/1396 - 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 -/// caller must call wait on the returned thread -pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { - if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); - // TODO compile-time call graph analysis to determine stack upper bound - // https://github.com/ziglang/zig/issues/157 - const default_stack_size = 8 * 1024 * 1024; - - const Context = @typeOf(context); - comptime assert(@ArgType(@typeOf(startFn), 0) == Context); - - if (builtin.os == builtin.Os.windows) { - const WinThread = struct { - const OuterContext = struct { - thread: Thread, - inner: Context, - }; - extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { - const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; - switch (@typeId(@typeOf(startFn).ReturnType)) { - builtin.TypeId.Int => { - return startFn(arg); - }, - builtin.TypeId.Void => { - startFn(arg); - return 0; - }, - else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), - } - } - }; - - const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory; - const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); - const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; - errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); - const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; - const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; - outer_context.* = WinThread.OuterContext{ - .thread = Thread{ - .data = Thread.Data{ - .heap_handle = heap_handle, - .alloc_start = bytes_ptr, - .handle = undefined, - }, - }, - .inner = context, - }; - - const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); - outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { - const err = windows.GetLastError(); - return switch (err) { - else => os.unexpectedErrorWindows(err), - }; - }; - return &outer_context.thread; - } - - const MainFuncs = struct { - extern fn linuxThreadMain(ctx_addr: usize) u8 { - const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; - - switch (@typeId(@typeOf(startFn).ReturnType)) { - builtin.TypeId.Int => { - return startFn(arg); - }, - builtin.TypeId.Void => { - startFn(arg); - return 0; - }, - else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), - } - } - extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { - if (@sizeOf(Context) == 0) { - _ = startFn({}); - return null; - } else { - _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); - return null; - } - } - }; - - const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; - - var stack_end_offset: usize = undefined; - var thread_start_offset: usize = undefined; - var context_start_offset: usize = undefined; - var tls_start_offset: usize = undefined; - const mmap_len = blk: { - // First in memory will be the stack, which grows downwards. - var l: usize = mem.alignForward(default_stack_size, os.page_size); - stack_end_offset = l; - // Above the stack, so that it can be in the same mmap call, put the Thread object. - l = mem.alignForward(l, @alignOf(Thread)); - thread_start_offset = l; - l += @sizeOf(Thread); - // Next, the Context object. - if (@sizeOf(Context) != 0) { - l = mem.alignForward(l, @alignOf(Context)); - context_start_offset = l; - l += @sizeOf(Context); - } - // 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); - tls_start_offset = l; - l += tls_phdr.p_memsz; - } - } - break :blk l; - }; - const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); - if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; - errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); - - const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); - thread_ptr.data.mmap_addr = mmap_addr; - thread_ptr.data.mmap_len = mmap_len; - - var arg: usize = undefined; - if (@sizeOf(Context) != 0) { - arg = mmap_addr + context_start_offset; - const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); - context_ptr.* = context; - } - - if (Thread.use_pthreads) { - // use pthreads - var attr: c.pthread_attr_t = undefined; - if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; - defer assert(c.pthread_attr_destroy(&attr) == 0); - - assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); - - const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); - switch (err) { - 0 => return thread_ptr, - posix.EAGAIN => return SpawnThreadError.SystemResources, - posix.EPERM => unreachable, - posix.EINVAL => unreachable, - else => return unexpectedErrorPosix(@intCast(usize, err)), - } - } else if (builtin.os == builtin.Os.linux) { - var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | - 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); - thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; - newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); - 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); - const err = posix.getErrno(rc); - switch (err) { - 0 => return thread_ptr, - posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, - posix.EINVAL => unreachable, - posix.ENOMEM => return SpawnThreadError.SystemResources, - posix.ENOSPC => unreachable, - posix.EPERM => unreachable, - posix.EUSERS => unreachable, - else => return unexpectedErrorPosix(err), - } - } else { - @compileError("Unsupported OS"); - } -} - -pub fn posixWait(pid: i32) i32 { - var status: i32 = undefined; - while (true) { - const err = posix.getErrno(posix.waitpid(pid, &status, 0)); - switch (err) { - 0 => return status, - posix.EINTR => continue, - posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. - posix.EINVAL => unreachable, // The options argument was invalid - else => unreachable, - } - } -} - -pub fn posixFStat(fd: i32) !posix.Stat { - var stat: posix.Stat = undefined; - const err = posix.getErrno(posix.fstat(fd, &stat)); - if (err > 0) { - return switch (err) { - // We do not make this an error code because if you get EBADF it's always a bug, - // since the fd could have been reused. - posix.EBADF => unreachable, - posix.ENOMEM => error.SystemResources, - else => os.unexpectedErrorPosix(err), - }; - } - - return stat; -} - -pub const CpuCountError = error{ - OutOfMemory, - PermissionDenied, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { - switch (builtin.os) { - builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { - var count: c_int = undefined; - var count_len: usize = @sizeOf(c_int); - const rc = posix.sysctlbyname(switch (builtin.os) { - builtin.Os.macosx => c"hw.logicalcpu", - else => c"hw.ncpu", - }, @ptrCast(*c_void, &count), &count_len, null, 0); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(usize, count), - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENOMEM => return CpuCountError.OutOfMemory, - posix.ENOTDIR => unreachable, - posix.EISDIR => unreachable, - posix.ENOENT => unreachable, - posix.EPERM => unreachable, - else => return os.unexpectedErrorPosix(err), - } - }, - builtin.Os.linux => { - const usize_count = 16; - const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); - - var set = try allocator.alloc(usize, usize_count); - defer allocator.free(set); - - while (true) { - const rc = posix.sched_getaffinity(0, set); - const err = posix.getErrno(rc); - switch (err) { - 0 => { - if (rc < set.len * @sizeOf(usize)) { - const result = set[0 .. rc / @sizeOf(usize)]; - var sum: usize = 0; - for (result) |x| { - sum += @popCount(x); - } - return sum; - } else { - set = try allocator.realloc(usize, set, set.len * 2); - continue; - } - }, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.EPERM => return CpuCountError.PermissionDenied, - posix.ESRCH => unreachable, - else => return os.unexpectedErrorPosix(err), - } - } - }, - builtin.Os.windows => { - var system_info: windows.SYSTEM_INFO = undefined; - windows.GetSystemInfo(&system_info); - return @intCast(usize, system_info.dwNumberOfProcessors); - }, - else => @compileError("unsupported OS"), - } -} - -pub const BsdKQueueError = error{ - /// The per-process limit on the number of open file descriptors has been reached. - ProcessFdQuotaExceeded, - - /// The system-wide limit on the total number of open files has been reached. - SystemFdQuotaExceeded, - - /// See https://github.com/ziglang/zig/issues/1396 - Unexpected, -}; - -pub fn bsdKQueue() BsdKQueueError!i32 { - const rc = posix.kqueue(); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, - posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, - else => return unexpectedErrorPosix(err), - } -} - -pub const BsdKEventError = error{ - /// The process does not have permission to register a filter. - AccessDenied, - - /// The event could not be found to be modified or deleted. - EventNotFound, - - /// No memory was available to register the event. - SystemResources, - - /// The specified process to attach to does not exist. - ProcessNotFound, -}; - -pub fn bsdKEvent( - kq: i32, - changelist: []const posix.Kevent, - eventlist: []posix.Kevent, - timeout: ?*const posix.timespec, -) BsdKEventError!usize { - while (true) { - const rc = posix.kevent(kq, changelist, eventlist, timeout); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EACCES => return BsdKEventError.AccessDenied, - posix.EFAULT => unreachable, - posix.EBADF => unreachable, - posix.EINTR => continue, - posix.EINVAL => unreachable, - posix.ENOENT => return BsdKEventError.EventNotFound, - posix.ENOMEM => return BsdKEventError.SystemResources, - posix.ESRCH => return BsdKEventError.ProcessNotFound, - else => unreachable, - } - } -} - -pub fn linuxINotifyInit1(flags: u32) !i32 { - const rc = linux.inotify_init1(flags); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EINVAL => unreachable, - posix.EMFILE => return error.ProcessFdQuotaExceeded, - posix.ENFILE => return error.SystemFdQuotaExceeded, - posix.ENOMEM => return error.SystemResources, - else => return unexpectedErrorPosix(err), - } -} - -pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { - const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); - const err = posix.getErrno(rc); - switch (err) { - 0 => return @intCast(i32, rc), - posix.EACCES => return error.AccessDenied, - posix.EBADF => unreachable, - posix.EFAULT => unreachable, - posix.EINVAL => unreachable, - posix.ENAMETOOLONG => return error.NameTooLong, - posix.ENOENT => return error.FileNotFound, - posix.ENOMEM => return error.SystemResources, - posix.ENOSPC => return error.UserResourceLimitReached, - else => return unexpectedErrorPosix(err), - } -} - -pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { - const rc = linux.inotify_rm_watch(inotify_fd, wd); - const err = posix.getErrno(rc); - switch (err) { - 0 => return rc, - posix.EBADF => unreachable, - posix.EINVAL => unreachable, - else => unreachable, - } -} - -pub const MProtectError = error{ - AccessDenied, - OutOfMemory, - Unexpected, -}; - -/// address and length must be page-aligned -pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { - const negative_page_size = @bitCast(usize, -isize(page_size)); - const aligned_address = address & negative_page_size; - const aligned_end = (address + length + page_size - 1) & negative_page_size; - assert(address == aligned_address); - assert(length == aligned_end - aligned_address); - const rc = posix.mprotect(address, length, protection); - const err = posix.getErrno(rc); - switch (err) { - 0 => return, - posix.EINVAL => unreachable, - posix.EACCES => return error.AccessDenied, - posix.ENOMEM => return error.OutOfMemory, - else => return unexpectedErrorPosix(err), - } -} diff --git a/std/os/linux.zig b/std/os/linux.zig new file mode 100644 index 0000000000..e7f822185b --- /dev/null +++ b/std/os/linux.zig @@ -0,0 +1,1541 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const builtin = @import("builtin"); +const maxInt = std.math.maxInt; +const vdso = @import("linux/vdso.zig"); +pub use switch (builtin.arch) { + builtin.Arch.x86_64 => @import("linux/x86_64.zig"), + builtin.Arch.i386 => @import("linux/i386.zig"), + builtin.Arch.aarch64 => @import("linux/arm64.zig"), + else => @compileError("unsupported arch"), +}; +pub use @import("linux/errno.zig"); + +pub const PATH_MAX = 4096; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const FUTEX_WAIT = 0; +pub const FUTEX_WAKE = 1; +pub const FUTEX_FD = 2; +pub const FUTEX_REQUEUE = 3; +pub const FUTEX_CMP_REQUEUE = 4; +pub const FUTEX_WAKE_OP = 5; +pub const FUTEX_LOCK_PI = 6; +pub const FUTEX_UNLOCK_PI = 7; +pub const FUTEX_TRYLOCK_PI = 8; +pub const FUTEX_WAIT_BITSET = 9; + +pub const FUTEX_PRIVATE_FLAG = 128; + +pub const FUTEX_CLOCK_REALTIME = 256; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; +pub const PROT_GROWSDOWN = 0x01000000; +pub const PROT_GROWSUP = 0x02000000; + +pub const MAP_FAILED = maxInt(usize); +pub const MAP_SHARED = 0x01; +pub const MAP_PRIVATE = 0x02; +pub const MAP_TYPE = 0x0f; +pub const MAP_FIXED = 0x10; +pub const MAP_ANONYMOUS = 0x20; +pub const MAP_NORESERVE = 0x4000; +pub const MAP_GROWSDOWN = 0x0100; +pub const MAP_DENYWRITE = 0x0800; +pub const MAP_EXECUTABLE = 0x1000; +pub const MAP_LOCKED = 0x2000; +pub const MAP_POPULATE = 0x8000; +pub const MAP_NONBLOCK = 0x10000; +pub const MAP_STACK = 0x20000; +pub const MAP_HUGETLB = 0x40000; +pub const MAP_FILE = 0; + +pub const F_OK = 0; +pub const X_OK = 1; +pub const W_OK = 2; +pub const R_OK = 4; + +pub const WNOHANG = 1; +pub const WUNTRACED = 2; +pub const WSTOPPED = 2; +pub const WEXITED = 4; +pub const WCONTINUED = 8; +pub const WNOWAIT = 0x1000000; + +pub const SA_NOCLDSTOP = 1; +pub const SA_NOCLDWAIT = 2; +pub const SA_SIGINFO = 4; +pub const SA_ONSTACK = 0x08000000; +pub const SA_RESTART = 0x10000000; +pub const SA_NODEFER = 0x40000000; +pub const SA_RESETHAND = 0x80000000; +pub const SA_RESTORER = 0x04000000; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGBUS = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGUSR1 = 10; +pub const SIGSEGV = 11; +pub const SIGUSR2 = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGSTKFLT = 16; +pub const SIGCHLD = 17; +pub const SIGCONT = 18; +pub const SIGSTOP = 19; +pub const SIGTSTP = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGURG = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGIO = 29; +pub const SIGPOLL = 29; +pub const SIGPWR = 30; +pub const SIGSYS = 31; +pub const SIGUNUSED = SIGSYS; + +pub const O_RDONLY = 0o0; +pub const O_WRONLY = 0o1; +pub const O_RDWR = 0o2; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 0; +pub const SIG_UNBLOCK = 1; +pub const SIG_SETMASK = 2; + +pub const PROTO_ip = 0o000; +pub const PROTO_icmp = 0o001; +pub const PROTO_igmp = 0o002; +pub const PROTO_ggp = 0o003; +pub const PROTO_ipencap = 0o004; +pub const PROTO_st = 0o005; +pub const PROTO_tcp = 0o006; +pub const PROTO_egp = 0o010; +pub const PROTO_pup = 0o014; +pub const PROTO_udp = 0o021; +pub const PROTO_hmp = 0o024; +pub const PROTO_xns_idp = 0o026; +pub const PROTO_rdp = 0o033; +pub const PROTO_iso_tp4 = 0o035; +pub const PROTO_xtp = 0o044; +pub const PROTO_ddp = 0o045; +pub const PROTO_idpr_cmtp = 0o046; +pub const PROTO_ipv6 = 0o051; +pub const PROTO_ipv6_route = 0o053; +pub const PROTO_ipv6_frag = 0o054; +pub const PROTO_idrp = 0o055; +pub const PROTO_rsvp = 0o056; +pub const PROTO_gre = 0o057; +pub const PROTO_esp = 0o062; +pub const PROTO_ah = 0o063; +pub const PROTO_skip = 0o071; +pub const PROTO_ipv6_icmp = 0o072; +pub const PROTO_ipv6_nonxt = 0o073; +pub const PROTO_ipv6_opts = 0o074; +pub const PROTO_rspf = 0o111; +pub const PROTO_vmtp = 0o121; +pub const PROTO_ospf = 0o131; +pub const PROTO_ipip = 0o136; +pub const PROTO_encap = 0o142; +pub const PROTO_pim = 0o147; +pub const PROTO_raw = 0o377; + +pub const SHUT_RD = 0; +pub const SHUT_WR = 1; +pub const SHUT_RDWR = 2; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; +pub const SOCK_DCCP = 6; +pub const SOCK_PACKET = 10; +pub const SOCK_CLOEXEC = 0o2000000; +pub const SOCK_NONBLOCK = 0o4000; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_AX25 = 3; +pub const PF_IPX = 4; +pub const PF_APPLETALK = 5; +pub const PF_NETROM = 6; +pub const PF_BRIDGE = 7; +pub const PF_ATMPVC = 8; +pub const PF_X25 = 9; +pub const PF_INET6 = 10; +pub const PF_ROSE = 11; +pub const PF_DECnet = 12; +pub const PF_NETBEUI = 13; +pub const PF_SECURITY = 14; +pub const PF_KEY = 15; +pub const PF_NETLINK = 16; +pub const PF_ROUTE = PF_NETLINK; +pub const PF_PACKET = 17; +pub const PF_ASH = 18; +pub const PF_ECONET = 19; +pub const PF_ATMSVC = 20; +pub const PF_RDS = 21; +pub const PF_SNA = 22; +pub const PF_IRDA = 23; +pub const PF_PPPOX = 24; +pub const PF_WANPIPE = 25; +pub const PF_LLC = 26; +pub const PF_IB = 27; +pub const PF_MPLS = 28; +pub const PF_CAN = 29; +pub const PF_TIPC = 30; +pub const PF_BLUETOOTH = 31; +pub const PF_IUCV = 32; +pub const PF_RXRPC = 33; +pub const PF_ISDN = 34; +pub const PF_PHONET = 35; +pub const PF_IEEE802154 = 36; +pub const PF_CAIF = 37; +pub const PF_ALG = 38; +pub const PF_NFC = 39; +pub const PF_VSOCK = 40; +pub const PF_KCM = 41; +pub const PF_QIPCRTR = 42; +pub const PF_SMC = 43; +pub const PF_MAX = 44; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_AX25 = PF_AX25; +pub const AF_IPX = PF_IPX; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_NETROM = PF_NETROM; +pub const AF_BRIDGE = PF_BRIDGE; +pub const AF_ATMPVC = PF_ATMPVC; +pub const AF_X25 = PF_X25; +pub const AF_INET6 = PF_INET6; +pub const AF_ROSE = PF_ROSE; +pub const AF_DECnet = PF_DECnet; +pub const AF_NETBEUI = PF_NETBEUI; +pub const AF_SECURITY = PF_SECURITY; +pub const AF_KEY = PF_KEY; +pub const AF_NETLINK = PF_NETLINK; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_PACKET = PF_PACKET; +pub const AF_ASH = PF_ASH; +pub const AF_ECONET = PF_ECONET; +pub const AF_ATMSVC = PF_ATMSVC; +pub const AF_RDS = PF_RDS; +pub const AF_SNA = PF_SNA; +pub const AF_IRDA = PF_IRDA; +pub const AF_PPPOX = PF_PPPOX; +pub const AF_WANPIPE = PF_WANPIPE; +pub const AF_LLC = PF_LLC; +pub const AF_IB = PF_IB; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_TIPC = PF_TIPC; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_IUCV = PF_IUCV; +pub const AF_RXRPC = PF_RXRPC; +pub const AF_ISDN = PF_ISDN; +pub const AF_PHONET = PF_PHONET; +pub const AF_IEEE802154 = PF_IEEE802154; +pub const AF_CAIF = PF_CAIF; +pub const AF_ALG = PF_ALG; +pub const AF_NFC = PF_NFC; +pub const AF_VSOCK = PF_VSOCK; +pub const AF_KCM = PF_KCM; +pub const AF_QIPCRTR = PF_QIPCRTR; +pub const AF_SMC = PF_SMC; +pub const AF_MAX = PF_MAX; + +pub const SO_DEBUG = 1; +pub const SO_REUSEADDR = 2; +pub const SO_TYPE = 3; +pub const SO_ERROR = 4; +pub const SO_DONTROUTE = 5; +pub const SO_BROADCAST = 6; +pub const SO_SNDBUF = 7; +pub const SO_RCVBUF = 8; +pub const SO_KEEPALIVE = 9; +pub const SO_OOBINLINE = 10; +pub const SO_NO_CHECK = 11; +pub const SO_PRIORITY = 12; +pub const SO_LINGER = 13; +pub const SO_BSDCOMPAT = 14; +pub const SO_REUSEPORT = 15; +pub const SO_PASSCRED = 16; +pub const SO_PEERCRED = 17; +pub const SO_RCVLOWAT = 18; +pub const SO_SNDLOWAT = 19; +pub const SO_RCVTIMEO = 20; +pub const SO_SNDTIMEO = 21; +pub const SO_ACCEPTCONN = 30; +pub const SO_SNDBUFFORCE = 32; +pub const SO_RCVBUFFORCE = 33; +pub const SO_PROTOCOL = 38; +pub const SO_DOMAIN = 39; + +pub const SO_SECURITY_AUTHENTICATION = 22; +pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; +pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; + +pub const SO_BINDTODEVICE = 25; + +pub const SO_ATTACH_FILTER = 26; +pub const SO_DETACH_FILTER = 27; +pub const SO_GET_FILTER = SO_ATTACH_FILTER; + +pub const SO_PEERNAME = 28; +pub const SO_TIMESTAMP = 29; +pub const SCM_TIMESTAMP = SO_TIMESTAMP; + +pub const SO_PEERSEC = 31; +pub const SO_PASSSEC = 34; +pub const SO_TIMESTAMPNS = 35; +pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; +pub const SO_MARK = 36; +pub const SO_TIMESTAMPING = 37; +pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; +pub const SO_RXQ_OVFL = 40; +pub const SO_WIFI_STATUS = 41; +pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; +pub const SO_PEEK_OFF = 42; +pub const SO_NOFCS = 43; +pub const SO_LOCK_FILTER = 44; +pub const SO_SELECT_ERR_QUEUE = 45; +pub const SO_BUSY_POLL = 46; +pub const SO_MAX_PACING_RATE = 47; +pub const SO_BPF_EXTENSIONS = 48; +pub const SO_INCOMING_CPU = 49; +pub const SO_ATTACH_BPF = 50; +pub const SO_DETACH_BPF = SO_DETACH_FILTER; +pub const SO_ATTACH_REUSEPORT_CBPF = 51; +pub const SO_ATTACH_REUSEPORT_EBPF = 52; +pub const SO_CNX_ADVICE = 53; +pub const SCM_TIMESTAMPING_OPT_STATS = 54; +pub const SO_MEMINFO = 55; +pub const SO_INCOMING_NAPI_ID = 56; +pub const SO_COOKIE = 57; +pub const SCM_TIMESTAMPING_PKTINFO = 58; +pub const SO_PEERGROUPS = 59; +pub const SO_ZEROCOPY = 60; + +pub const SOL_SOCKET = 1; + +pub const SOL_IP = 0; +pub const SOL_IPV6 = 41; +pub const SOL_ICMPV6 = 58; + +pub const SOL_RAW = 255; +pub const SOL_DECNET = 261; +pub const SOL_X25 = 262; +pub const SOL_PACKET = 263; +pub const SOL_ATM = 264; +pub const SOL_AAL = 265; +pub const SOL_IRDA = 266; +pub const SOL_NETBEUI = 267; +pub const SOL_LLC = 268; +pub const SOL_DCCP = 269; +pub const SOL_NETLINK = 270; +pub const SOL_TIPC = 271; +pub const SOL_RXRPC = 272; +pub const SOL_PPPOL2TP = 273; +pub const SOL_BLUETOOTH = 274; +pub const SOL_PNPIPE = 275; +pub const SOL_RDS = 276; +pub const SOL_IUCV = 277; +pub const SOL_CAIF = 278; +pub const SOL_ALG = 279; +pub const SOL_NFC = 280; +pub const SOL_KCM = 281; +pub const SOL_TLS = 282; + +pub const SOMAXCONN = 128; + +pub const MSG_OOB = 0x0001; +pub const MSG_PEEK = 0x0002; +pub const MSG_DONTROUTE = 0x0004; +pub const MSG_CTRUNC = 0x0008; +pub const MSG_PROXY = 0x0010; +pub const MSG_TRUNC = 0x0020; +pub const MSG_DONTWAIT = 0x0040; +pub const MSG_EOR = 0x0080; +pub const MSG_WAITALL = 0x0100; +pub const MSG_FIN = 0x0200; +pub const MSG_SYN = 0x0400; +pub const MSG_CONFIRM = 0x0800; +pub const MSG_RST = 0x1000; +pub const MSG_ERRQUEUE = 0x2000; +pub const MSG_NOSIGNAL = 0x4000; +pub const MSG_MORE = 0x8000; +pub const MSG_WAITFORONE = 0x10000; +pub const MSG_BATCH = 0x40000; +pub const MSG_ZEROCOPY = 0x4000000; +pub const MSG_FASTOPEN = 0x20000000; +pub const MSG_CMSG_CLOEXEC = 0x40000000; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +pub const TCGETS = 0x5401; +pub const TCSETS = 0x5402; +pub const TCSETSW = 0x5403; +pub const TCSETSF = 0x5404; +pub const TCGETA = 0x5405; +pub const TCSETA = 0x5406; +pub const TCSETAW = 0x5407; +pub const TCSETAF = 0x5408; +pub const TCSBRK = 0x5409; +pub const TCXONC = 0x540A; +pub const TCFLSH = 0x540B; +pub const TIOCEXCL = 0x540C; +pub const TIOCNXCL = 0x540D; +pub const TIOCSCTTY = 0x540E; +pub const TIOCGPGRP = 0x540F; +pub const TIOCSPGRP = 0x5410; +pub const TIOCOUTQ = 0x5411; +pub const TIOCSTI = 0x5412; +pub const TIOCGWINSZ = 0x5413; +pub const TIOCSWINSZ = 0x5414; +pub const TIOCMGET = 0x5415; +pub const TIOCMBIS = 0x5416; +pub const TIOCMBIC = 0x5417; +pub const TIOCMSET = 0x5418; +pub const TIOCGSOFTCAR = 0x5419; +pub const TIOCSSOFTCAR = 0x541A; +pub const FIONREAD = 0x541B; +pub const TIOCINQ = FIONREAD; +pub const TIOCLINUX = 0x541C; +pub const TIOCCONS = 0x541D; +pub const TIOCGSERIAL = 0x541E; +pub const TIOCSSERIAL = 0x541F; +pub const TIOCPKT = 0x5420; +pub const FIONBIO = 0x5421; +pub const TIOCNOTTY = 0x5422; +pub const TIOCSETD = 0x5423; +pub const TIOCGETD = 0x5424; +pub const TCSBRKP = 0x5425; +pub const TIOCSBRK = 0x5427; +pub const TIOCCBRK = 0x5428; +pub const TIOCGSID = 0x5429; +pub const TIOCGRS485 = 0x542E; +pub const TIOCSRS485 = 0x542F; +pub const TIOCGPTN = 0x80045430; +pub const TIOCSPTLCK = 0x40045431; +pub const TIOCGDEV = 0x80045432; +pub const TCGETX = 0x5432; +pub const TCSETX = 0x5433; +pub const TCSETXF = 0x5434; +pub const TCSETXW = 0x5435; +pub const TIOCSIG = 0x40045436; +pub const TIOCVHANGUP = 0x5437; +pub const TIOCGPKT = 0x80045438; +pub const TIOCGPTLCK = 0x80045439; +pub const TIOCGEXCL = 0x80045440; + +pub const EPOLL_CLOEXEC = O_CLOEXEC; + +pub const EPOLL_CTL_ADD = 1; +pub const EPOLL_CTL_DEL = 2; +pub const EPOLL_CTL_MOD = 3; + +pub const EPOLLIN = 0x001; +pub const EPOLLPRI = 0x002; +pub const EPOLLOUT = 0x004; +pub const EPOLLRDNORM = 0x040; +pub const EPOLLRDBAND = 0x080; +pub const EPOLLWRNORM = 0x100; +pub const EPOLLWRBAND = 0x200; +pub const EPOLLMSG = 0x400; +pub const EPOLLERR = 0x008; +pub const EPOLLHUP = 0x010; +pub const EPOLLRDHUP = 0x2000; +pub const EPOLLEXCLUSIVE = (u32(1) << 28); +pub const EPOLLWAKEUP = (u32(1) << 29); +pub const EPOLLONESHOT = (u32(1) << 30); +pub const EPOLLET = (u32(1) << 31); + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_MONOTONIC = 1; +pub const CLOCK_PROCESS_CPUTIME_ID = 2; +pub const CLOCK_THREAD_CPUTIME_ID = 3; +pub const CLOCK_MONOTONIC_RAW = 4; +pub const CLOCK_REALTIME_COARSE = 5; +pub const CLOCK_MONOTONIC_COARSE = 6; +pub const CLOCK_BOOTTIME = 7; +pub const CLOCK_REALTIME_ALARM = 8; +pub const CLOCK_BOOTTIME_ALARM = 9; +pub const CLOCK_SGI_CYCLE = 10; +pub const CLOCK_TAI = 11; + +pub const CSIGNAL = 0x000000ff; +pub const CLONE_VM = 0x00000100; +pub const CLONE_FS = 0x00000200; +pub const CLONE_FILES = 0x00000400; +pub const CLONE_SIGHAND = 0x00000800; +pub const CLONE_PTRACE = 0x00002000; +pub const CLONE_VFORK = 0x00004000; +pub const CLONE_PARENT = 0x00008000; +pub const CLONE_THREAD = 0x00010000; +pub const CLONE_NEWNS = 0x00020000; +pub const CLONE_SYSVSEM = 0x00040000; +pub const CLONE_SETTLS = 0x00080000; +pub const CLONE_PARENT_SETTID = 0x00100000; +pub const CLONE_CHILD_CLEARTID = 0x00200000; +pub const CLONE_DETACHED = 0x00400000; +pub const CLONE_UNTRACED = 0x00800000; +pub const CLONE_CHILD_SETTID = 0x01000000; +pub const CLONE_NEWCGROUP = 0x02000000; +pub const CLONE_NEWUTS = 0x04000000; +pub const CLONE_NEWIPC = 0x08000000; +pub const CLONE_NEWUSER = 0x10000000; +pub const CLONE_NEWPID = 0x20000000; +pub const CLONE_NEWNET = 0x40000000; +pub const CLONE_IO = 0x80000000; + +pub const EFD_SEMAPHORE = 1; +pub const EFD_CLOEXEC = O_CLOEXEC; +pub const EFD_NONBLOCK = O_NONBLOCK; + +pub const MS_RDONLY = 1; +pub const MS_NOSUID = 2; +pub const MS_NODEV = 4; +pub const MS_NOEXEC = 8; +pub const MS_SYNCHRONOUS = 16; +pub const MS_REMOUNT = 32; +pub const MS_MANDLOCK = 64; +pub const MS_DIRSYNC = 128; +pub const MS_NOATIME = 1024; +pub const MS_NODIRATIME = 2048; +pub const MS_BIND = 4096; +pub const MS_MOVE = 8192; +pub const MS_REC = 16384; +pub const MS_SILENT = 32768; +pub const MS_POSIXACL = (1 << 16); +pub const MS_UNBINDABLE = (1 << 17); +pub const MS_PRIVATE = (1 << 18); +pub const MS_SLAVE = (1 << 19); +pub const MS_SHARED = (1 << 20); +pub const MS_RELATIME = (1 << 21); +pub const MS_KERNMOUNT = (1 << 22); +pub const MS_I_VERSION = (1 << 23); +pub const MS_STRICTATIME = (1 << 24); +pub const MS_LAZYTIME = (1 << 25); +pub const MS_NOREMOTELOCK = (1 << 27); +pub const MS_NOSEC = (1 << 28); +pub const MS_BORN = (1 << 29); +pub const MS_ACTIVE = (1 << 30); +pub const MS_NOUSER = (1 << 31); + +pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); + +pub const MS_MGC_VAL = 0xc0ed0000; +pub const MS_MGC_MSK = 0xffff0000; + +pub const MNT_FORCE = 1; +pub const MNT_DETACH = 2; +pub const MNT_EXPIRE = 4; +pub const UMOUNT_NOFOLLOW = 8; + +pub const IN_CLOEXEC = O_CLOEXEC; +pub const IN_NONBLOCK = O_NONBLOCK; + +pub const IN_ACCESS = 0x00000001; +pub const IN_MODIFY = 0x00000002; +pub const IN_ATTRIB = 0x00000004; +pub const IN_CLOSE_WRITE = 0x00000008; +pub const IN_CLOSE_NOWRITE = 0x00000010; +pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; +pub const IN_OPEN = 0x00000020; +pub const IN_MOVED_FROM = 0x00000040; +pub const IN_MOVED_TO = 0x00000080; +pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; +pub const IN_CREATE = 0x00000100; +pub const IN_DELETE = 0x00000200; +pub const IN_DELETE_SELF = 0x00000400; +pub const IN_MOVE_SELF = 0x00000800; +pub const IN_ALL_EVENTS = 0x00000fff; + +pub const IN_UNMOUNT = 0x00002000; +pub const IN_Q_OVERFLOW = 0x00004000; +pub const IN_IGNORED = 0x00008000; + +pub const IN_ONLYDIR = 0x01000000; +pub const IN_DONT_FOLLOW = 0x02000000; +pub const IN_EXCL_UNLINK = 0x04000000; +pub const IN_MASK_ADD = 0x20000000; + +pub const IN_ISDIR = 0x40000000; +pub const IN_ONESHOT = 0x80000000; + +pub const S_IFMT = 0o170000; + +pub const S_IFDIR = 0o040000; +pub const S_IFCHR = 0o020000; +pub const S_IFBLK = 0o060000; +pub const S_IFREG = 0o100000; +pub const S_IFIFO = 0o010000; +pub const S_IFLNK = 0o120000; +pub const S_IFSOCK = 0o140000; + +pub const S_ISUID = 0o4000; +pub const S_ISGID = 0o2000; +pub const S_ISVTX = 0o1000; +pub const S_IRUSR = 0o400; +pub const S_IWUSR = 0o200; +pub const S_IXUSR = 0o100; +pub const S_IRWXU = 0o700; +pub const S_IRGRP = 0o040; +pub const S_IWGRP = 0o020; +pub const S_IXGRP = 0o010; +pub const S_IRWXG = 0o070; +pub const S_IROTH = 0o004; +pub const S_IWOTH = 0o002; +pub const S_IXOTH = 0o001; +pub const S_IRWXO = 0o007; + +pub fn S_ISREG(m: u32) bool { + return m & S_IFMT == S_IFREG; +} + +pub fn S_ISDIR(m: u32) bool { + return m & S_IFMT == S_IFDIR; +} + +pub fn S_ISCHR(m: u32) bool { + return m & S_IFMT == S_IFCHR; +} + +pub fn S_ISBLK(m: u32) bool { + return m & S_IFMT == S_IFBLK; +} + +pub fn S_ISFIFO(m: u32) bool { + return m & S_IFMT == S_IFIFO; +} + +pub fn S_ISLNK(m: u32) bool { + return m & S_IFMT == S_IFLNK; +} + +pub fn S_ISSOCK(m: u32) bool { + return m & S_IFMT == S_IFSOCK; +} + +pub const TFD_NONBLOCK = O_NONBLOCK; +pub const TFD_CLOEXEC = O_CLOEXEC; + +pub const TFD_TIMER_ABSTIME = 1; +pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); + +fn unsigned(s: i32) u32 { + return @bitCast(u32, s); +} +fn signed(s: u32) i32 { + return @bitCast(i32, s); +} +pub fn WEXITSTATUS(s: i32) i32 { + return signed((unsigned(s) & 0xff00) >> 8); +} +pub fn WTERMSIG(s: i32) i32 { + return signed(unsigned(s) & 0x7f); +} +pub fn WSTOPSIG(s: i32) i32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: i32) bool { + return WTERMSIG(s) == 0; +} +pub fn WIFSTOPPED(s: i32) bool { + return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; +} +pub fn WIFSIGNALED(s: i32) bool { + return (unsigned(s) & 0xffff) -% 1 < 0xff; +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +/// Get the errno from a syscall return value, or 0 for no error. +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 dup2(old: i32, new: i32) usize { + return dup3(old, new, 0); +} + +pub fn dup3(old: i32, new: i32, flags: u32) usize { + return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chdir(path: [*]const u8) usize { + return syscall1(SYS_chdir, @ptrToInt(path)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn chroot(path: [*]const u8) usize { + return syscall1(SYS_chroot, @ptrToInt(path)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { + return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); +} + +pub fn fork() usize { + return clone2(SIGCHLD, 0); +} + +/// This must be inline, and inline call the syscall function, because if the +/// child does a return it will clobber the parent's stack. +/// It is advised to avoid this function and use clone instead, because +/// the compiler is not aware of how vfork affects control flow and you may +/// see different results in optimized builds. +pub inline fn vfork() usize { + return @inlineCall(syscall0, SYS_vfork); +} + +pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { + return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout)); +} + +pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize { + return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val)); +} + +pub fn getcwd(buf: [*]u8, size: usize) usize { + return syscall2(SYS_getcwd, @ptrToInt(buf), size); +} + +pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { + return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); +} + +pub fn inotify_init1(flags: u32) usize { + return syscall1(SYS_inotify_init1, flags); +} + +pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { + return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); +} + +pub fn inotify_rm_watch(fd: i32, wd: i32) usize { + return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); +} + +pub fn isatty(fd: i32) bool { + var wsz: winsize = undefined; + return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mkdir(path: [*]const u8, mode: u32) usize { + return mkdirat(AT_FDCWD, path, mode); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { + return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { + return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount(special: [*]const u8) usize { + return syscall2(SYS_umount2, @ptrToInt(special), 0); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn umount2(special: [*]const u8, flags: u32) usize { + return syscall2(SYS_umount2, @ptrToInt(special), flags); +} + +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); +} + +pub fn mprotect(address: usize, length: usize, protection: usize) usize { + return syscall3(SYS_mprotect, address, length, protection); +} + +pub fn munmap(address: usize, length: usize) usize { + return syscall2(SYS_munmap, address, length); +} + +pub fn read(fd: i32, buf: [*]u8, count: usize) usize { + return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); +} + +pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { + return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); +} + +pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { + return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); +} + +pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { + return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); +} + +pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { + return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rmdir(path: [*]const u8) usize { + return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { + return symlinkat(existing, AT_FDCWD, new); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { + return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { + return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn access(path: [*]const u8, mode: u32) usize { + return faccessat(AT_FDCWD, path, mode); +} + +pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { + return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); +} + +pub fn pipe(fd: *[2]i32) usize { + return pipe2(fd, 0); +} + +pub fn pipe2(fd: *[2]i32, flags: u32) usize { + return syscall2(SYS_pipe2, @ptrToInt(fd), flags); +} + +pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { + return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); +} + +pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { + return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn rename(old: [*]const u8, new: [*]const u8) usize { + return renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { + return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { + return openat(AT_FDCWD, path, flags, perm); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn create(path: [*]const u8, perm: usize) usize { + return syscall2(SYS_creat, @ptrToInt(path), perm); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { + // dirfd could be negative, for example AT_FDCWD is -100 + return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); +} + +/// See also `clone` (from the arch-specific include) +pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { + return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); +} + +/// See also `clone` (from the arch-specific include) +pub fn clone2(flags: u32, child_stack_ptr: usize) usize { + return syscall2(SYS_clone, flags, child_stack_ptr); +} + +pub fn close(fd: i32) usize { + return syscall1(SYS_close, @bitCast(usize, isize(fd))); +} + +pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { + return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); +} + +pub fn exit(status: i32) noreturn { + _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); + unreachable; +} + +pub fn exit_group(status: i32) noreturn { + _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); + unreachable; +} + +pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { + return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); +} + +pub fn kill(pid: i32, sig: i32) usize { + return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn unlink(path: [*]const u8) usize { + return unlinkat(AT_FDCWD, path, 0); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { + return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); +} + +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); +} + +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 rc = f(clk_id, tp); + switch (rc) { + 0, @bitCast(usize, isize(-EINVAL)) => return rc, + else => {}, + } + } + } + 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); +} + +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { + return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { + return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); +} + +pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { + return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + +pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { + return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); +} + +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { + return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); +} + +pub fn setuid(uid: u32) usize { + return syscall1(SYS_setuid, uid); +} + +pub fn setgid(gid: u32) usize { + return syscall1(SYS_setgid, gid); +} + +pub fn setreuid(ruid: u32, euid: u32) usize { + return syscall2(SYS_setreuid, ruid, euid); +} + +pub fn setregid(rgid: u32, egid: u32) usize { + return syscall2(SYS_setregid, rgid, egid); +} + +pub fn getuid() u32 { + return u32(syscall0(SYS_getuid)); +} + +pub fn getgid() u32 { + return u32(syscall0(SYS_getgid)); +} + +pub fn geteuid() u32 { + return u32(syscall0(SYS_geteuid)); +} + +pub fn getegid() u32 { + return u32(syscall0(SYS_getegid)); +} + +pub fn seteuid(euid: u32) usize { + return syscall1(SYS_seteuid, euid); +} + +pub fn setegid(egid: u32) usize { + return syscall1(SYS_setegid, egid); +} + +pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { + return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); +} + +pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { + return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); +} + +pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { + return syscall3(SYS_setresuid, ruid, euid, suid); +} + +pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { + return syscall3(SYS_setresgid, rgid, egid, sgid); +} + +pub fn getgroups(size: usize, list: *u32) usize { + return syscall2(SYS_getgroups, size, @ptrToInt(list)); +} + +pub fn setgroups(size: usize, list: *const u32) usize { + return syscall2(SYS_setgroups, size, @ptrToInt(list)); +} + +pub fn getpid() i32 { + return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); +} + +pub fn gettid() i32 { + return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); +} + +pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { + return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); +} + +pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { + assert(sig >= 1); + assert(sig != SIGKILL); + assert(sig != SIGSTOP); + var ksa = k_sigaction{ + .handler = act.handler, + .flags = act.flags | SA_RESTORER, + .mask = undefined, + .restorer = @ptrCast(extern fn () void, restore_rt), + }; + var ksa_old: k_sigaction = undefined; + @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); + const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); + const err = getErrno(result); + if (err != 0) { + return result; + } + if (oact) |old| { + old.handler = ksa_old.handler; + old.flags = @truncate(u32, ksa_old.flags); + @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); + } + return 0; +} + +const NSIG = 65; +const sigset_t = [128 / @sizeOf(usize)]usize; +const all_mask = []usize{maxInt(usize)}; +const app_mask = []usize{0xfffffffc7fffffff}; + +const k_sigaction = extern struct { + handler: extern fn (i32) void, + flags: usize, + restorer: extern fn () void, + mask: [2]u32, +}; + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = struct { + handler: extern fn (i32) void, + mask: sigset_t, + flags: u32, +}; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); +pub const empty_sigset = []usize{0} ** sigset_t.len; + +pub fn raise(sig: i32) usize { + var set: sigset_t = undefined; + blockAppSignals(&set); + const tid = syscall0(SYS_gettid); + const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); + restoreSignals(&set); + return ret; +} + +fn blockAllSignals(set: *sigset_t) void { + _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); +} + +fn blockAppSignals(set: *sigset_t) void { + _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); +} + +fn restoreSignals(set: *sigset_t) void { + _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); +} + +pub fn sigaddset(set: *sigset_t, sig: u6) void { + const s = sig - 1; + (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); +} + +pub fn sigismember(set: *const sigset_t, sig: u6) bool { + const s = sig - 1; + return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; +} + +pub const in_port_t = u16; +pub const sa_family_t = u16; +pub const socklen_t = u32; + +/// This intentionally only has ip4 and ip6 +pub const sockaddr = extern union { + in: sockaddr_in, + in6: sockaddr_in6, +}; + +pub const sockaddr_in = extern struct { + family: sa_family_t, + port: in_port_t, + addr: u32, + zero: [8]u8, +}; + +pub const sockaddr_in6 = extern struct { + family: sa_family_t, + port: in_port_t, + flowinfo: u32, + addr: [16]u8, + scope_id: u32, +}; + +pub const sockaddr_un = extern struct { + family: sa_family_t, + path: [108]u8, +}; + +pub const iovec = extern struct { + iov_base: [*]u8, + iov_len: usize, +}; + +pub const iovec_const = extern struct { + iov_base: [*]const u8, + iov_len: usize, +}; + +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)); +} + +pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); +} + +pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { + return syscall3(SYS_socket, domain, socket_type, protocol); +} + +pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { + return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); +} + +pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { + 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 { + return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), 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); +} + +pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { + return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); +} + +pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { + return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); +} + +pub fn shutdown(fd: i32, how: i32) usize { + return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); +} + +pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { + return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); +} + +pub fn listen(fd: i32, backlog: u32) usize { + return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); +} + +pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { + return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); +} + +pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { + return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); +} + +pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { + return accept4(fd, addr, len, 0); +} + +pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { + return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); +} + +pub fn fstat(fd: i32, stat_buf: *Stat) usize { + return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { + return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { + return fstatat(AF_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { + return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { + return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { + return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); +} + +pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { + return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { + return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { + return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { + return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { + return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { + return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { + return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { + return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { + return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); +} + +// TODO https://github.com/ziglang/zig/issues/265 +pub fn fremovexattr(fd: usize, name: [*]const u8) usize { + return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); +} + +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 { + ptr: usize, + fd: i32, + @"u32": u32, + @"u64": u64, +}; + +pub const epoll_event = packed struct { + events: u32, + data: epoll_data, +}; + +pub fn epoll_create() usize { + return epoll_create1(0); +} + +pub fn epoll_create1(flags: usize) usize { + return syscall1(SYS_epoll_create1, flags); +} + +pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { + return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); +} + +pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { + return epoll_pwait(epoll_fd, events, maxevents, timeout, null); +} + +pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { + return syscall6( + SYS_epoll_pwait, + @bitCast(usize, isize(epoll_fd)), + @ptrToInt(events), + @intCast(usize, maxevents), + @bitCast(usize, isize(timeout)), + @ptrToInt(sigmask), + @sizeOf(sigset_t), + ); +} + +pub fn eventfd(count: u32, flags: u32) usize { + return syscall2(SYS_eventfd2, count, flags); +} + +pub fn timerfd_create(clockid: i32, flags: u32) usize { + return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); +} + +pub const itimerspec = extern struct { + it_interval: timespec, + it_value: timespec, +}; + +pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { + return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); +} + +pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { + return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); +} + +pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; +pub const _LINUX_CAPABILITY_U32S_1 = 1; + +pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; +pub const _LINUX_CAPABILITY_U32S_2 = 2; + +pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; +pub const _LINUX_CAPABILITY_U32S_3 = 2; + +pub const VFS_CAP_REVISION_MASK = 0xFF000000; +pub const VFS_CAP_REVISION_SHIFT = 24; +pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; +pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; + +pub const VFS_CAP_REVISION_1 = 0x01000000; +pub const VFS_CAP_U32_1 = 1; +pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); + +pub const VFS_CAP_REVISION_2 = 0x02000000; +pub const VFS_CAP_U32_2 = 2; +pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); + +pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; +pub const VFS_CAP_U32 = VFS_CAP_U32_2; +pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; + +pub const vfs_cap_data = extern struct { + //all of these are mandated as little endian + //when on disk. + const Data = struct { + permitted: u32, + inheritable: u32, + }; + + magic_etc: u32, + data: [VFS_CAP_U32]Data, +}; + +pub const CAP_CHOWN = 0; +pub const CAP_DAC_OVERRIDE = 1; +pub const CAP_DAC_READ_SEARCH = 2; +pub const CAP_FOWNER = 3; +pub const CAP_FSETID = 4; +pub const CAP_KILL = 5; +pub const CAP_SETGID = 6; +pub const CAP_SETUID = 7; +pub const CAP_SETPCAP = 8; +pub const CAP_LINUX_IMMUTABLE = 9; +pub const CAP_NET_BIND_SERVICE = 10; +pub const CAP_NET_BROADCAST = 11; +pub const CAP_NET_ADMIN = 12; +pub const CAP_NET_RAW = 13; +pub const CAP_IPC_LOCK = 14; +pub const CAP_IPC_OWNER = 15; +pub const CAP_SYS_MODULE = 16; +pub const CAP_SYS_RAWIO = 17; +pub const CAP_SYS_CHROOT = 18; +pub const CAP_SYS_PTRACE = 19; +pub const CAP_SYS_PACCT = 20; +pub const CAP_SYS_ADMIN = 21; +pub const CAP_SYS_BOOT = 22; +pub const CAP_SYS_NICE = 23; +pub const CAP_SYS_RESOURCE = 24; +pub const CAP_SYS_TIME = 25; +pub const CAP_SYS_TTY_CONFIG = 26; +pub const CAP_MKNOD = 27; +pub const CAP_LEASE = 28; +pub const CAP_AUDIT_WRITE = 29; +pub const CAP_AUDIT_CONTROL = 30; +pub const CAP_SETFCAP = 31; +pub const CAP_MAC_OVERRIDE = 32; +pub const CAP_MAC_ADMIN = 33; +pub const CAP_SYSLOG = 34; +pub const CAP_WAKE_ALARM = 35; +pub const CAP_BLOCK_SUSPEND = 36; +pub const CAP_AUDIT_READ = 37; +pub const CAP_LAST_CAP = CAP_AUDIT_READ; + +pub fn cap_valid(u8: x) bool { + return x >= 0 and x <= CAP_LAST_CAP; +} + +pub fn CAP_TO_MASK(cap: u8) u32 { + return u32(1) << u5(cap & 31); +} + +pub fn CAP_TO_INDEX(cap: u8) u8 { + return cap >> 5; +} + +pub const cap_t = extern struct { + hdrp: *cap_user_header_t, + datap: *cap_user_data_t, +}; + +pub const cap_user_header_t = extern struct { + version: u32, + pid: usize, +}; + +pub const cap_user_data_t = extern struct { + effective: u32, + permitted: u32, + inheritable: u32, +}; + +pub fn unshare(flags: usize) usize { + return syscall1(SYS_unshare, flags); +} + +pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { + return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); +} + +pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { + return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); +} + +pub const inotify_event = extern struct { + wd: i32, + mask: u32, + cookie: u32, + len: u32, + //name: [?]u8, +}; + +pub const dirent64 = extern struct { + d_ino: u64, + d_off: u64, + d_reclen: u16, + d_type: u8, + d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 +}; + +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 91e4a49679..ac2c18ee5f 100644 --- a/std/os/linux/arm64.zig +++ b/std/os/linux/arm64.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const linux = std.os.linux; const socklen_t = linux.socklen_t; const iovec = linux.iovec; diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig deleted file mode 100644 index 3a56461a40..0000000000 --- a/std/os/linux/index.zig +++ /dev/null @@ -1,1541 +0,0 @@ -const std = @import("../../index.zig"); -const assert = std.debug.assert; -const builtin = @import("builtin"); -const maxInt = std.math.maxInt; -const vdso = @import("vdso.zig"); -pub use switch (builtin.arch) { - builtin.Arch.x86_64 => @import("x86_64.zig"), - builtin.Arch.i386 => @import("i386.zig"), - builtin.Arch.aarch64 => @import("arm64.zig"), - else => @compileError("unsupported arch"), -}; -pub use @import("errno.zig"); - -pub const PATH_MAX = 4096; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const FUTEX_WAIT = 0; -pub const FUTEX_WAKE = 1; -pub const FUTEX_FD = 2; -pub const FUTEX_REQUEUE = 3; -pub const FUTEX_CMP_REQUEUE = 4; -pub const FUTEX_WAKE_OP = 5; -pub const FUTEX_LOCK_PI = 6; -pub const FUTEX_UNLOCK_PI = 7; -pub const FUTEX_TRYLOCK_PI = 8; -pub const FUTEX_WAIT_BITSET = 9; - -pub const FUTEX_PRIVATE_FLAG = 128; - -pub const FUTEX_CLOCK_REALTIME = 256; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; -pub const PROT_GROWSDOWN = 0x01000000; -pub const PROT_GROWSUP = 0x02000000; - -pub const MAP_FAILED = maxInt(usize); -pub const MAP_SHARED = 0x01; -pub const MAP_PRIVATE = 0x02; -pub const MAP_TYPE = 0x0f; -pub const MAP_FIXED = 0x10; -pub const MAP_ANONYMOUS = 0x20; -pub const MAP_NORESERVE = 0x4000; -pub const MAP_GROWSDOWN = 0x0100; -pub const MAP_DENYWRITE = 0x0800; -pub const MAP_EXECUTABLE = 0x1000; -pub const MAP_LOCKED = 0x2000; -pub const MAP_POPULATE = 0x8000; -pub const MAP_NONBLOCK = 0x10000; -pub const MAP_STACK = 0x20000; -pub const MAP_HUGETLB = 0x40000; -pub const MAP_FILE = 0; - -pub const F_OK = 0; -pub const X_OK = 1; -pub const W_OK = 2; -pub const R_OK = 4; - -pub const WNOHANG = 1; -pub const WUNTRACED = 2; -pub const WSTOPPED = 2; -pub const WEXITED = 4; -pub const WCONTINUED = 8; -pub const WNOWAIT = 0x1000000; - -pub const SA_NOCLDSTOP = 1; -pub const SA_NOCLDWAIT = 2; -pub const SA_SIGINFO = 4; -pub const SA_ONSTACK = 0x08000000; -pub const SA_RESTART = 0x10000000; -pub const SA_NODEFER = 0x40000000; -pub const SA_RESETHAND = 0x80000000; -pub const SA_RESTORER = 0x04000000; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGBUS = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGUSR1 = 10; -pub const SIGSEGV = 11; -pub const SIGUSR2 = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGSTKFLT = 16; -pub const SIGCHLD = 17; -pub const SIGCONT = 18; -pub const SIGSTOP = 19; -pub const SIGTSTP = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGURG = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGIO = 29; -pub const SIGPOLL = 29; -pub const SIGPWR = 30; -pub const SIGSYS = 31; -pub const SIGUNUSED = SIGSYS; - -pub const O_RDONLY = 0o0; -pub const O_WRONLY = 0o1; -pub const O_RDWR = 0o2; - -pub const SEEK_SET = 0; -pub const SEEK_CUR = 1; -pub const SEEK_END = 2; - -pub const SIG_BLOCK = 0; -pub const SIG_UNBLOCK = 1; -pub const SIG_SETMASK = 2; - -pub const PROTO_ip = 0o000; -pub const PROTO_icmp = 0o001; -pub const PROTO_igmp = 0o002; -pub const PROTO_ggp = 0o003; -pub const PROTO_ipencap = 0o004; -pub const PROTO_st = 0o005; -pub const PROTO_tcp = 0o006; -pub const PROTO_egp = 0o010; -pub const PROTO_pup = 0o014; -pub const PROTO_udp = 0o021; -pub const PROTO_hmp = 0o024; -pub const PROTO_xns_idp = 0o026; -pub const PROTO_rdp = 0o033; -pub const PROTO_iso_tp4 = 0o035; -pub const PROTO_xtp = 0o044; -pub const PROTO_ddp = 0o045; -pub const PROTO_idpr_cmtp = 0o046; -pub const PROTO_ipv6 = 0o051; -pub const PROTO_ipv6_route = 0o053; -pub const PROTO_ipv6_frag = 0o054; -pub const PROTO_idrp = 0o055; -pub const PROTO_rsvp = 0o056; -pub const PROTO_gre = 0o057; -pub const PROTO_esp = 0o062; -pub const PROTO_ah = 0o063; -pub const PROTO_skip = 0o071; -pub const PROTO_ipv6_icmp = 0o072; -pub const PROTO_ipv6_nonxt = 0o073; -pub const PROTO_ipv6_opts = 0o074; -pub const PROTO_rspf = 0o111; -pub const PROTO_vmtp = 0o121; -pub const PROTO_ospf = 0o131; -pub const PROTO_ipip = 0o136; -pub const PROTO_encap = 0o142; -pub const PROTO_pim = 0o147; -pub const PROTO_raw = 0o377; - -pub const SHUT_RD = 0; -pub const SHUT_WR = 1; -pub const SHUT_RDWR = 2; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; -pub const SOCK_DCCP = 6; -pub const SOCK_PACKET = 10; -pub const SOCK_CLOEXEC = 0o2000000; -pub const SOCK_NONBLOCK = 0o4000; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; -pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_AX25 = 3; -pub const PF_IPX = 4; -pub const PF_APPLETALK = 5; -pub const PF_NETROM = 6; -pub const PF_BRIDGE = 7; -pub const PF_ATMPVC = 8; -pub const PF_X25 = 9; -pub const PF_INET6 = 10; -pub const PF_ROSE = 11; -pub const PF_DECnet = 12; -pub const PF_NETBEUI = 13; -pub const PF_SECURITY = 14; -pub const PF_KEY = 15; -pub const PF_NETLINK = 16; -pub const PF_ROUTE = PF_NETLINK; -pub const PF_PACKET = 17; -pub const PF_ASH = 18; -pub const PF_ECONET = 19; -pub const PF_ATMSVC = 20; -pub const PF_RDS = 21; -pub const PF_SNA = 22; -pub const PF_IRDA = 23; -pub const PF_PPPOX = 24; -pub const PF_WANPIPE = 25; -pub const PF_LLC = 26; -pub const PF_IB = 27; -pub const PF_MPLS = 28; -pub const PF_CAN = 29; -pub const PF_TIPC = 30; -pub const PF_BLUETOOTH = 31; -pub const PF_IUCV = 32; -pub const PF_RXRPC = 33; -pub const PF_ISDN = 34; -pub const PF_PHONET = 35; -pub const PF_IEEE802154 = 36; -pub const PF_CAIF = 37; -pub const PF_ALG = 38; -pub const PF_NFC = 39; -pub const PF_VSOCK = 40; -pub const PF_KCM = 41; -pub const PF_QIPCRTR = 42; -pub const PF_SMC = 43; -pub const PF_MAX = 44; - -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; -pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_AX25 = PF_AX25; -pub const AF_IPX = PF_IPX; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_NETROM = PF_NETROM; -pub const AF_BRIDGE = PF_BRIDGE; -pub const AF_ATMPVC = PF_ATMPVC; -pub const AF_X25 = PF_X25; -pub const AF_INET6 = PF_INET6; -pub const AF_ROSE = PF_ROSE; -pub const AF_DECnet = PF_DECnet; -pub const AF_NETBEUI = PF_NETBEUI; -pub const AF_SECURITY = PF_SECURITY; -pub const AF_KEY = PF_KEY; -pub const AF_NETLINK = PF_NETLINK; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_PACKET = PF_PACKET; -pub const AF_ASH = PF_ASH; -pub const AF_ECONET = PF_ECONET; -pub const AF_ATMSVC = PF_ATMSVC; -pub const AF_RDS = PF_RDS; -pub const AF_SNA = PF_SNA; -pub const AF_IRDA = PF_IRDA; -pub const AF_PPPOX = PF_PPPOX; -pub const AF_WANPIPE = PF_WANPIPE; -pub const AF_LLC = PF_LLC; -pub const AF_IB = PF_IB; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_TIPC = PF_TIPC; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_IUCV = PF_IUCV; -pub const AF_RXRPC = PF_RXRPC; -pub const AF_ISDN = PF_ISDN; -pub const AF_PHONET = PF_PHONET; -pub const AF_IEEE802154 = PF_IEEE802154; -pub const AF_CAIF = PF_CAIF; -pub const AF_ALG = PF_ALG; -pub const AF_NFC = PF_NFC; -pub const AF_VSOCK = PF_VSOCK; -pub const AF_KCM = PF_KCM; -pub const AF_QIPCRTR = PF_QIPCRTR; -pub const AF_SMC = PF_SMC; -pub const AF_MAX = PF_MAX; - -pub const SO_DEBUG = 1; -pub const SO_REUSEADDR = 2; -pub const SO_TYPE = 3; -pub const SO_ERROR = 4; -pub const SO_DONTROUTE = 5; -pub const SO_BROADCAST = 6; -pub const SO_SNDBUF = 7; -pub const SO_RCVBUF = 8; -pub const SO_KEEPALIVE = 9; -pub const SO_OOBINLINE = 10; -pub const SO_NO_CHECK = 11; -pub const SO_PRIORITY = 12; -pub const SO_LINGER = 13; -pub const SO_BSDCOMPAT = 14; -pub const SO_REUSEPORT = 15; -pub const SO_PASSCRED = 16; -pub const SO_PEERCRED = 17; -pub const SO_RCVLOWAT = 18; -pub const SO_SNDLOWAT = 19; -pub const SO_RCVTIMEO = 20; -pub const SO_SNDTIMEO = 21; -pub const SO_ACCEPTCONN = 30; -pub const SO_SNDBUFFORCE = 32; -pub const SO_RCVBUFFORCE = 33; -pub const SO_PROTOCOL = 38; -pub const SO_DOMAIN = 39; - -pub const SO_SECURITY_AUTHENTICATION = 22; -pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; -pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; - -pub const SO_BINDTODEVICE = 25; - -pub const SO_ATTACH_FILTER = 26; -pub const SO_DETACH_FILTER = 27; -pub const SO_GET_FILTER = SO_ATTACH_FILTER; - -pub const SO_PEERNAME = 28; -pub const SO_TIMESTAMP = 29; -pub const SCM_TIMESTAMP = SO_TIMESTAMP; - -pub const SO_PEERSEC = 31; -pub const SO_PASSSEC = 34; -pub const SO_TIMESTAMPNS = 35; -pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; -pub const SO_MARK = 36; -pub const SO_TIMESTAMPING = 37; -pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; -pub const SO_RXQ_OVFL = 40; -pub const SO_WIFI_STATUS = 41; -pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; -pub const SO_PEEK_OFF = 42; -pub const SO_NOFCS = 43; -pub const SO_LOCK_FILTER = 44; -pub const SO_SELECT_ERR_QUEUE = 45; -pub const SO_BUSY_POLL = 46; -pub const SO_MAX_PACING_RATE = 47; -pub const SO_BPF_EXTENSIONS = 48; -pub const SO_INCOMING_CPU = 49; -pub const SO_ATTACH_BPF = 50; -pub const SO_DETACH_BPF = SO_DETACH_FILTER; -pub const SO_ATTACH_REUSEPORT_CBPF = 51; -pub const SO_ATTACH_REUSEPORT_EBPF = 52; -pub const SO_CNX_ADVICE = 53; -pub const SCM_TIMESTAMPING_OPT_STATS = 54; -pub const SO_MEMINFO = 55; -pub const SO_INCOMING_NAPI_ID = 56; -pub const SO_COOKIE = 57; -pub const SCM_TIMESTAMPING_PKTINFO = 58; -pub const SO_PEERGROUPS = 59; -pub const SO_ZEROCOPY = 60; - -pub const SOL_SOCKET = 1; - -pub const SOL_IP = 0; -pub const SOL_IPV6 = 41; -pub const SOL_ICMPV6 = 58; - -pub const SOL_RAW = 255; -pub const SOL_DECNET = 261; -pub const SOL_X25 = 262; -pub const SOL_PACKET = 263; -pub const SOL_ATM = 264; -pub const SOL_AAL = 265; -pub const SOL_IRDA = 266; -pub const SOL_NETBEUI = 267; -pub const SOL_LLC = 268; -pub const SOL_DCCP = 269; -pub const SOL_NETLINK = 270; -pub const SOL_TIPC = 271; -pub const SOL_RXRPC = 272; -pub const SOL_PPPOL2TP = 273; -pub const SOL_BLUETOOTH = 274; -pub const SOL_PNPIPE = 275; -pub const SOL_RDS = 276; -pub const SOL_IUCV = 277; -pub const SOL_CAIF = 278; -pub const SOL_ALG = 279; -pub const SOL_NFC = 280; -pub const SOL_KCM = 281; -pub const SOL_TLS = 282; - -pub const SOMAXCONN = 128; - -pub const MSG_OOB = 0x0001; -pub const MSG_PEEK = 0x0002; -pub const MSG_DONTROUTE = 0x0004; -pub const MSG_CTRUNC = 0x0008; -pub const MSG_PROXY = 0x0010; -pub const MSG_TRUNC = 0x0020; -pub const MSG_DONTWAIT = 0x0040; -pub const MSG_EOR = 0x0080; -pub const MSG_WAITALL = 0x0100; -pub const MSG_FIN = 0x0200; -pub const MSG_SYN = 0x0400; -pub const MSG_CONFIRM = 0x0800; -pub const MSG_RST = 0x1000; -pub const MSG_ERRQUEUE = 0x2000; -pub const MSG_NOSIGNAL = 0x4000; -pub const MSG_MORE = 0x8000; -pub const MSG_WAITFORONE = 0x10000; -pub const MSG_BATCH = 0x40000; -pub const MSG_ZEROCOPY = 0x4000000; -pub const MSG_FASTOPEN = 0x20000000; -pub const MSG_CMSG_CLOEXEC = 0x40000000; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -pub const TCGETS = 0x5401; -pub const TCSETS = 0x5402; -pub const TCSETSW = 0x5403; -pub const TCSETSF = 0x5404; -pub const TCGETA = 0x5405; -pub const TCSETA = 0x5406; -pub const TCSETAW = 0x5407; -pub const TCSETAF = 0x5408; -pub const TCSBRK = 0x5409; -pub const TCXONC = 0x540A; -pub const TCFLSH = 0x540B; -pub const TIOCEXCL = 0x540C; -pub const TIOCNXCL = 0x540D; -pub const TIOCSCTTY = 0x540E; -pub const TIOCGPGRP = 0x540F; -pub const TIOCSPGRP = 0x5410; -pub const TIOCOUTQ = 0x5411; -pub const TIOCSTI = 0x5412; -pub const TIOCGWINSZ = 0x5413; -pub const TIOCSWINSZ = 0x5414; -pub const TIOCMGET = 0x5415; -pub const TIOCMBIS = 0x5416; -pub const TIOCMBIC = 0x5417; -pub const TIOCMSET = 0x5418; -pub const TIOCGSOFTCAR = 0x5419; -pub const TIOCSSOFTCAR = 0x541A; -pub const FIONREAD = 0x541B; -pub const TIOCINQ = FIONREAD; -pub const TIOCLINUX = 0x541C; -pub const TIOCCONS = 0x541D; -pub const TIOCGSERIAL = 0x541E; -pub const TIOCSSERIAL = 0x541F; -pub const TIOCPKT = 0x5420; -pub const FIONBIO = 0x5421; -pub const TIOCNOTTY = 0x5422; -pub const TIOCSETD = 0x5423; -pub const TIOCGETD = 0x5424; -pub const TCSBRKP = 0x5425; -pub const TIOCSBRK = 0x5427; -pub const TIOCCBRK = 0x5428; -pub const TIOCGSID = 0x5429; -pub const TIOCGRS485 = 0x542E; -pub const TIOCSRS485 = 0x542F; -pub const TIOCGPTN = 0x80045430; -pub const TIOCSPTLCK = 0x40045431; -pub const TIOCGDEV = 0x80045432; -pub const TCGETX = 0x5432; -pub const TCSETX = 0x5433; -pub const TCSETXF = 0x5434; -pub const TCSETXW = 0x5435; -pub const TIOCSIG = 0x40045436; -pub const TIOCVHANGUP = 0x5437; -pub const TIOCGPKT = 0x80045438; -pub const TIOCGPTLCK = 0x80045439; -pub const TIOCGEXCL = 0x80045440; - -pub const EPOLL_CLOEXEC = O_CLOEXEC; - -pub const EPOLL_CTL_ADD = 1; -pub const EPOLL_CTL_DEL = 2; -pub const EPOLL_CTL_MOD = 3; - -pub const EPOLLIN = 0x001; -pub const EPOLLPRI = 0x002; -pub const EPOLLOUT = 0x004; -pub const EPOLLRDNORM = 0x040; -pub const EPOLLRDBAND = 0x080; -pub const EPOLLWRNORM = 0x100; -pub const EPOLLWRBAND = 0x200; -pub const EPOLLMSG = 0x400; -pub const EPOLLERR = 0x008; -pub const EPOLLHUP = 0x010; -pub const EPOLLRDHUP = 0x2000; -pub const EPOLLEXCLUSIVE = (u32(1) << 28); -pub const EPOLLWAKEUP = (u32(1) << 29); -pub const EPOLLONESHOT = (u32(1) << 30); -pub const EPOLLET = (u32(1) << 31); - -pub const CLOCK_REALTIME = 0; -pub const CLOCK_MONOTONIC = 1; -pub const CLOCK_PROCESS_CPUTIME_ID = 2; -pub const CLOCK_THREAD_CPUTIME_ID = 3; -pub const CLOCK_MONOTONIC_RAW = 4; -pub const CLOCK_REALTIME_COARSE = 5; -pub const CLOCK_MONOTONIC_COARSE = 6; -pub const CLOCK_BOOTTIME = 7; -pub const CLOCK_REALTIME_ALARM = 8; -pub const CLOCK_BOOTTIME_ALARM = 9; -pub const CLOCK_SGI_CYCLE = 10; -pub const CLOCK_TAI = 11; - -pub const CSIGNAL = 0x000000ff; -pub const CLONE_VM = 0x00000100; -pub const CLONE_FS = 0x00000200; -pub const CLONE_FILES = 0x00000400; -pub const CLONE_SIGHAND = 0x00000800; -pub const CLONE_PTRACE = 0x00002000; -pub const CLONE_VFORK = 0x00004000; -pub const CLONE_PARENT = 0x00008000; -pub const CLONE_THREAD = 0x00010000; -pub const CLONE_NEWNS = 0x00020000; -pub const CLONE_SYSVSEM = 0x00040000; -pub const CLONE_SETTLS = 0x00080000; -pub const CLONE_PARENT_SETTID = 0x00100000; -pub const CLONE_CHILD_CLEARTID = 0x00200000; -pub const CLONE_DETACHED = 0x00400000; -pub const CLONE_UNTRACED = 0x00800000; -pub const CLONE_CHILD_SETTID = 0x01000000; -pub const CLONE_NEWCGROUP = 0x02000000; -pub const CLONE_NEWUTS = 0x04000000; -pub const CLONE_NEWIPC = 0x08000000; -pub const CLONE_NEWUSER = 0x10000000; -pub const CLONE_NEWPID = 0x20000000; -pub const CLONE_NEWNET = 0x40000000; -pub const CLONE_IO = 0x80000000; - -pub const EFD_SEMAPHORE = 1; -pub const EFD_CLOEXEC = O_CLOEXEC; -pub const EFD_NONBLOCK = O_NONBLOCK; - -pub const MS_RDONLY = 1; -pub const MS_NOSUID = 2; -pub const MS_NODEV = 4; -pub const MS_NOEXEC = 8; -pub const MS_SYNCHRONOUS = 16; -pub const MS_REMOUNT = 32; -pub const MS_MANDLOCK = 64; -pub const MS_DIRSYNC = 128; -pub const MS_NOATIME = 1024; -pub const MS_NODIRATIME = 2048; -pub const MS_BIND = 4096; -pub const MS_MOVE = 8192; -pub const MS_REC = 16384; -pub const MS_SILENT = 32768; -pub const MS_POSIXACL = (1 << 16); -pub const MS_UNBINDABLE = (1 << 17); -pub const MS_PRIVATE = (1 << 18); -pub const MS_SLAVE = (1 << 19); -pub const MS_SHARED = (1 << 20); -pub const MS_RELATIME = (1 << 21); -pub const MS_KERNMOUNT = (1 << 22); -pub const MS_I_VERSION = (1 << 23); -pub const MS_STRICTATIME = (1 << 24); -pub const MS_LAZYTIME = (1 << 25); -pub const MS_NOREMOTELOCK = (1 << 27); -pub const MS_NOSEC = (1 << 28); -pub const MS_BORN = (1 << 29); -pub const MS_ACTIVE = (1 << 30); -pub const MS_NOUSER = (1 << 31); - -pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); - -pub const MS_MGC_VAL = 0xc0ed0000; -pub const MS_MGC_MSK = 0xffff0000; - -pub const MNT_FORCE = 1; -pub const MNT_DETACH = 2; -pub const MNT_EXPIRE = 4; -pub const UMOUNT_NOFOLLOW = 8; - -pub const IN_CLOEXEC = O_CLOEXEC; -pub const IN_NONBLOCK = O_NONBLOCK; - -pub const IN_ACCESS = 0x00000001; -pub const IN_MODIFY = 0x00000002; -pub const IN_ATTRIB = 0x00000004; -pub const IN_CLOSE_WRITE = 0x00000008; -pub const IN_CLOSE_NOWRITE = 0x00000010; -pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; -pub const IN_OPEN = 0x00000020; -pub const IN_MOVED_FROM = 0x00000040; -pub const IN_MOVED_TO = 0x00000080; -pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; -pub const IN_CREATE = 0x00000100; -pub const IN_DELETE = 0x00000200; -pub const IN_DELETE_SELF = 0x00000400; -pub const IN_MOVE_SELF = 0x00000800; -pub const IN_ALL_EVENTS = 0x00000fff; - -pub const IN_UNMOUNT = 0x00002000; -pub const IN_Q_OVERFLOW = 0x00004000; -pub const IN_IGNORED = 0x00008000; - -pub const IN_ONLYDIR = 0x01000000; -pub const IN_DONT_FOLLOW = 0x02000000; -pub const IN_EXCL_UNLINK = 0x04000000; -pub const IN_MASK_ADD = 0x20000000; - -pub const IN_ISDIR = 0x40000000; -pub const IN_ONESHOT = 0x80000000; - -pub const S_IFMT = 0o170000; - -pub const S_IFDIR = 0o040000; -pub const S_IFCHR = 0o020000; -pub const S_IFBLK = 0o060000; -pub const S_IFREG = 0o100000; -pub const S_IFIFO = 0o010000; -pub const S_IFLNK = 0o120000; -pub const S_IFSOCK = 0o140000; - -pub const S_ISUID = 0o4000; -pub const S_ISGID = 0o2000; -pub const S_ISVTX = 0o1000; -pub const S_IRUSR = 0o400; -pub const S_IWUSR = 0o200; -pub const S_IXUSR = 0o100; -pub const S_IRWXU = 0o700; -pub const S_IRGRP = 0o040; -pub const S_IWGRP = 0o020; -pub const S_IXGRP = 0o010; -pub const S_IRWXG = 0o070; -pub const S_IROTH = 0o004; -pub const S_IWOTH = 0o002; -pub const S_IXOTH = 0o001; -pub const S_IRWXO = 0o007; - -pub fn S_ISREG(m: u32) bool { - return m & S_IFMT == S_IFREG; -} - -pub fn S_ISDIR(m: u32) bool { - return m & S_IFMT == S_IFDIR; -} - -pub fn S_ISCHR(m: u32) bool { - return m & S_IFMT == S_IFCHR; -} - -pub fn S_ISBLK(m: u32) bool { - return m & S_IFMT == S_IFBLK; -} - -pub fn S_ISFIFO(m: u32) bool { - return m & S_IFMT == S_IFIFO; -} - -pub fn S_ISLNK(m: u32) bool { - return m & S_IFMT == S_IFLNK; -} - -pub fn S_ISSOCK(m: u32) bool { - return m & S_IFMT == S_IFSOCK; -} - -pub const TFD_NONBLOCK = O_NONBLOCK; -pub const TFD_CLOEXEC = O_CLOEXEC; - -pub const TFD_TIMER_ABSTIME = 1; -pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); - -fn unsigned(s: i32) u32 { - return @bitCast(u32, s); -} -fn signed(s: u32) i32 { - return @bitCast(i32, s); -} -pub fn WEXITSTATUS(s: i32) i32 { - return signed((unsigned(s) & 0xff00) >> 8); -} -pub fn WTERMSIG(s: i32) i32 { - return signed(unsigned(s) & 0x7f); -} -pub fn WSTOPSIG(s: i32) i32 { - return WEXITSTATUS(s); -} -pub fn WIFEXITED(s: i32) bool { - return WTERMSIG(s) == 0; -} -pub fn WIFSTOPPED(s: i32) bool { - return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; -} -pub fn WIFSIGNALED(s: i32) bool { - return (unsigned(s) & 0xffff) -% 1 < 0xff; -} - -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, -}; - -/// Get the errno from a syscall return value, or 0 for no error. -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 dup2(old: i32, new: i32) usize { - return dup3(old, new, 0); -} - -pub fn dup3(old: i32, new: i32, flags: u32) usize { - return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn chdir(path: [*]const u8) usize { - return syscall1(SYS_chdir, @ptrToInt(path)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn chroot(path: [*]const u8) usize { - return syscall1(SYS_chroot, @ptrToInt(path)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); -} - -pub fn fork() usize { - return clone2(SIGCHLD, 0); -} - -/// This must be inline, and inline call the syscall function, because if the -/// child does a return it will clobber the parent's stack. -/// It is advised to avoid this function and use clone instead, because -/// the compiler is not aware of how vfork affects control flow and you may -/// see different results in optimized builds. -pub inline fn vfork() usize { - return @inlineCall(syscall0, SYS_vfork); -} - -pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { - return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout)); -} - -pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize { - return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return syscall2(SYS_getcwd, @ptrToInt(buf), size); -} - -pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { - return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); -} - -pub fn inotify_init1(flags: u32) usize { - return syscall1(SYS_inotify_init1, flags); -} - -pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { - return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); -} - -pub fn inotify_rm_watch(fd: i32, wd: i32) usize { - return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); -} - -pub fn isatty(fd: i32) bool { - var wsz: winsize = undefined; - return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return mkdirat(AT_FDCWD, path, mode); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { - return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { - return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn umount(special: [*]const u8) usize { - return syscall2(SYS_umount2, @ptrToInt(special), 0); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn umount2(special: [*]const u8, flags: u32) usize { - return syscall2(SYS_umount2, @ptrToInt(special), flags); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); -} - -pub fn mprotect(address: usize, length: usize, protection: usize) usize { - return syscall3(SYS_mprotect, address, length, protection); -} - -pub fn munmap(address: usize, length: usize) usize { - return syscall2(SYS_munmap, address, length); -} - -pub fn read(fd: i32, buf: [*]u8, count: usize) usize { - return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); -} - -pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { - return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); -} - -pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { - return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); -} - -pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { - return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); -} - -pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { - return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn rmdir(path: [*]const u8) usize { - return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return symlinkat(existing, AT_FDCWD, new); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { - return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { - return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn access(path: [*]const u8, mode: u32) usize { - return faccessat(AT_FDCWD, path, mode); -} - -pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { - return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); -} - -pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); -} - -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - return syscall2(SYS_pipe2, @ptrToInt(fd), flags); -} - -pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { - return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { - return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { - return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { - return openat(AT_FDCWD, path, flags, perm); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn create(path: [*]const u8, perm: usize) usize { - return syscall2(SYS_creat, @ptrToInt(path), perm); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { - // dirfd could be negative, for example AT_FDCWD is -100 - return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); -} - -/// See also `clone` (from the arch-specific include) -pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { - return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); -} - -/// See also `clone` (from the arch-specific include) -pub fn clone2(flags: u32, child_stack_ptr: usize) usize { - return syscall2(SYS_clone, flags, child_stack_ptr); -} - -pub fn close(fd: i32) usize { - return syscall1(SYS_close, @bitCast(usize, isize(fd))); -} - -pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { - return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); -} - -pub fn exit(status: i32) noreturn { - _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); - unreachable; -} - -pub fn exit_group(status: i32) noreturn { - _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); - unreachable; -} - -pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { - return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); -} - -pub fn kill(pid: i32, sig: i32) usize { - return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn unlink(path: [*]const u8) usize { - return unlinkat(AT_FDCWD, path, 0); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { - return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); -} - -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); -} - -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 rc = f(clk_id, tp); - switch (rc) { - 0, @bitCast(usize, isize(-EINVAL)) => return rc, - else => {}, - } - } - } - 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); -} - -pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); -} - -pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { - return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); -} - -pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { - return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); -} - -pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { - return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); -} - -pub fn setuid(uid: u32) usize { - return syscall1(SYS_setuid, uid); -} - -pub fn setgid(gid: u32) usize { - return syscall1(SYS_setgid, gid); -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return syscall2(SYS_setreuid, ruid, euid); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return syscall2(SYS_setregid, rgid, egid); -} - -pub fn getuid() u32 { - return u32(syscall0(SYS_getuid)); -} - -pub fn getgid() u32 { - return u32(syscall0(SYS_getgid)); -} - -pub fn geteuid() u32 { - return u32(syscall0(SYS_geteuid)); -} - -pub fn getegid() u32 { - return u32(syscall0(SYS_getegid)); -} - -pub fn seteuid(euid: u32) usize { - return syscall1(SYS_seteuid, euid); -} - -pub fn setegid(egid: u32) usize { - return syscall1(SYS_setegid, egid); -} - -pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { - return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); -} - -pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { - return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); -} - -pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { - return syscall3(SYS_setresuid, ruid, euid, suid); -} - -pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { - return syscall3(SYS_setresgid, rgid, egid, sgid); -} - -pub fn getgroups(size: usize, list: *u32) usize { - return syscall2(SYS_getgroups, size, @ptrToInt(list)); -} - -pub fn setgroups(size: usize, list: *const u32) usize { - return syscall2(SYS_setgroups, size, @ptrToInt(list)); -} - -pub fn getpid() i32 { - return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); -} - -pub fn gettid() i32 { - return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); -} - -pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { - return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); -} - -pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { - assert(sig >= 1); - assert(sig != SIGKILL); - assert(sig != SIGSTOP); - var ksa = k_sigaction{ - .handler = act.handler, - .flags = act.flags | SA_RESTORER, - .mask = undefined, - .restorer = @ptrCast(extern fn () void, restore_rt), - }; - var ksa_old: k_sigaction = undefined; - @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); - const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); - const err = getErrno(result); - if (err != 0) { - return result; - } - if (oact) |old| { - old.handler = ksa_old.handler; - old.flags = @truncate(u32, ksa_old.flags); - @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); - } - return 0; -} - -const NSIG = 65; -const sigset_t = [128 / @sizeOf(usize)]usize; -const all_mask = []usize{maxInt(usize)}; -const app_mask = []usize{0xfffffffc7fffffff}; - -const k_sigaction = extern struct { - handler: extern fn (i32) void, - flags: usize, - restorer: extern fn () void, - mask: [2]u32, -}; - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = struct { - handler: extern fn (i32) void, - mask: sigset_t, - flags: u32, -}; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); -pub const empty_sigset = []usize{0} ** sigset_t.len; - -pub fn raise(sig: i32) usize { - var set: sigset_t = undefined; - blockAppSignals(&set); - const tid = syscall0(SYS_gettid); - const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); - restoreSignals(&set); - return ret; -} - -fn blockAllSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); -} - -fn blockAppSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); -} - -fn restoreSignals(set: *sigset_t) void { - _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); -} - -pub fn sigaddset(set: *sigset_t, sig: u6) void { - const s = sig - 1; - (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); -} - -pub fn sigismember(set: *const sigset_t, sig: u6) bool { - const s = sig - 1; - return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; -} - -pub const in_port_t = u16; -pub const sa_family_t = u16; -pub const socklen_t = u32; - -/// This intentionally only has ip4 and ip6 -pub const sockaddr = extern union { - in: sockaddr_in, - in6: sockaddr_in6, -}; - -pub const sockaddr_in = extern struct { - family: sa_family_t, - port: in_port_t, - addr: u32, - zero: [8]u8, -}; - -pub const sockaddr_in6 = extern struct { - family: sa_family_t, - port: in_port_t, - flowinfo: u32, - addr: [16]u8, - scope_id: u32, -}; - -pub const sockaddr_un = extern struct { - family: sa_family_t, - path: [108]u8, -}; - -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, -}; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -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)); -} - -pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); -} - -pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { - return syscall3(SYS_socket, domain, socket_type, protocol); -} - -pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { - return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); -} - -pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { - 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 { - return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), 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); -} - -pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { - return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); -} - -pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { - return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); -} - -pub fn shutdown(fd: i32, how: i32) usize { - return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); -} - -pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { - return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); -} - -pub fn listen(fd: i32, backlog: u32) usize { - return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); -} - -pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { - return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); -} - -pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { - return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); -} - -pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { - return accept4(fd, addr, len, 0); -} - -pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { - return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); -} - -pub fn fstat(fd: i32, stat_buf: *Stat) usize { - return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { - return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { - return fstatat(AF_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { - return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { - return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { - return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); -} - -pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { - return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { - return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { - return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { - return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { - return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { - return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { - return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { - return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { - return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); -} - -// TODO https://github.com/ziglang/zig/issues/265 -pub fn fremovexattr(fd: usize, name: [*]const u8) usize { - return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); -} - -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 { - ptr: usize, - fd: i32, - @"u32": u32, - @"u64": u64, -}; - -pub const epoll_event = packed struct { - events: u32, - data: epoll_data, -}; - -pub fn epoll_create() usize { - return epoll_create1(0); -} - -pub fn epoll_create1(flags: usize) usize { - return syscall1(SYS_epoll_create1, flags); -} - -pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { - return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); -} - -pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { - return epoll_pwait(epoll_fd, events, maxevents, timeout, null); -} - -pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { - return syscall6( - SYS_epoll_pwait, - @bitCast(usize, isize(epoll_fd)), - @ptrToInt(events), - @intCast(usize, maxevents), - @bitCast(usize, isize(timeout)), - @ptrToInt(sigmask), - @sizeOf(sigset_t), - ); -} - -pub fn eventfd(count: u32, flags: u32) usize { - return syscall2(SYS_eventfd2, count, flags); -} - -pub fn timerfd_create(clockid: i32, flags: u32) usize { - return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); -} - -pub const itimerspec = extern struct { - it_interval: timespec, - it_value: timespec, -}; - -pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { - return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); -} - -pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { - return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); -} - -pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; -pub const _LINUX_CAPABILITY_U32S_1 = 1; - -pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; -pub const _LINUX_CAPABILITY_U32S_2 = 2; - -pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; -pub const _LINUX_CAPABILITY_U32S_3 = 2; - -pub const VFS_CAP_REVISION_MASK = 0xFF000000; -pub const VFS_CAP_REVISION_SHIFT = 24; -pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; -pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; - -pub const VFS_CAP_REVISION_1 = 0x01000000; -pub const VFS_CAP_U32_1 = 1; -pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); - -pub const VFS_CAP_REVISION_2 = 0x02000000; -pub const VFS_CAP_U32_2 = 2; -pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); - -pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; -pub const VFS_CAP_U32 = VFS_CAP_U32_2; -pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; - -pub const vfs_cap_data = extern struct { - //all of these are mandated as little endian - //when on disk. - const Data = struct { - permitted: u32, - inheritable: u32, - }; - - magic_etc: u32, - data: [VFS_CAP_U32]Data, -}; - -pub const CAP_CHOWN = 0; -pub const CAP_DAC_OVERRIDE = 1; -pub const CAP_DAC_READ_SEARCH = 2; -pub const CAP_FOWNER = 3; -pub const CAP_FSETID = 4; -pub const CAP_KILL = 5; -pub const CAP_SETGID = 6; -pub const CAP_SETUID = 7; -pub const CAP_SETPCAP = 8; -pub const CAP_LINUX_IMMUTABLE = 9; -pub const CAP_NET_BIND_SERVICE = 10; -pub const CAP_NET_BROADCAST = 11; -pub const CAP_NET_ADMIN = 12; -pub const CAP_NET_RAW = 13; -pub const CAP_IPC_LOCK = 14; -pub const CAP_IPC_OWNER = 15; -pub const CAP_SYS_MODULE = 16; -pub const CAP_SYS_RAWIO = 17; -pub const CAP_SYS_CHROOT = 18; -pub const CAP_SYS_PTRACE = 19; -pub const CAP_SYS_PACCT = 20; -pub const CAP_SYS_ADMIN = 21; -pub const CAP_SYS_BOOT = 22; -pub const CAP_SYS_NICE = 23; -pub const CAP_SYS_RESOURCE = 24; -pub const CAP_SYS_TIME = 25; -pub const CAP_SYS_TTY_CONFIG = 26; -pub const CAP_MKNOD = 27; -pub const CAP_LEASE = 28; -pub const CAP_AUDIT_WRITE = 29; -pub const CAP_AUDIT_CONTROL = 30; -pub const CAP_SETFCAP = 31; -pub const CAP_MAC_OVERRIDE = 32; -pub const CAP_MAC_ADMIN = 33; -pub const CAP_SYSLOG = 34; -pub const CAP_WAKE_ALARM = 35; -pub const CAP_BLOCK_SUSPEND = 36; -pub const CAP_AUDIT_READ = 37; -pub const CAP_LAST_CAP = CAP_AUDIT_READ; - -pub fn cap_valid(u8: x) bool { - return x >= 0 and x <= CAP_LAST_CAP; -} - -pub fn CAP_TO_MASK(cap: u8) u32 { - return u32(1) << u5(cap & 31); -} - -pub fn CAP_TO_INDEX(cap: u8) u8 { - return cap >> 5; -} - -pub const cap_t = extern struct { - hdrp: *cap_user_header_t, - datap: *cap_user_data_t, -}; - -pub const cap_user_header_t = extern struct { - version: u32, - pid: usize, -}; - -pub const cap_user_data_t = extern struct { - effective: u32, - permitted: u32, - inheritable: u32, -}; - -pub fn unshare(flags: usize) usize { - return syscall1(SYS_unshare, flags); -} - -pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { - return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); -} - -pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { - return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); -} - -pub const inotify_event = extern struct { - wd: i32, - mask: u32, - cookie: u32, - len: u32, - //name: [?]u8, -}; - -pub const dirent64 = extern struct { - d_ino: u64, - d_off: u64, - d_reclen: u16, - d_type: u8, - d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 -}; - -test "import" { - if (builtin.os == builtin.Os.linux) { - _ = @import("test.zig"); - } -} diff --git a/std/os/linux/test.zig b/std/os/linux/test.zig index 40fb7823d2..286748b70c 100644 --- a/std/os/linux/test.zig +++ b/std/os/linux/test.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const builtin = @import("builtin"); const linux = std.os.linux; const expect = std.testing.expect; diff --git a/std/os/linux/vdso.zig b/std/os/linux/vdso.zig index 07745ad1eb..efd69d8542 100644 --- a/std/os/linux/vdso.zig +++ b/std/os/linux/vdso.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const elf = std.elf; const linux = std.os.linux; const cstr = std.cstr; diff --git a/std/os/linux/x86_64.zig b/std/os/linux/x86_64.zig index 2ea369d9f2..d194cd4003 100644 --- a/std/os/linux/x86_64.zig +++ b/std/os/linux/x86_64.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const linux = std.os.linux; const socklen_t = linux.socklen_t; const iovec = linux.iovec; diff --git a/std/os/netbsd.zig b/std/os/netbsd.zig new file mode 100644 index 0000000000..2d4b6ff124 --- /dev/null +++ b/std/os/netbsd.zig @@ -0,0 +1,724 @@ +const builtin = @import("builtin"); + +pub use @import("netbsd/errno.zig"); + +const std = @import("../std.zig"); +const c = std.c; + +const assert = std.debug.assert; +const maxInt = std.math.maxInt; +pub const Kevent = c.Kevent; + +pub const CTL_KERN = 1; +pub const CTL_DEBUG = 5; + +pub const KERN_PROC_ARGS = 48; // struct: process argv/env +pub const KERN_PROC_PATHNAME = 5; // path to executable + +pub const PATH_MAX = 1024; + +pub const STDIN_FILENO = 0; +pub const STDOUT_FILENO = 1; +pub const STDERR_FILENO = 2; + +pub const PROT_NONE = 0; +pub const PROT_READ = 1; +pub const PROT_WRITE = 2; +pub const PROT_EXEC = 4; + +pub const CLOCK_REALTIME = 0; +pub const CLOCK_VIRTUAL = 1; +pub const CLOCK_PROF = 2; +pub const CLOCK_MONOTONIC = 3; +pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; +pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; + +pub const MAP_FAILED = maxInt(usize); +pub const MAP_SHARED = 0x0001; +pub const MAP_PRIVATE = 0x0002; +pub const MAP_REMAPDUP = 0x0004; +pub const MAP_FIXED = 0x0010; +pub const MAP_RENAME = 0x0020; +pub const MAP_NORESERVE = 0x0040; +pub const MAP_INHERIT = 0x0080; +pub const MAP_HASSEMAPHORE = 0x0200; +pub const MAP_TRYFIXED = 0x0400; +pub const MAP_WIRED = 0x0800; + +pub const MAP_FILE = 0x0000; +pub const MAP_NOSYNC = 0x0800; +pub const MAP_ANON = 0x1000; +pub const MAP_ANONYMOUS = MAP_ANON; +pub const MAP_STACK = 0x2000; + +pub const WNOHANG = 0x00000001; +pub const WUNTRACED = 0x00000002; +pub const WSTOPPED = WUNTRACED; +pub const WCONTINUED = 0x00000010; +pub const WNOWAIT = 0x00010000; +pub const WEXITED = 0x00000020; +pub const WTRAPPED = 0x00000040; + +pub const SA_ONSTACK = 0x0001; +pub const SA_RESTART = 0x0002; +pub const SA_RESETHAND = 0x0004; +pub const SA_NOCLDSTOP = 0x0008; +pub const SA_NODEFER = 0x0010; +pub const SA_NOCLDWAIT = 0x0020; +pub const SA_SIGINFO = 0x0040; + +pub const SIGHUP = 1; +pub const SIGINT = 2; +pub const SIGQUIT = 3; +pub const SIGILL = 4; +pub const SIGTRAP = 5; +pub const SIGABRT = 6; +pub const SIGIOT = SIGABRT; +pub const SIGEMT = 7; +pub const SIGFPE = 8; +pub const SIGKILL = 9; +pub const SIGBUS = 10; +pub const SIGSEGV = 11; +pub const SIGSYS = 12; +pub const SIGPIPE = 13; +pub const SIGALRM = 14; +pub const SIGTERM = 15; +pub const SIGURG = 16; +pub const SIGSTOP = 17; +pub const SIGTSTP = 18; +pub const SIGCONT = 19; +pub const SIGCHLD = 20; +pub const SIGTTIN = 21; +pub const SIGTTOU = 22; +pub const SIGIO = 23; +pub const SIGXCPU = 24; +pub const SIGXFSZ = 25; +pub const SIGVTALRM = 26; +pub const SIGPROF = 27; +pub const SIGWINCH = 28; +pub const SIGINFO = 29; +pub const SIGUSR1 = 30; +pub const SIGUSR2 = 31; +pub const SIGPWR = 32; + +pub const SIGRTMIN = 33; +pub const SIGRTMAX = 63; + +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission + +pub const O_RDONLY = 0x0000; +pub const O_WRONLY = 0x0001; +pub const O_RDWR = 0x0002; +pub const O_ACCMODE = 0x0003; + +pub const O_CREAT = 0x0200; +pub const O_EXCL = 0x0800; +pub const O_NOCTTY = 0x8000; +pub const O_TRUNC = 0x0400; +pub const O_APPEND = 0x0008; +pub const O_NONBLOCK = 0x0004; +pub const O_DSYNC = 0x00010000; +pub const O_SYNC = 0x0080; +pub const O_RSYNC = 0x00020000; +pub const O_DIRECTORY = 0x00080000; +pub const O_NOFOLLOW = 0x00000100; +pub const O_CLOEXEC = 0x00400000; + +pub const O_ASYNC = 0x0040; +pub const O_DIRECT = 0x00080000; +pub const O_LARGEFILE = 0; +pub const O_NOATIME = 0; +pub const O_PATH = 0; +pub const O_TMPFILE = 0; +pub const O_NDELAY = O_NONBLOCK; + +pub const F_DUPFD = 0; +pub const F_GETFD = 1; +pub const F_SETFD = 2; +pub const F_GETFL = 3; +pub const F_SETFL = 4; + +pub const F_GETOWN = 5; +pub const F_SETOWN = 6; + +pub const F_GETLK = 7; +pub const F_SETLK = 8; +pub const F_SETLKW = 9; + +pub const SEEK_SET = 0; +pub const SEEK_CUR = 1; +pub const SEEK_END = 2; + +pub const SIG_BLOCK = 1; +pub const SIG_UNBLOCK = 2; +pub const SIG_SETMASK = 3; + +pub const SOCK_STREAM = 1; +pub const SOCK_DGRAM = 2; +pub const SOCK_RAW = 3; +pub const SOCK_RDM = 4; +pub const SOCK_SEQPACKET = 5; + +pub const SOCK_CLOEXEC = 0x10000000; +pub const SOCK_NONBLOCK = 0x20000000; + +pub const PROTO_ip = 0; +pub const PROTO_icmp = 1; +pub const PROTO_igmp = 2; +pub const PROTO_ggp = 3; +pub const PROTO_ipencap = 4; +pub const PROTO_tcp = 6; +pub const PROTO_egp = 8; +pub const PROTO_pup = 12; +pub const PROTO_udp = 17; +pub const PROTO_xns_idp = 22; +pub const PROTO_iso_tp4 = 29; +pub const PROTO_ipv6 = 41; +pub const PROTO_ipv6_route = 43; +pub const PROTO_ipv6_frag = 44; +pub const PROTO_rsvp = 46; +pub const PROTO_gre = 47; +pub const PROTO_esp = 50; +pub const PROTO_ah = 51; +pub const PROTO_ipv6_icmp = 58; +pub const PROTO_ipv6_nonxt = 59; +pub const PROTO_ipv6_opts = 60; +pub const PROTO_encap = 98; +pub const PROTO_pim = 103; +pub const PROTO_raw = 255; + +pub const PF_UNSPEC = 0; +pub const PF_LOCAL = 1; +pub const PF_UNIX = PF_LOCAL; +pub const PF_FILE = PF_LOCAL; +pub const PF_INET = 2; +pub const PF_APPLETALK = 16; +pub const PF_INET6 = 24; +pub const PF_DECnet = 12; +pub const PF_KEY = 29; +pub const PF_ROUTE = 34; +pub const PF_SNA = 11; +pub const PF_MPLS = 33; +pub const PF_CAN = 35; +pub const PF_BLUETOOTH = 31; +pub const PF_ISDN = 26; +pub const PF_MAX = 37; + +pub const AF_UNSPEC = PF_UNSPEC; +pub const AF_LOCAL = PF_LOCAL; +pub const AF_UNIX = AF_LOCAL; +pub const AF_FILE = AF_LOCAL; +pub const AF_INET = PF_INET; +pub const AF_APPLETALK = PF_APPLETALK; +pub const AF_INET6 = PF_INET6; +pub const AF_KEY = PF_KEY; +pub const AF_ROUTE = PF_ROUTE; +pub const AF_SNA = PF_SNA; +pub const AF_MPLS = PF_MPLS; +pub const AF_CAN = PF_CAN; +pub const AF_BLUETOOTH = PF_BLUETOOTH; +pub const AF_ISDN = PF_ISDN; +pub const AF_MAX = PF_MAX; + +pub const DT_UNKNOWN = 0; +pub const DT_FIFO = 1; +pub const DT_CHR = 2; +pub const DT_DIR = 4; +pub const DT_BLK = 6; +pub const DT_REG = 8; +pub const DT_LNK = 10; +pub const DT_SOCK = 12; +pub const DT_WHT = 14; + +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; + +/// delete event from kq +pub const EV_DELETE = 0x0002; + +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = 0; +pub const EVFILT_WRITE = 1; + +/// attached to aio requests +pub const EVFILT_AIO = 2; + +/// attached to vnodes +pub const EVFILT_VNODE = 3; + +/// attached to struct proc +pub const EVFILT_PROC = 4; + +/// attached to struct proc +pub const EVFILT_SIGNAL = 5; + +/// timers +pub const EVFILT_TIMER = 6; + +/// Filesystem events +pub const EVFILT_FS = 7; + +/// On input, NOTE_TRIGGER causes the event to be triggered for output. +pub const NOTE_TRIGGER = 0x08000000; + +/// low water mark +pub const NOTE_LOWAT = 0x00000001; + +/// vnode was removed +pub const NOTE_DELETE = 0x00000001; + +/// data contents changed +pub const NOTE_WRITE = 0x00000002; + +/// size increased +pub const NOTE_EXTEND = 0x00000004; + +/// attributes changed +pub const NOTE_ATTRIB = 0x00000008; + +/// link count changed +pub const NOTE_LINK = 0x00000010; + +/// vnode was renamed +pub const NOTE_RENAME = 0x00000020; + +/// vnode access was revoked +pub const NOTE_REVOKE = 0x00000040; + +/// process exited +pub const NOTE_EXIT = 0x80000000; + +/// process forked +pub const NOTE_FORK = 0x40000000; + +/// process exec'd +pub const NOTE_EXEC = 0x20000000; + +/// mask for signal & exit status +pub const NOTE_PDATAMASK = 0x000fffff; +pub const NOTE_PCTRLMASK = 0xf0000000; + +pub const TIOCCBRK = 0x2000747a; +pub const TIOCCDTR = 0x20007478; +pub const TIOCCONS = 0x80047462; +pub const TIOCDCDTIMESTAMP = 0x40107458; +pub const TIOCDRAIN = 0x2000745e; +pub const TIOCEXCL = 0x2000740d; +pub const TIOCEXT = 0x80047460; +pub const TIOCFLAG_CDTRCTS = 0x10; +pub const TIOCFLAG_CLOCAL = 0x2; +pub const TIOCFLAG_CRTSCTS = 0x4; +pub const TIOCFLAG_MDMBUF = 0x8; +pub const TIOCFLAG_SOFTCAR = 0x1; +pub const TIOCFLUSH = 0x80047410; +pub const TIOCGETA = 0x402c7413; +pub const TIOCGETD = 0x4004741a; +pub const TIOCGFLAGS = 0x4004745d; +pub const TIOCGLINED = 0x40207442; +pub const TIOCGPGRP = 0x40047477; +pub const TIOCGQSIZE = 0x40047481; +pub const TIOCGRANTPT = 0x20007447; +pub const TIOCGSID = 0x40047463; +pub const TIOCGSIZE = 0x40087468; +pub const TIOCGWINSZ = 0x40087468; +pub const TIOCMBIC = 0x8004746b; +pub const TIOCMBIS = 0x8004746c; +pub const TIOCMGET = 0x4004746a; +pub const TIOCMSET = 0x8004746d; +pub const TIOCM_CAR = 0x40; +pub const TIOCM_CD = 0x40; +pub const TIOCM_CTS = 0x20; +pub const TIOCM_DSR = 0x100; +pub const TIOCM_DTR = 0x2; +pub const TIOCM_LE = 0x1; +pub const TIOCM_RI = 0x80; +pub const TIOCM_RNG = 0x80; +pub const TIOCM_RTS = 0x4; +pub const TIOCM_SR = 0x10; +pub const TIOCM_ST = 0x8; +pub const TIOCNOTTY = 0x20007471; +pub const TIOCNXCL = 0x2000740e; +pub const TIOCOUTQ = 0x40047473; +pub const TIOCPKT = 0x80047470; +pub const TIOCPKT_DATA = 0x0; +pub const TIOCPKT_DOSTOP = 0x20; +pub const TIOCPKT_FLUSHREAD = 0x1; +pub const TIOCPKT_FLUSHWRITE = 0x2; +pub const TIOCPKT_IOCTL = 0x40; +pub const TIOCPKT_NOSTOP = 0x10; +pub const TIOCPKT_START = 0x8; +pub const TIOCPKT_STOP = 0x4; +pub const TIOCPTMGET = 0x40287446; +pub const TIOCPTSNAME = 0x40287448; +pub const TIOCRCVFRAME = 0x80087445; +pub const TIOCREMOTE = 0x80047469; +pub const TIOCSBRK = 0x2000747b; +pub const TIOCSCTTY = 0x20007461; +pub const TIOCSDTR = 0x20007479; +pub const TIOCSETA = 0x802c7414; +pub const TIOCSETAF = 0x802c7416; +pub const TIOCSETAW = 0x802c7415; +pub const TIOCSETD = 0x8004741b; +pub const TIOCSFLAGS = 0x8004745c; +pub const TIOCSIG = 0x2000745f; +pub const TIOCSLINED = 0x80207443; +pub const TIOCSPGRP = 0x80047476; +pub const TIOCSQSIZE = 0x80047480; +pub const TIOCSSIZE = 0x80087467; +pub const TIOCSTART = 0x2000746e; +pub const TIOCSTAT = 0x80047465; +pub const TIOCSTI = 0x80017472; +pub const TIOCSTOP = 0x2000746f; +pub const TIOCSWINSZ = 0x80087467; +pub const TIOCUCNTL = 0x80047466; +pub const TIOCXMTFRAME = 0x80087444; + +pub const sockaddr = c.sockaddr; +pub const sockaddr_in = c.sockaddr_in; +pub const sockaddr_in6 = c.sockaddr_in6; + +fn unsigned(s: i32) u32 { + return @bitCast(u32, s); +} +fn signed(s: u32) i32 { + return @bitCast(i32, s); +} +pub fn WEXITSTATUS(s: i32) i32 { + return signed((unsigned(s) >> 8) & 0xff); +} +pub fn WTERMSIG(s: i32) i32 { + return signed(unsigned(s) & 0x7f); +} +pub fn WSTOPSIG(s: i32) i32 { + return WEXITSTATUS(s); +} +pub fn WIFEXITED(s: i32) bool { + return WTERMSIG(s) == 0; +} + +pub fn WIFCONTINUED(s: i32) bool { + return ((s & 0x7f) == 0xffff); +} + +pub fn WIFSTOPPED(s: i32) bool { + return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); +} + +pub fn WIFSIGNALED(s: i32) bool { + return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); +} + +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, +}; + +/// Get the errno from a syscall return value, or 0 for no error. +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 dup2(old: i32, new: i32) usize { + return errnoWrap(c.dup2(old, new)); +} + +pub fn chdir(path: [*]const u8) usize { + return errnoWrap(c.chdir(path)); +} + +pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { + return errnoWrap(c.execve(path, argv, envp)); +} + +pub fn fork() usize { + return errnoWrap(c.fork()); +} + +pub fn access(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.access(path, mode)); +} + +pub fn getcwd(buf: [*]u8, size: usize) usize { + return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; +} + +pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { + return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); +} + +pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { + return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); +} + +pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { + return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; +} + +pub fn isatty(fd: i32) bool { + return c.isatty(fd) != 0; +} + +pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { + return errnoWrap(c.readlink(path, buf_ptr, buf_len)); +} + +pub fn mkdir(path: [*]const u8, mode: u32) usize { + return errnoWrap(c.mkdir(path, mode)); +} + +pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { + const ptr_result = c.mmap( + @ptrCast(?*c_void, address), + length, + @bitCast(c_int, @intCast(c_uint, prot)), + @bitCast(c_int, c_uint(flags)), + fd, + offset, + ); + const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); + return errnoWrap(isize_result); +} + +pub fn munmap(address: usize, length: usize) usize { + return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); +} + +pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { + return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); +} + +pub fn rmdir(path: [*]const u8) usize { + return errnoWrap(c.rmdir(path)); +} + +pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { + return errnoWrap(c.symlink(existing, new)); +} + +pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); +} + +pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { + return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); +} + +pub fn pipe(fd: *[2]i32) usize { + return pipe2(fd, 0); +} + +pub fn pipe2(fd: *[2]i32, flags: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); +} + +pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { + return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); +} + +pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { + return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); +} + +pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { + return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); +} + +pub fn rename(old: [*]const u8, new: [*]const u8) usize { + return errnoWrap(c.rename(old, new)); +} + +pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { + return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); +} + +pub fn create(path: [*]const u8, perm: usize) usize { + return arch.syscall2(SYS_creat, @ptrToInt(path), perm); +} + +pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { + return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); +} + +pub fn close(fd: i32) usize { + return errnoWrap(c.close(fd)); +} + +pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { + return errnoWrap(c.lseek(fd, offset, whence)); +} + +pub fn exit(code: i32) noreturn { + c.exit(code); +} + +pub fn kill(pid: i32, sig: i32) usize { + return errnoWrap(c.kill(pid, sig)); +} + +pub fn unlink(path: [*]const u8) usize { + return errnoWrap(c.unlink(path)); +} + +pub fn waitpid(pid: i32, status: *i32, options: u32) usize { + comptime assert(i32.bit_count == c_int.bit_count); + return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); +} + +pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { + return errnoWrap(c.nanosleep(req, rem)); +} + +pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_gettime(clk_id, tp)); +} + +pub fn clock_getres(clk_id: i32, tp: *timespec) usize { + return errnoWrap(c.clock_getres(clk_id, tp)); +} + +pub fn setuid(uid: u32) usize { + return errnoWrap(c.setuid(uid)); +} + +pub fn setgid(gid: u32) usize { + return errnoWrap(c.setgid(gid)); +} + +pub fn setreuid(ruid: u32, euid: u32) usize { + return errnoWrap(c.setreuid(ruid, euid)); +} + +pub fn setregid(rgid: u32, egid: u32) usize { + return errnoWrap(c.setregid(rgid, egid)); +} + +const NSIG = 32; + +pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); +pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); +pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); + +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + /// signal handler + __sigaction_u: extern union { + __sa_handler: extern fn (i32) void, + __sa_sigaction: extern fn (i32, *__siginfo, usize) void, + }, + + /// see signal options + sa_flags: u32, + + /// signal mask to apply + sa_mask: sigset_t, +}; + +pub const _SIG_WORDS = 4; +pub const _SIG_MAXSIG = 128; + +pub inline fn _SIG_IDX(sig: usize) usize { + return sig - 1; +} +pub inline fn _SIG_WORD(sig: usize) usize { + return_SIG_IDX(sig) >> 5; +} +pub inline fn _SIG_BIT(sig: usize) usize { + return 1 << (_SIG_IDX(sig) & 31); +} +pub inline fn _SIG_VALID(sig: usize) usize { + return sig <= _SIG_MAXSIG and sig > 0; +} + +pub const sigset_t = extern struct { + __bits: [_SIG_WORDS]u32, +}; + +pub fn raise(sig: i32) usize { + return errnoWrap(c.raise(sig)); +} + +pub const Stat = c.Stat; +pub const dirent = c.dirent; +pub const timespec = c.timespec; + +pub fn fstat(fd: i32, buf: *c.Stat) usize { + return errnoWrap(c.fstat(fd, buf)); +} +pub const iovec = extern struct { + iov_base: [*]u8, + iov_len: usize, +}; + +pub const iovec_const = extern struct { + iov_base: [*]const u8, + iov_len: usize, +}; + +// TODO avoid libc dependency +pub fn kqueue() usize { + return errnoWrap(c.kqueue()); +} + +// TODO avoid libc dependency +pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { + return errnoWrap(c.kevent( + kq, + changelist.ptr, + @intCast(c_int, changelist.len), + eventlist.ptr, + @intCast(c_int, eventlist.len), + timeout, + )); +} + +// TODO avoid libc dependency +pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); +} + +// TODO avoid libc dependency +pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { + return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); +} + +// TODO avoid libc dependency +pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { + return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); +} + +// TODO avoid libc dependency + +/// Takes the return value from a syscall and formats it back in the way +/// that the kernel represents it to libc. Errno was a mistake, let's make +/// it go away forever. +fn errnoWrap(value: isize) usize { + return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); +} diff --git a/std/os/netbsd/index.zig b/std/os/netbsd/index.zig deleted file mode 100644 index ebabc3c801..0000000000 --- a/std/os/netbsd/index.zig +++ /dev/null @@ -1,725 +0,0 @@ -const builtin = @import("builtin"); - -pub use @import("errno.zig"); - -const std = @import("../../index.zig"); -const c = std.c; - -const assert = std.debug.assert; -const maxInt = std.math.maxInt; -pub const Kevent = c.Kevent; - -pub const CTL_KERN = 1; -pub const CTL_DEBUG = 5; - -pub const KERN_PROC_ARGS = 48; // struct: process argv/env -pub const KERN_PROC_PATHNAME = 5; // path to executable - -pub const PATH_MAX = 1024; - -pub const STDIN_FILENO = 0; -pub const STDOUT_FILENO = 1; -pub const STDERR_FILENO = 2; - -pub const PROT_NONE = 0; -pub const PROT_READ = 1; -pub const PROT_WRITE = 2; -pub const PROT_EXEC = 4; - -pub const CLOCK_REALTIME = 0; -pub const CLOCK_VIRTUAL = 1; -pub const CLOCK_PROF = 2; -pub const CLOCK_MONOTONIC = 3; -pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; -pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; - -pub const MAP_FAILED = maxInt(usize); -pub const MAP_SHARED = 0x0001; -pub const MAP_PRIVATE = 0x0002; -pub const MAP_REMAPDUP = 0x0004; -pub const MAP_FIXED = 0x0010; -pub const MAP_RENAME = 0x0020; -pub const MAP_NORESERVE = 0x0040; -pub const MAP_INHERIT = 0x0080; -pub const MAP_HASSEMAPHORE = 0x0200; -pub const MAP_TRYFIXED = 0x0400; -pub const MAP_WIRED = 0x0800; - -pub const MAP_FILE = 0x0000; -pub const MAP_NOSYNC = 0x0800; -pub const MAP_ANON = 0x1000; -pub const MAP_ANONYMOUS = MAP_ANON; -pub const MAP_STACK = 0x2000; - -pub const WNOHANG = 0x00000001; -pub const WUNTRACED = 0x00000002; -pub const WSTOPPED = WUNTRACED; -pub const WCONTINUED = 0x00000010; -pub const WNOWAIT = 0x00010000; -pub const WEXITED = 0x00000020; -pub const WTRAPPED = 0x00000040; - -pub const SA_ONSTACK = 0x0001; -pub const SA_RESTART = 0x0002; -pub const SA_RESETHAND = 0x0004; -pub const SA_NOCLDSTOP = 0x0008; -pub const SA_NODEFER = 0x0010; -pub const SA_NOCLDWAIT = 0x0020; -pub const SA_SIGINFO = 0x0040; - -pub const SIGHUP = 1; -pub const SIGINT = 2; -pub const SIGQUIT = 3; -pub const SIGILL = 4; -pub const SIGTRAP = 5; -pub const SIGABRT = 6; -pub const SIGIOT = SIGABRT; -pub const SIGEMT = 7; -pub const SIGFPE = 8; -pub const SIGKILL = 9; -pub const SIGBUS = 10; -pub const SIGSEGV = 11; -pub const SIGSYS = 12; -pub const SIGPIPE = 13; -pub const SIGALRM = 14; -pub const SIGTERM = 15; -pub const SIGURG = 16; -pub const SIGSTOP = 17; -pub const SIGTSTP = 18; -pub const SIGCONT = 19; -pub const SIGCHLD = 20; -pub const SIGTTIN = 21; -pub const SIGTTOU = 22; -pub const SIGIO = 23; -pub const SIGXCPU = 24; -pub const SIGXFSZ = 25; -pub const SIGVTALRM = 26; -pub const SIGPROF = 27; -pub const SIGWINCH = 28; -pub const SIGINFO = 29; -pub const SIGUSR1 = 30; -pub const SIGUSR2 = 31; -pub const SIGPWR = 32; - -pub const SIGRTMIN = 33; -pub const SIGRTMAX = 63; - -// access function -pub const F_OK = 0; // test for existence of file -pub const X_OK = 1; // test for execute or search permission -pub const W_OK = 2; // test for write permission -pub const R_OK = 4; // test for read permission - - -pub const O_RDONLY = 0x0000; -pub const O_WRONLY = 0x0001; -pub const O_RDWR = 0x0002; -pub const O_ACCMODE = 0x0003; - -pub const O_CREAT = 0x0200; -pub const O_EXCL = 0x0800; -pub const O_NOCTTY = 0x8000; -pub const O_TRUNC = 0x0400; -pub const O_APPEND = 0x0008; -pub const O_NONBLOCK = 0x0004; -pub const O_DSYNC = 0x00010000; -pub const O_SYNC = 0x0080; -pub const O_RSYNC = 0x00020000; -pub const O_DIRECTORY = 0x00080000; -pub const O_NOFOLLOW = 0x00000100; -pub const O_CLOEXEC = 0x00400000; - -pub const O_ASYNC = 0x0040; -pub const O_DIRECT = 0x00080000; -pub const O_LARGEFILE = 0; -pub const O_NOATIME = 0; -pub const O_PATH = 0; -pub const O_TMPFILE = 0; -pub const O_NDELAY = O_NONBLOCK; - -pub const F_DUPFD = 0; -pub const F_GETFD = 1; -pub const F_SETFD = 2; -pub const F_GETFL = 3; -pub const F_SETFL = 4; - -pub const F_GETOWN = 5; -pub const F_SETOWN = 6; - -pub const F_GETLK = 7; -pub const F_SETLK = 8; -pub const F_SETLKW = 9; - -pub const SEEK_SET = 0; -pub const SEEK_CUR = 1; -pub const SEEK_END = 2; - -pub const SIG_BLOCK = 1; -pub const SIG_UNBLOCK = 2; -pub const SIG_SETMASK = 3; - -pub const SOCK_STREAM = 1; -pub const SOCK_DGRAM = 2; -pub const SOCK_RAW = 3; -pub const SOCK_RDM = 4; -pub const SOCK_SEQPACKET = 5; - -pub const SOCK_CLOEXEC = 0x10000000; -pub const SOCK_NONBLOCK = 0x20000000; - -pub const PROTO_ip = 0; -pub const PROTO_icmp = 1; -pub const PROTO_igmp = 2; -pub const PROTO_ggp = 3; -pub const PROTO_ipencap = 4; -pub const PROTO_tcp = 6; -pub const PROTO_egp = 8; -pub const PROTO_pup = 12; -pub const PROTO_udp = 17; -pub const PROTO_xns_idp = 22; -pub const PROTO_iso_tp4 = 29; -pub const PROTO_ipv6 = 41; -pub const PROTO_ipv6_route = 43; -pub const PROTO_ipv6_frag = 44; -pub const PROTO_rsvp = 46; -pub const PROTO_gre = 47; -pub const PROTO_esp = 50; -pub const PROTO_ah = 51; -pub const PROTO_ipv6_icmp = 58; -pub const PROTO_ipv6_nonxt = 59; -pub const PROTO_ipv6_opts = 60; -pub const PROTO_encap = 98; -pub const PROTO_pim = 103; -pub const PROTO_raw = 255; - -pub const PF_UNSPEC = 0; -pub const PF_LOCAL = 1; -pub const PF_UNIX = PF_LOCAL; -pub const PF_FILE = PF_LOCAL; -pub const PF_INET = 2; -pub const PF_APPLETALK = 16; -pub const PF_INET6 = 24; -pub const PF_DECnet = 12; -pub const PF_KEY = 29; -pub const PF_ROUTE = 34; -pub const PF_SNA = 11; -pub const PF_MPLS = 33; -pub const PF_CAN = 35; -pub const PF_BLUETOOTH = 31; -pub const PF_ISDN = 26; -pub const PF_MAX = 37; - -pub const AF_UNSPEC = PF_UNSPEC; -pub const AF_LOCAL = PF_LOCAL; -pub const AF_UNIX = AF_LOCAL; -pub const AF_FILE = AF_LOCAL; -pub const AF_INET = PF_INET; -pub const AF_APPLETALK = PF_APPLETALK; -pub const AF_INET6 = PF_INET6; -pub const AF_KEY = PF_KEY; -pub const AF_ROUTE = PF_ROUTE; -pub const AF_SNA = PF_SNA; -pub const AF_MPLS = PF_MPLS; -pub const AF_CAN = PF_CAN; -pub const AF_BLUETOOTH = PF_BLUETOOTH; -pub const AF_ISDN = PF_ISDN; -pub const AF_MAX = PF_MAX; - -pub const DT_UNKNOWN = 0; -pub const DT_FIFO = 1; -pub const DT_CHR = 2; -pub const DT_DIR = 4; -pub const DT_BLK = 6; -pub const DT_REG = 8; -pub const DT_LNK = 10; -pub const DT_SOCK = 12; -pub const DT_WHT = 14; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -pub const EVFILT_READ = 0; -pub const EVFILT_WRITE = 1; - -/// attached to aio requests -pub const EVFILT_AIO = 2; - -/// attached to vnodes -pub const EVFILT_VNODE = 3; - -/// attached to struct proc -pub const EVFILT_PROC = 4; - -/// attached to struct proc -pub const EVFILT_SIGNAL = 5; - -/// timers -pub const EVFILT_TIMER = 6; - -/// Filesystem events -pub const EVFILT_FS = 7; - -/// On input, NOTE_TRIGGER causes the event to be triggered for output. -pub const NOTE_TRIGGER = 0x08000000; - -/// low water mark -pub const NOTE_LOWAT = 0x00000001; - -/// vnode was removed -pub const NOTE_DELETE = 0x00000001; - -/// data contents changed -pub const NOTE_WRITE = 0x00000002; - -/// size increased -pub const NOTE_EXTEND = 0x00000004; - -/// attributes changed -pub const NOTE_ATTRIB = 0x00000008; - -/// link count changed -pub const NOTE_LINK = 0x00000010; - -/// vnode was renamed -pub const NOTE_RENAME = 0x00000020; - -/// vnode access was revoked -pub const NOTE_REVOKE = 0x00000040; - -/// process exited -pub const NOTE_EXIT = 0x80000000; - -/// process forked -pub const NOTE_FORK = 0x40000000; - -/// process exec'd -pub const NOTE_EXEC = 0x20000000; - -/// mask for signal & exit status -pub const NOTE_PDATAMASK = 0x000fffff; -pub const NOTE_PCTRLMASK = 0xf0000000; - -pub const TIOCCBRK = 0x2000747a; -pub const TIOCCDTR = 0x20007478; -pub const TIOCCONS = 0x80047462; -pub const TIOCDCDTIMESTAMP = 0x40107458; -pub const TIOCDRAIN = 0x2000745e; -pub const TIOCEXCL = 0x2000740d; -pub const TIOCEXT = 0x80047460; -pub const TIOCFLAG_CDTRCTS = 0x10; -pub const TIOCFLAG_CLOCAL = 0x2; -pub const TIOCFLAG_CRTSCTS = 0x4; -pub const TIOCFLAG_MDMBUF = 0x8; -pub const TIOCFLAG_SOFTCAR = 0x1; -pub const TIOCFLUSH = 0x80047410; -pub const TIOCGETA = 0x402c7413; -pub const TIOCGETD = 0x4004741a; -pub const TIOCGFLAGS = 0x4004745d; -pub const TIOCGLINED = 0x40207442; -pub const TIOCGPGRP = 0x40047477; -pub const TIOCGQSIZE = 0x40047481; -pub const TIOCGRANTPT = 0x20007447; -pub const TIOCGSID = 0x40047463; -pub const TIOCGSIZE = 0x40087468; -pub const TIOCGWINSZ = 0x40087468; -pub const TIOCMBIC = 0x8004746b; -pub const TIOCMBIS = 0x8004746c; -pub const TIOCMGET = 0x4004746a; -pub const TIOCMSET = 0x8004746d; -pub const TIOCM_CAR = 0x40; -pub const TIOCM_CD = 0x40; -pub const TIOCM_CTS = 0x20; -pub const TIOCM_DSR = 0x100; -pub const TIOCM_DTR = 0x2; -pub const TIOCM_LE = 0x1; -pub const TIOCM_RI = 0x80; -pub const TIOCM_RNG = 0x80; -pub const TIOCM_RTS = 0x4; -pub const TIOCM_SR = 0x10; -pub const TIOCM_ST = 0x8; -pub const TIOCNOTTY = 0x20007471; -pub const TIOCNXCL = 0x2000740e; -pub const TIOCOUTQ = 0x40047473; -pub const TIOCPKT = 0x80047470; -pub const TIOCPKT_DATA = 0x0; -pub const TIOCPKT_DOSTOP = 0x20; -pub const TIOCPKT_FLUSHREAD = 0x1; -pub const TIOCPKT_FLUSHWRITE = 0x2; -pub const TIOCPKT_IOCTL = 0x40; -pub const TIOCPKT_NOSTOP = 0x10; -pub const TIOCPKT_START = 0x8; -pub const TIOCPKT_STOP = 0x4; -pub const TIOCPTMGET = 0x40287446; -pub const TIOCPTSNAME = 0x40287448; -pub const TIOCRCVFRAME = 0x80087445; -pub const TIOCREMOTE = 0x80047469; -pub const TIOCSBRK = 0x2000747b; -pub const TIOCSCTTY = 0x20007461; -pub const TIOCSDTR = 0x20007479; -pub const TIOCSETA = 0x802c7414; -pub const TIOCSETAF = 0x802c7416; -pub const TIOCSETAW = 0x802c7415; -pub const TIOCSETD = 0x8004741b; -pub const TIOCSFLAGS = 0x8004745c; -pub const TIOCSIG = 0x2000745f; -pub const TIOCSLINED = 0x80207443; -pub const TIOCSPGRP = 0x80047476; -pub const TIOCSQSIZE = 0x80047480; -pub const TIOCSSIZE = 0x80087467; -pub const TIOCSTART = 0x2000746e; -pub const TIOCSTAT = 0x80047465; -pub const TIOCSTI = 0x80017472; -pub const TIOCSTOP = 0x2000746f; -pub const TIOCSWINSZ = 0x80087467; -pub const TIOCUCNTL = 0x80047466; -pub const TIOCXMTFRAME = 0x80087444; - -pub const sockaddr = c.sockaddr; -pub const sockaddr_in = c.sockaddr_in; -pub const sockaddr_in6 = c.sockaddr_in6; - -fn unsigned(s: i32) u32 { - return @bitCast(u32, s); -} -fn signed(s: u32) i32 { - return @bitCast(i32, s); -} -pub fn WEXITSTATUS(s: i32) i32 { - return signed((unsigned(s) >> 8) & 0xff); -} -pub fn WTERMSIG(s: i32) i32 { - return signed(unsigned(s) & 0x7f); -} -pub fn WSTOPSIG(s: i32) i32 { - return WEXITSTATUS(s); -} -pub fn WIFEXITED(s: i32) bool { - return WTERMSIG(s) == 0; -} - -pub fn WIFCONTINUED(s: i32) bool { - return ((s & 0x7f) == 0xffff); -} - -pub fn WIFSTOPPED(s: i32) bool { - return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); -} - -pub fn WIFSIGNALED(s: i32) bool { - return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); -} - -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, -}; - -/// Get the errno from a syscall return value, or 0 for no error. -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 dup2(old: i32, new: i32) usize { - return errnoWrap(c.dup2(old, new)); -} - -pub fn chdir(path: [*]const u8) usize { - return errnoWrap(c.chdir(path)); -} - -pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { - return errnoWrap(c.execve(path, argv, envp)); -} - -pub fn fork() usize { - return errnoWrap(c.fork()); -} - -pub fn access(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.access(path, mode)); -} - -pub fn getcwd(buf: [*]u8, size: usize) usize { - return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { - return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); -} - -pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { - return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); -} - -pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { - return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; -} - -pub fn isatty(fd: i32) bool { - return c.isatty(fd) != 0; -} - -pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { - return errnoWrap(c.readlink(path, buf_ptr, buf_len)); -} - -pub fn mkdir(path: [*]const u8, mode: u32) usize { - return errnoWrap(c.mkdir(path, mode)); -} - -pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { - const ptr_result = c.mmap( - @ptrCast(?*c_void, address), - length, - @bitCast(c_int, @intCast(c_uint, prot)), - @bitCast(c_int, c_uint(flags)), - fd, - offset, - ); - const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); - return errnoWrap(isize_result); -} - -pub fn munmap(address: usize, length: usize) usize { - return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); -} - -pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { - return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); -} - -pub fn rmdir(path: [*]const u8) usize { - return errnoWrap(c.rmdir(path)); -} - -pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.symlink(existing, new)); -} - -pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); -} - -pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { - return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn pipe(fd: *[2]i32) usize { - return pipe2(fd, 0); -} - -pub fn pipe2(fd: *[2]i32, flags: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); -} - -pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { - return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); -} - -pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { - return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); -} - -pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { - return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); -} - -pub fn rename(old: [*]const u8, new: [*]const u8) usize { - return errnoWrap(c.rename(old, new)); -} - -pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { - return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); -} - -pub fn create(path: [*]const u8, perm: usize) usize { - return arch.syscall2(SYS_creat, @ptrToInt(path), perm); -} - -pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { - return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); -} - -pub fn close(fd: i32) usize { - return errnoWrap(c.close(fd)); -} - -pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { - return errnoWrap(c.lseek(fd, offset, whence)); -} - -pub fn exit(code: i32) noreturn { - c.exit(code); -} - -pub fn kill(pid: i32, sig: i32) usize { - return errnoWrap(c.kill(pid, sig)); -} - -pub fn unlink(path: [*]const u8) usize { - return errnoWrap(c.unlink(path)); -} - -pub fn waitpid(pid: i32, status: *i32, options: u32) usize { - comptime assert(i32.bit_count == c_int.bit_count); - return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); -} - -pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { - return errnoWrap(c.nanosleep(req, rem)); -} - -pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_gettime(clk_id, tp)); -} - -pub fn clock_getres(clk_id: i32, tp: *timespec) usize { - return errnoWrap(c.clock_getres(clk_id, tp)); -} - -pub fn setuid(uid: u32) usize { - return errnoWrap(c.setuid(uid)); -} - -pub fn setgid(gid: u32) usize { - return errnoWrap(c.setgid(gid)); -} - -pub fn setreuid(ruid: u32, euid: u32) usize { - return errnoWrap(c.setreuid(ruid, euid)); -} - -pub fn setregid(rgid: u32, egid: u32) usize { - return errnoWrap(c.setregid(rgid, egid)); -} - -const NSIG = 32; - -pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); -pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); -pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); - -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { - /// signal handler - __sigaction_u: extern union { - __sa_handler: extern fn (i32) void, - __sa_sigaction: extern fn (i32, *__siginfo, usize) void, - }, - - /// see signal options - sa_flags: u32, - - /// signal mask to apply - sa_mask: sigset_t, -}; - -pub const _SIG_WORDS = 4; -pub const _SIG_MAXSIG = 128; - -pub inline fn _SIG_IDX(sig: usize) usize { - return sig - 1; -} -pub inline fn _SIG_WORD(sig: usize) usize { - return_SIG_IDX(sig) >> 5; -} -pub inline fn _SIG_BIT(sig: usize) usize { - return 1 << (_SIG_IDX(sig) & 31); -} -pub inline fn _SIG_VALID(sig: usize) usize { - return sig <= _SIG_MAXSIG and sig > 0; -} - -pub const sigset_t = extern struct { - __bits: [_SIG_WORDS]u32, -}; - -pub fn raise(sig: i32) usize { - return errnoWrap(c.raise(sig)); -} - -pub const Stat = c.Stat; -pub const dirent = c.dirent; -pub const timespec = c.timespec; - -pub fn fstat(fd: i32, buf: *c.Stat) usize { - return errnoWrap(c.fstat(fd, buf)); -} -pub const iovec = extern struct { - iov_base: [*]u8, - iov_len: usize, -}; - -pub const iovec_const = extern struct { - iov_base: [*]const u8, - iov_len: usize, -}; - -// TODO avoid libc dependency -pub fn kqueue() usize { - return errnoWrap(c.kqueue()); -} - -// TODO avoid libc dependency -pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { - return errnoWrap(c.kevent( - kq, - changelist.ptr, - @intCast(c_int, changelist.len), - eventlist.ptr, - @intCast(c_int, eventlist.len), - timeout, - )); -} - -// TODO avoid libc dependency -pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { - return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); -} - -// TODO avoid libc dependency -pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { - return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); -} - -// TODO avoid libc dependency - -/// Takes the return value from a syscall and formats it back in the way -/// that the kernel represents it to libc. Errno was a mistake, let's make -/// it go away forever. -fn errnoWrap(value: isize) usize { - return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); -} diff --git a/std/os/path.zig b/std/os/path.zig index 290f8ba17f..b68ff32408 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const debug = std.debug; diff --git a/std/os/test.zig b/std/os/test.zig index b2a4d96651..ab21ea1568 100644 --- a/std/os/test.zig +++ b/std/os/test.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const os = std.os; const expect = std.testing.expect; const io = std.io; diff --git a/std/os/time.zig b/std/os/time.zig index bce6f265de..66ceedb1b6 100644 --- a/std/os/time.zig +++ b/std/os/time.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const Os = builtin.Os; const debug = std.debug; diff --git a/std/os/windows.zig b/std/os/windows.zig new file mode 100644 index 0000000000..96274632ce --- /dev/null +++ b/std/os/windows.zig @@ -0,0 +1,399 @@ +const std = @import("../std.zig"); +const assert = std.debug.assert; +const maxInt = std.math.maxInt; + +pub use @import("windows/advapi32.zig"); +pub use @import("windows/kernel32.zig"); +pub use @import("windows/ntdll.zig"); +pub use @import("windows/ole32.zig"); +pub use @import("windows/shell32.zig"); + +test "import" { + _ = @import("windows/util.zig"); +} + +pub const ERROR = @import("windows/error.zig"); + +pub const SHORT = c_short; +pub const BOOL = c_int; +pub const BOOLEAN = BYTE; +pub const BYTE = u8; +pub const CHAR = u8; +pub const DWORD = u32; +pub const FLOAT = f32; +pub const HANDLE = *c_void; +pub const HCRYPTPROV = ULONG_PTR; +pub const HINSTANCE = *@OpaqueType(); +pub const HMODULE = *@OpaqueType(); +pub const FARPROC = *@OpaqueType(); +pub const INT = c_int; +pub const LPBYTE = *BYTE; +pub const LPCH = *CHAR; +pub const LPCSTR = [*]const CHAR; +pub const LPCTSTR = [*]const TCHAR; +pub const LPCVOID = *const c_void; +pub const LPDWORD = *DWORD; +pub const LPSTR = [*]CHAR; +pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; +pub const LPVOID = *c_void; +pub const LPWSTR = [*]WCHAR; +pub const LPCWSTR = [*]const WCHAR; +pub const PVOID = *c_void; +pub const PWSTR = [*]WCHAR; +pub const SIZE_T = usize; +pub const TCHAR = if (UNICODE) WCHAR else u8; +pub const UINT = c_uint; +pub const ULONG_PTR = usize; +pub const DWORD_PTR = ULONG_PTR; +pub const UNICODE = false; +pub const WCHAR = u16; +pub const WORD = u16; +pub const LARGE_INTEGER = i64; +pub const ULONG = u32; +pub const LONG = i32; +pub const ULONGLONG = u64; +pub const LONGLONG = i64; + +pub const TRUE = 1; +pub const FALSE = 0; + +/// The standard input device. Initially, this is the console input buffer, CONIN$. +pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; + +/// The standard output device. Initially, this is the active console screen buffer, CONOUT$. +pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; + +/// The standard error device. Initially, this is the active console screen buffer, CONOUT$. +pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; + +pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); + +pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); + +pub const OVERLAPPED = extern struct { + Internal: ULONG_PTR, + InternalHigh: ULONG_PTR, + Offset: DWORD, + OffsetHigh: DWORD, + hEvent: ?HANDLE, +}; +pub const LPOVERLAPPED = *OVERLAPPED; + +pub const MAX_PATH = 260; + +// TODO issue #305 +pub const FILE_INFO_BY_HANDLE_CLASS = u32; +pub const FileBasicInfo = 0; +pub const FileStandardInfo = 1; +pub const FileNameInfo = 2; +pub const FileRenameInfo = 3; +pub const FileDispositionInfo = 4; +pub const FileAllocationInfo = 5; +pub const FileEndOfFileInfo = 6; +pub const FileStreamInfo = 7; +pub const FileCompressionInfo = 8; +pub const FileAttributeTagInfo = 9; +pub const FileIdBothDirectoryInfo = 10; +pub const FileIdBothDirectoryRestartInfo = 11; +pub const FileIoPriorityHintInfo = 12; +pub const FileRemoteProtocolInfo = 13; +pub const FileFullDirectoryInfo = 14; +pub const FileFullDirectoryRestartInfo = 15; +pub const FileStorageInfo = 16; +pub const FileAlignmentInfo = 17; +pub const FileIdInfo = 18; +pub const FileIdExtdDirectoryInfo = 19; +pub const FileIdExtdDirectoryRestartInfo = 20; + +pub const FILE_NAME_INFO = extern struct { + FileNameLength: DWORD, + FileName: [1]WCHAR, +}; + +/// Return the normalized drive name. This is the default. +pub const FILE_NAME_NORMALIZED = 0x0; + +/// Return the opened file name (not normalized). +pub const FILE_NAME_OPENED = 0x8; + +/// Return the path with the drive letter. This is the default. +pub const VOLUME_NAME_DOS = 0x0; + +/// Return the path with a volume GUID path instead of the drive name. +pub const VOLUME_NAME_GUID = 0x1; + +/// Return the path with no drive information. +pub const VOLUME_NAME_NONE = 0x4; + +/// Return the path with the volume device path. +pub const VOLUME_NAME_NT = 0x2; + +pub const SECURITY_ATTRIBUTES = extern struct { + nLength: DWORD, + lpSecurityDescriptor: ?*c_void, + bInheritHandle: BOOL, +}; +pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; +pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; + +pub const GENERIC_READ = 0x80000000; +pub const GENERIC_WRITE = 0x40000000; +pub const GENERIC_EXECUTE = 0x20000000; +pub const GENERIC_ALL = 0x10000000; + +pub const FILE_SHARE_DELETE = 0x00000004; +pub const FILE_SHARE_READ = 0x00000001; +pub const FILE_SHARE_WRITE = 0x00000002; + +pub const CREATE_ALWAYS = 2; +pub const CREATE_NEW = 1; +pub const OPEN_ALWAYS = 4; +pub const OPEN_EXISTING = 3; +pub const TRUNCATE_EXISTING = 5; + +pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; +pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; +pub const FILE_ATTRIBUTE_DEVICE = 0x40; +pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; +pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; +pub const FILE_ATTRIBUTE_HIDDEN = 0x2; +pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; +pub const FILE_ATTRIBUTE_NORMAL = 0x80; +pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; +pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; +pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; +pub const FILE_ATTRIBUTE_READONLY = 0x1; +pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; +pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; +pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; +pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; +pub const FILE_ATTRIBUTE_SYSTEM = 0x4; +pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; +pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; + +pub const PROCESS_INFORMATION = extern struct { + hProcess: HANDLE, + hThread: HANDLE, + dwProcessId: DWORD, + dwThreadId: DWORD, +}; + +pub const STARTUPINFOW = extern struct { + cb: DWORD, + lpReserved: ?LPWSTR, + lpDesktop: ?LPWSTR, + lpTitle: ?LPWSTR, + dwX: DWORD, + dwY: DWORD, + dwXSize: DWORD, + dwYSize: DWORD, + dwXCountChars: DWORD, + dwYCountChars: DWORD, + dwFillAttribute: DWORD, + dwFlags: DWORD, + wShowWindow: WORD, + cbReserved2: WORD, + lpReserved2: ?LPBYTE, + hStdInput: ?HANDLE, + hStdOutput: ?HANDLE, + hStdError: ?HANDLE, +}; + +pub const STARTF_FORCEONFEEDBACK = 0x00000040; +pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; +pub const STARTF_PREVENTPINNING = 0x00002000; +pub const STARTF_RUNFULLSCREEN = 0x00000020; +pub const STARTF_TITLEISAPPID = 0x00001000; +pub const STARTF_TITLEISLINKNAME = 0x00000800; +pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; +pub const STARTF_USECOUNTCHARS = 0x00000008; +pub const STARTF_USEFILLATTRIBUTE = 0x00000010; +pub const STARTF_USEHOTKEY = 0x00000200; +pub const STARTF_USEPOSITION = 0x00000004; +pub const STARTF_USESHOWWINDOW = 0x00000001; +pub const STARTF_USESIZE = 0x00000002; +pub const STARTF_USESTDHANDLES = 0x00000100; + +pub const INFINITE = 4294967295; + +pub const WAIT_ABANDONED = 0x00000080; +pub const WAIT_OBJECT_0 = 0x00000000; +pub const WAIT_TIMEOUT = 0x00000102; +pub const WAIT_FAILED = 0xFFFFFFFF; + +pub const HANDLE_FLAG_INHERIT = 0x00000001; +pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; + +pub const MOVEFILE_COPY_ALLOWED = 2; +pub const MOVEFILE_CREATE_HARDLINK = 16; +pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; +pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; +pub const MOVEFILE_REPLACE_EXISTING = 1; +pub const MOVEFILE_WRITE_THROUGH = 8; + +pub const FILE_BEGIN = 0; +pub const FILE_CURRENT = 1; +pub const FILE_END = 2; + +pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; +pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; +pub const HEAP_NO_SERIALIZE = 0x00000001; + +pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; +pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; + +pub const WIN32_FIND_DATAW = extern struct { + dwFileAttributes: DWORD, + ftCreationTime: FILETIME, + ftLastAccessTime: FILETIME, + ftLastWriteTime: FILETIME, + nFileSizeHigh: DWORD, + nFileSizeLow: DWORD, + dwReserved0: DWORD, + dwReserved1: DWORD, + cFileName: [260]u16, + cAlternateFileName: [14]u16, +}; + +pub const FILETIME = extern struct { + dwLowDateTime: DWORD, + dwHighDateTime: DWORD, +}; + +pub const SYSTEM_INFO = extern struct { + anon1: extern union { + dwOemId: DWORD, + anon2: extern struct { + wProcessorArchitecture: WORD, + wReserved: WORD, + }, + }, + dwPageSize: DWORD, + lpMinimumApplicationAddress: LPVOID, + lpMaximumApplicationAddress: LPVOID, + dwActiveProcessorMask: DWORD_PTR, + dwNumberOfProcessors: DWORD, + dwProcessorType: DWORD, + dwAllocationGranularity: DWORD, + wProcessorLevel: WORD, + wProcessorRevision: WORD, +}; + +pub const HRESULT = c_long; + +pub const KNOWNFOLDERID = GUID; +pub const GUID = extern struct { + Data1: c_ulong, + Data2: c_ushort, + Data3: c_ushort, + Data4: [8]u8, + + pub fn parse(str: []const u8) GUID { + var guid: GUID = undefined; + var index: usize = 0; + assert(str[index] == '{'); + index += 1; + + guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; + index += 8; + + assert(str[index] == '-'); + index += 1; + + guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; + index += 4; + + assert(str[index] == '-'); + index += 1; + + guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; + index += 4; + + assert(str[index] == '-'); + index += 1; + + guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; + index += 2; + guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; + index += 2; + + assert(str[index] == '-'); + index += 1; + + var i: usize = 2; + while (i < guid.Data4.len) : (i += 1) { + guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; + index += 2; + } + + assert(str[index] == '}'); + index += 1; + return guid; + } +}; + +pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); + +pub const KF_FLAG_DEFAULT = 0; +pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; +pub const KF_FLAG_CREATE = 32768; +pub const KF_FLAG_DONT_VERIFY = 16384; +pub const KF_FLAG_DONT_UNEXPAND = 8192; +pub const KF_FLAG_NO_ALIAS = 4096; +pub const KF_FLAG_INIT = 2048; +pub const KF_FLAG_DEFAULT_PATH = 1024; +pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; +pub const KF_FLAG_SIMPLE_IDLIST = 256; +pub const KF_FLAG_ALIAS_ONLY = -2147483648; + +pub const S_OK = 0; +pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); +pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); +pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); +pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); +pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); +pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); +pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); +pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); +pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); +pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); + +pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; +pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; +pub const FILE_FLAG_NO_BUFFERING = 0x20000000; +pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; +pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; +pub const FILE_FLAG_OVERLAPPED = 0x40000000; +pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; +pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; +pub const FILE_FLAG_SESSION_AWARE = 0x00800000; +pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; +pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; + +pub const SMALL_RECT = extern struct { + Left: SHORT, + Top: SHORT, + Right: SHORT, + Bottom: SHORT, +}; + +pub const COORD = extern struct { + X: SHORT, + Y: SHORT, +}; + +pub const CREATE_UNICODE_ENVIRONMENT = 1024; + +pub const TLS_OUT_OF_INDEXES = 4294967295; +pub const IMAGE_TLS_DIRECTORY = extern struct { + StartAddressOfRawData: usize, + EndAddressOfRawData: usize, + AddressOfIndex: usize, + AddressOfCallBacks: usize, + SizeOfZeroFill: u32, + Characteristics: u32, +}; +pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; +pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; + +pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void; diff --git a/std/os/windows/advapi32.zig b/std/os/windows/advapi32.zig index 185c1424c6..9672767500 100644 --- a/std/os/windows/advapi32.zig +++ b/std/os/windows/advapi32.zig @@ -1,4 +1,4 @@ -use @import("index.zig"); +use @import("../windows.zig"); pub const PROV_RSA_FULL = 1; diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig deleted file mode 100644 index 8e9ed8b8db..0000000000 --- a/std/os/windows/index.zig +++ /dev/null @@ -1,399 +0,0 @@ -const std = @import("../../index.zig"); -const assert = std.debug.assert; -const maxInt = std.math.maxInt; - -pub use @import("advapi32.zig"); -pub use @import("kernel32.zig"); -pub use @import("ntdll.zig"); -pub use @import("ole32.zig"); -pub use @import("shell32.zig"); - -test "import" { - _ = @import("util.zig"); -} - -pub const ERROR = @import("error.zig"); - -pub const SHORT = c_short; -pub const BOOL = c_int; -pub const BOOLEAN = BYTE; -pub const BYTE = u8; -pub const CHAR = u8; -pub const DWORD = u32; -pub const FLOAT = f32; -pub const HANDLE = *c_void; -pub const HCRYPTPROV = ULONG_PTR; -pub const HINSTANCE = *@OpaqueType(); -pub const HMODULE = *@OpaqueType(); -pub const FARPROC = *@OpaqueType(); -pub const INT = c_int; -pub const LPBYTE = *BYTE; -pub const LPCH = *CHAR; -pub const LPCSTR = [*]const CHAR; -pub const LPCTSTR = [*]const TCHAR; -pub const LPCVOID = *const c_void; -pub const LPDWORD = *DWORD; -pub const LPSTR = [*]CHAR; -pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; -pub const LPVOID = *c_void; -pub const LPWSTR = [*]WCHAR; -pub const LPCWSTR = [*]const WCHAR; -pub const PVOID = *c_void; -pub const PWSTR = [*]WCHAR; -pub const SIZE_T = usize; -pub const TCHAR = if (UNICODE) WCHAR else u8; -pub const UINT = c_uint; -pub const ULONG_PTR = usize; -pub const DWORD_PTR = ULONG_PTR; -pub const UNICODE = false; -pub const WCHAR = u16; -pub const WORD = u16; -pub const LARGE_INTEGER = i64; -pub const ULONG = u32; -pub const LONG = i32; -pub const ULONGLONG = u64; -pub const LONGLONG = i64; - -pub const TRUE = 1; -pub const FALSE = 0; - -/// The standard input device. Initially, this is the console input buffer, CONIN$. -pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; - -/// The standard output device. Initially, this is the active console screen buffer, CONOUT$. -pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; - -/// The standard error device. Initially, this is the active console screen buffer, CONOUT$. -pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; - -pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); - -pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); - -pub const OVERLAPPED = extern struct { - Internal: ULONG_PTR, - InternalHigh: ULONG_PTR, - Offset: DWORD, - OffsetHigh: DWORD, - hEvent: ?HANDLE, -}; -pub const LPOVERLAPPED = *OVERLAPPED; - -pub const MAX_PATH = 260; - -// TODO issue #305 -pub const FILE_INFO_BY_HANDLE_CLASS = u32; -pub const FileBasicInfo = 0; -pub const FileStandardInfo = 1; -pub const FileNameInfo = 2; -pub const FileRenameInfo = 3; -pub const FileDispositionInfo = 4; -pub const FileAllocationInfo = 5; -pub const FileEndOfFileInfo = 6; -pub const FileStreamInfo = 7; -pub const FileCompressionInfo = 8; -pub const FileAttributeTagInfo = 9; -pub const FileIdBothDirectoryInfo = 10; -pub const FileIdBothDirectoryRestartInfo = 11; -pub const FileIoPriorityHintInfo = 12; -pub const FileRemoteProtocolInfo = 13; -pub const FileFullDirectoryInfo = 14; -pub const FileFullDirectoryRestartInfo = 15; -pub const FileStorageInfo = 16; -pub const FileAlignmentInfo = 17; -pub const FileIdInfo = 18; -pub const FileIdExtdDirectoryInfo = 19; -pub const FileIdExtdDirectoryRestartInfo = 20; - -pub const FILE_NAME_INFO = extern struct { - FileNameLength: DWORD, - FileName: [1]WCHAR, -}; - -/// Return the normalized drive name. This is the default. -pub const FILE_NAME_NORMALIZED = 0x0; - -/// Return the opened file name (not normalized). -pub const FILE_NAME_OPENED = 0x8; - -/// Return the path with the drive letter. This is the default. -pub const VOLUME_NAME_DOS = 0x0; - -/// Return the path with a volume GUID path instead of the drive name. -pub const VOLUME_NAME_GUID = 0x1; - -/// Return the path with no drive information. -pub const VOLUME_NAME_NONE = 0x4; - -/// Return the path with the volume device path. -pub const VOLUME_NAME_NT = 0x2; - -pub const SECURITY_ATTRIBUTES = extern struct { - nLength: DWORD, - lpSecurityDescriptor: ?*c_void, - bInheritHandle: BOOL, -}; -pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; -pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; - -pub const GENERIC_READ = 0x80000000; -pub const GENERIC_WRITE = 0x40000000; -pub const GENERIC_EXECUTE = 0x20000000; -pub const GENERIC_ALL = 0x10000000; - -pub const FILE_SHARE_DELETE = 0x00000004; -pub const FILE_SHARE_READ = 0x00000001; -pub const FILE_SHARE_WRITE = 0x00000002; - -pub const CREATE_ALWAYS = 2; -pub const CREATE_NEW = 1; -pub const OPEN_ALWAYS = 4; -pub const OPEN_EXISTING = 3; -pub const TRUNCATE_EXISTING = 5; - -pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; -pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; -pub const FILE_ATTRIBUTE_DEVICE = 0x40; -pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; -pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; -pub const FILE_ATTRIBUTE_HIDDEN = 0x2; -pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; -pub const FILE_ATTRIBUTE_NORMAL = 0x80; -pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; -pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; -pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; -pub const FILE_ATTRIBUTE_READONLY = 0x1; -pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; -pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; -pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; -pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; -pub const FILE_ATTRIBUTE_SYSTEM = 0x4; -pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; -pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; - -pub const PROCESS_INFORMATION = extern struct { - hProcess: HANDLE, - hThread: HANDLE, - dwProcessId: DWORD, - dwThreadId: DWORD, -}; - -pub const STARTUPINFOW = extern struct { - cb: DWORD, - lpReserved: ?LPWSTR, - lpDesktop: ?LPWSTR, - lpTitle: ?LPWSTR, - dwX: DWORD, - dwY: DWORD, - dwXSize: DWORD, - dwYSize: DWORD, - dwXCountChars: DWORD, - dwYCountChars: DWORD, - dwFillAttribute: DWORD, - dwFlags: DWORD, - wShowWindow: WORD, - cbReserved2: WORD, - lpReserved2: ?LPBYTE, - hStdInput: ?HANDLE, - hStdOutput: ?HANDLE, - hStdError: ?HANDLE, -}; - -pub const STARTF_FORCEONFEEDBACK = 0x00000040; -pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; -pub const STARTF_PREVENTPINNING = 0x00002000; -pub const STARTF_RUNFULLSCREEN = 0x00000020; -pub const STARTF_TITLEISAPPID = 0x00001000; -pub const STARTF_TITLEISLINKNAME = 0x00000800; -pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; -pub const STARTF_USECOUNTCHARS = 0x00000008; -pub const STARTF_USEFILLATTRIBUTE = 0x00000010; -pub const STARTF_USEHOTKEY = 0x00000200; -pub const STARTF_USEPOSITION = 0x00000004; -pub const STARTF_USESHOWWINDOW = 0x00000001; -pub const STARTF_USESIZE = 0x00000002; -pub const STARTF_USESTDHANDLES = 0x00000100; - -pub const INFINITE = 4294967295; - -pub const WAIT_ABANDONED = 0x00000080; -pub const WAIT_OBJECT_0 = 0x00000000; -pub const WAIT_TIMEOUT = 0x00000102; -pub const WAIT_FAILED = 0xFFFFFFFF; - -pub const HANDLE_FLAG_INHERIT = 0x00000001; -pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; - -pub const MOVEFILE_COPY_ALLOWED = 2; -pub const MOVEFILE_CREATE_HARDLINK = 16; -pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; -pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; -pub const MOVEFILE_REPLACE_EXISTING = 1; -pub const MOVEFILE_WRITE_THROUGH = 8; - -pub const FILE_BEGIN = 0; -pub const FILE_CURRENT = 1; -pub const FILE_END = 2; - -pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; -pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; -pub const HEAP_NO_SERIALIZE = 0x00000001; - -pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; -pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; - -pub const WIN32_FIND_DATAW = extern struct { - dwFileAttributes: DWORD, - ftCreationTime: FILETIME, - ftLastAccessTime: FILETIME, - ftLastWriteTime: FILETIME, - nFileSizeHigh: DWORD, - nFileSizeLow: DWORD, - dwReserved0: DWORD, - dwReserved1: DWORD, - cFileName: [260]u16, - cAlternateFileName: [14]u16, -}; - -pub const FILETIME = extern struct { - dwLowDateTime: DWORD, - dwHighDateTime: DWORD, -}; - -pub const SYSTEM_INFO = extern struct { - anon1: extern union { - dwOemId: DWORD, - anon2: extern struct { - wProcessorArchitecture: WORD, - wReserved: WORD, - }, - }, - dwPageSize: DWORD, - lpMinimumApplicationAddress: LPVOID, - lpMaximumApplicationAddress: LPVOID, - dwActiveProcessorMask: DWORD_PTR, - dwNumberOfProcessors: DWORD, - dwProcessorType: DWORD, - dwAllocationGranularity: DWORD, - wProcessorLevel: WORD, - wProcessorRevision: WORD, -}; - -pub const HRESULT = c_long; - -pub const KNOWNFOLDERID = GUID; -pub const GUID = extern struct { - Data1: c_ulong, - Data2: c_ushort, - Data3: c_ushort, - Data4: [8]u8, - - pub fn parse(str: []const u8) GUID { - var guid: GUID = undefined; - var index: usize = 0; - assert(str[index] == '{'); - index += 1; - - guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; - index += 8; - - assert(str[index] == '-'); - index += 1; - - guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; - index += 4; - - assert(str[index] == '-'); - index += 1; - - guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; - index += 4; - - assert(str[index] == '-'); - index += 1; - - guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; - index += 2; - guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; - index += 2; - - assert(str[index] == '-'); - index += 1; - - var i: usize = 2; - while (i < guid.Data4.len) : (i += 1) { - guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; - index += 2; - } - - assert(str[index] == '}'); - index += 1; - return guid; - } -}; - -pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); - -pub const KF_FLAG_DEFAULT = 0; -pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; -pub const KF_FLAG_CREATE = 32768; -pub const KF_FLAG_DONT_VERIFY = 16384; -pub const KF_FLAG_DONT_UNEXPAND = 8192; -pub const KF_FLAG_NO_ALIAS = 4096; -pub const KF_FLAG_INIT = 2048; -pub const KF_FLAG_DEFAULT_PATH = 1024; -pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; -pub const KF_FLAG_SIMPLE_IDLIST = 256; -pub const KF_FLAG_ALIAS_ONLY = -2147483648; - -pub const S_OK = 0; -pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); -pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); -pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); -pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); -pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); -pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); -pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); -pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); -pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); -pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); - -pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; -pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; -pub const FILE_FLAG_NO_BUFFERING = 0x20000000; -pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; -pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; -pub const FILE_FLAG_OVERLAPPED = 0x40000000; -pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; -pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; -pub const FILE_FLAG_SESSION_AWARE = 0x00800000; -pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; -pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; - -pub const SMALL_RECT = extern struct { - Left: SHORT, - Top: SHORT, - Right: SHORT, - Bottom: SHORT, -}; - -pub const COORD = extern struct { - X: SHORT, - Y: SHORT, -}; - -pub const CREATE_UNICODE_ENVIRONMENT = 1024; - -pub const TLS_OUT_OF_INDEXES = 4294967295; -pub const IMAGE_TLS_DIRECTORY = extern struct { - StartAddressOfRawData: usize, - EndAddressOfRawData: usize, - AddressOfIndex: usize, - AddressOfCallBacks: usize, - SizeOfZeroFill: u32, - Characteristics: u32, -}; -pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; -pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; - -pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void; diff --git a/std/os/windows/kernel32.zig b/std/os/windows/kernel32.zig index ce8443de02..64a97ca87d 100644 --- a/std/os/windows/kernel32.zig +++ b/std/os/windows/kernel32.zig @@ -1,4 +1,4 @@ -use @import("index.zig"); +use @import("../windows.zig"); pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; @@ -262,12 +262,10 @@ pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; -pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; +pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; pub const RTL_RUN_ONCE = extern struct { Ptr: ?*c_void, }; -pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { - .Ptr = null, -}; +pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; diff --git a/std/os/windows/ntdll.zig b/std/os/windows/ntdll.zig index acb78a59f4..1b63c1e7ec 100644 --- a/std/os/windows/ntdll.zig +++ b/std/os/windows/ntdll.zig @@ -1,3 +1,3 @@ -use @import("index.zig"); +use @import("../windows.zig"); pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD; diff --git a/std/os/windows/ole32.zig b/std/os/windows/ole32.zig index 8f34b45bbf..1ecfecba3c 100644 --- a/std/os/windows/ole32.zig +++ b/std/os/windows/ole32.zig @@ -1,4 +1,4 @@ -use @import("index.zig"); +use @import("../windows.zig"); pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; pub extern "ole32" stdcallcc fn CoUninitialize() void; diff --git a/std/os/windows/shell32.zig b/std/os/windows/shell32.zig index 139babc7a8..c299e01f17 100644 --- a/std/os/windows/shell32.zig +++ b/std/os/windows/shell32.zig @@ -1,4 +1,4 @@ -use @import("index.zig"); +use @import("../windows.zig"); pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig index 849c15a6f5..0952343051 100644 --- a/std/os/windows/util.zig +++ b/std/os/windows/util.zig @@ -1,4 +1,4 @@ -const std = @import("../../index.zig"); +const std = @import("../../std.zig"); const builtin = @import("builtin"); const os = std.os; const unicode = std.unicode; diff --git a/std/os/zen.zig b/std/os/zen.zig index 76c4df9d62..0564956b24 100644 --- a/std/os/zen.zig +++ b/std/os/zen.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; ////////////////////////// diff --git a/std/pdb.zig b/std/pdb.zig index 663f81e0c2..2b02a84871 100644 --- a/std/pdb.zig +++ b/std/pdb.zig @@ -1,5 +1,5 @@ const builtin = @import("builtin"); -const std = @import("index.zig"); +const std = @import("std.zig"); const io = std.io; const math = std.math; const mem = std.mem; diff --git a/std/priority_queue.zig b/std/priority_queue.zig index 33b5a963c6..d2c493af3e 100644 --- a/std/priority_queue.zig +++ b/std/priority_queue.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const Allocator = std.mem.Allocator; const debug = std.debug; const expect = std.testing.expect; diff --git a/std/rand.zig b/std/rand.zig new file mode 100644 index 0000000000..a2fdfed6fd --- /dev/null +++ b/std/rand.zig @@ -0,0 +1,1005 @@ +// The engines provided here should be initialized from an external source. For now, getRandomBytes +// from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using +// a normal PRNG will be faster and use substantially less stack space. +// +// ``` +// var buf: [8]u8 = undefined; +// try std.os.getRandomBytes(buf[0..]); +// const seed = mem.readIntSliceLittle(u64, buf[0..8]); +// +// var r = DefaultPrng.init(seed); +// +// const s = r.random.int(u64); +// ``` +// +// TODO(tiehuis): Benchmark these against other reference implementations. + +const std = @import("std.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const expect = std.testing.expect; +const mem = std.mem; +const math = std.math; +const ziggurat = @import("rand/ziggurat.zig"); +const maxInt = std.math.maxInt; + +// When you need fast unbiased random numbers +pub const DefaultPrng = Xoroshiro128; + +// When you need cryptographically secure random numbers +pub const DefaultCsprng = Isaac64; + +pub const Random = struct { + fillFn: fn (r: *Random, buf: []u8) void, + + /// Read random bytes into the specified buffer until full. + pub fn bytes(r: *Random, buf: []u8) void { + r.fillFn(r, buf); + } + + pub fn boolean(r: *Random) bool { + return r.int(u1) != 0; + } + + /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. + /// `i` is evenly distributed. + pub fn int(r: *Random, comptime T: type) T { + const UnsignedT = @IntType(false, T.bit_count); + const ByteAlignedT = @IntType(false, @divTrunc(T.bit_count + 7, 8) * 8); + + var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined; + r.bytes(rand_bytes[0..]); + + // use LE instead of native endian for better portability maybe? + // TODO: endian portability is pointless if the underlying prng isn't endian portable. + // TODO: document the endian portability of this library. + const byte_aligned_result = mem.readIntSliceLittle(ByteAlignedT, rand_bytes); + const unsigned_result = @truncate(UnsignedT, byte_aligned_result); + return @bitCast(T, unsigned_result); + } + + /// Constant-time implementation off ::uintLessThan. + /// The results of this function may be biased. + pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T { + comptime assert(T.is_signed == false); + comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! + assert(0 < less_than); + if (T.bit_count <= 32) { + return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than)); + } else { + return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than)); + } + } + + /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. + /// This function assumes that the underlying ::fillFn produces evenly distributed values. + /// Within this assumption, the runtime of this function is exponentially distributed. + /// If ::fillFn were backed by a true random generator, + /// the runtime of this function would technically be unbounded. + /// However, if ::fillFn is backed by any evenly distributed pseudo random number generator, + /// this function is guaranteed to return. + /// If you need deterministic runtime bounds, use `::uintLessThanBiased`. + pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { + comptime assert(T.is_signed == false); + comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! + assert(0 < less_than); + // Small is typically u32 + const Small = @IntType(false, @divTrunc(T.bit_count + 31, 32) * 32); + // Large is typically u64 + const Large = @IntType(false, Small.bit_count * 2); + + // adapted from: + // http://www.pcg-random.org/posts/bounded-rands.html + // "Lemire's (with an extra tweak from me)" + var x: Small = r.int(Small); + var m: Large = Large(x) * Large(less_than); + var l: Small = @truncate(Small, m); + if (l < less_than) { + // TODO: workaround for https://github.com/ziglang/zig/issues/1770 + // should be: + // var t: Small = -%less_than; + var t: Small = @bitCast(Small, -%@bitCast(@IntType(true, Small.bit_count), Small(less_than))); + + if (t >= less_than) { + t -= less_than; + if (t >= less_than) { + t %= less_than; + } + } + while (l < t) { + x = r.int(Small); + m = Large(x) * Large(less_than); + l = @truncate(Small, m); + } + } + return @intCast(T, m >> Small.bit_count); + } + + /// Constant-time implementation off ::uintAtMost. + /// The results of this function may be biased. + pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T { + assert(T.is_signed == false); + if (at_most == maxInt(T)) { + // have the full range + return r.int(T); + } + return r.uintLessThanBiased(T, at_most + 1); + } + + /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. + /// See ::uintLessThan, which this function uses in most cases, + /// for commentary on the runtime of this function. + pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T { + assert(T.is_signed == false); + if (at_most == maxInt(T)) { + // have the full range + return r.int(T); + } + return r.uintLessThan(T, at_most + 1); + } + + /// Constant-time implementation off ::intRangeLessThan. + /// The results of this function may be biased. + pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T { + assert(at_least < less_than); + if (T.is_signed) { + // Two's complement makes this math pretty easy. + const UnsignedT = @IntType(false, T.bit_count); + const lo = @bitCast(UnsignedT, at_least); + const hi = @bitCast(UnsignedT, less_than); + const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); + return @bitCast(T, result); + } else { + // The signed implementation would work fine, but we can use stricter arithmetic operators here. + return at_least + r.uintLessThanBiased(T, less_than - at_least); + } + } + + /// Returns an evenly distributed random integer `at_least <= i < less_than`. + /// See ::uintLessThan, which this function uses in most cases, + /// for commentary on the runtime of this function. + pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T { + assert(at_least < less_than); + if (T.is_signed) { + // Two's complement makes this math pretty easy. + const UnsignedT = @IntType(false, T.bit_count); + const lo = @bitCast(UnsignedT, at_least); + const hi = @bitCast(UnsignedT, less_than); + const result = lo +% r.uintLessThan(UnsignedT, hi -% lo); + return @bitCast(T, result); + } else { + // The signed implementation would work fine, but we can use stricter arithmetic operators here. + return at_least + r.uintLessThan(T, less_than - at_least); + } + } + + /// Constant-time implementation off ::intRangeAtMostBiased. + /// The results of this function may be biased. + pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T { + assert(at_least <= at_most); + if (T.is_signed) { + // Two's complement makes this math pretty easy. + const UnsignedT = @IntType(false, T.bit_count); + const lo = @bitCast(UnsignedT, at_least); + const hi = @bitCast(UnsignedT, at_most); + const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); + return @bitCast(T, result); + } else { + // The signed implementation would work fine, but we can use stricter arithmetic operators here. + return at_least + r.uintAtMostBiased(T, at_most - at_least); + } + } + + /// Returns an evenly distributed random integer `at_least <= i <= at_most`. + /// See ::uintLessThan, which this function uses in most cases, + /// for commentary on the runtime of this function. + pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T { + assert(at_least <= at_most); + if (T.is_signed) { + // Two's complement makes this math pretty easy. + const UnsignedT = @IntType(false, T.bit_count); + const lo = @bitCast(UnsignedT, at_least); + const hi = @bitCast(UnsignedT, at_most); + const result = lo +% r.uintAtMost(UnsignedT, hi -% lo); + return @bitCast(T, result); + } else { + // The signed implementation would work fine, but we can use stricter arithmetic operators here. + return at_least + r.uintAtMost(T, at_most - at_least); + } + } + + /// TODO: deprecated. use ::boolean or ::int instead. + pub fn scalar(r: *Random, comptime T: type) T { + return if (T == bool) r.boolean() else r.int(T); + } + + /// TODO: deprecated. renamed to ::intRangeLessThan + pub fn range(r: *Random, comptime T: type, start: T, end: T) T { + return r.intRangeLessThan(T, start, end); + } + + /// Return a floating point value evenly distributed in the range [0, 1). + pub fn float(r: *Random, comptime T: type) T { + // Generate a uniform value between [1, 2) and scale down to [0, 1). + // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. + switch (T) { + f32 => { + const s = r.int(u32); + const repr = (0x7f << 23) | (s >> 9); + return @bitCast(f32, repr) - 1.0; + }, + f64 => { + const s = r.int(u64); + const repr = (0x3ff << 52) | (s >> 12); + return @bitCast(f64, repr) - 1.0; + }, + else => @compileError("unknown floating point type"), + } + } + + /// Return a floating point value normally distributed with mean = 0, stddev = 1. + /// + /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. + pub fn floatNorm(r: *Random, comptime T: type) T { + const value = ziggurat.next_f64(r, ziggurat.NormDist); + switch (T) { + f32 => return @floatCast(f32, value), + f64 => return value, + else => @compileError("unknown floating point type"), + } + } + + /// Return an exponentially distributed float with a rate parameter of 1. + /// + /// To use a different rate parameter, use: floatExp(...) / desiredRate. + pub fn floatExp(r: *Random, comptime T: type) T { + const value = ziggurat.next_f64(r, ziggurat.ExpDist); + switch (T) { + f32 => return @floatCast(f32, value), + f64 => return value, + else => @compileError("unknown floating point type"), + } + } + + /// Shuffle a slice into a random order. + pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { + if (buf.len < 2) { + return; + } + + var i: usize = 0; + while (i < buf.len - 1) : (i += 1) { + const j = r.intRangeLessThan(usize, i, buf.len); + mem.swap(T, &buf[i], &buf[j]); + } + } +}; + +/// Convert a random integer 0 <= random_int <= maxValue(T), +/// into an integer 0 <= result < less_than. +/// This function introduces a minor bias. +pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { + comptime assert(T.is_signed == false); + const T2 = @IntType(false, T.bit_count * 2); + + // adapted from: + // http://www.pcg-random.org/posts/bounded-rands.html + // "Integer Multiplication (Biased)" + var m: T2 = T2(random_int) * T2(less_than); + return @intCast(T, m >> T.bit_count); +} + +const SequentialPrng = struct { + const Self = @This(); + random: Random, + next_value: u8, + + pub fn init() Self { + return Self{ + .random = Random{ .fillFn = fill }, + .next_value = 0, + }; + } + + fn fill(r: *Random, buf: []u8) void { + const self = @fieldParentPtr(Self, "random", r); + for (buf) |*b| { + b.* = self.next_value; + } + self.next_value +%= 1; + } +}; + +test "Random int" { + testRandomInt(); + comptime testRandomInt(); +} +fn testRandomInt() void { + var r = SequentialPrng.init(); + + expect(r.random.int(u0) == 0); + + r.next_value = 0; + expect(r.random.int(u1) == 0); + expect(r.random.int(u1) == 1); + expect(r.random.int(u2) == 2); + expect(r.random.int(u2) == 3); + expect(r.random.int(u2) == 0); + + r.next_value = 0xff; + expect(r.random.int(u8) == 0xff); + r.next_value = 0x11; + expect(r.random.int(u8) == 0x11); + + r.next_value = 0xff; + expect(r.random.int(u32) == 0xffffffff); + r.next_value = 0x11; + expect(r.random.int(u32) == 0x11111111); + + r.next_value = 0xff; + expect(r.random.int(i32) == -1); + r.next_value = 0x11; + expect(r.random.int(i32) == 0x11111111); + + r.next_value = 0xff; + expect(r.random.int(i8) == -1); + r.next_value = 0x11; + expect(r.random.int(i8) == 0x11); + + r.next_value = 0xff; + expect(r.random.int(u33) == 0x1ffffffff); + r.next_value = 0xff; + expect(r.random.int(i1) == -1); + r.next_value = 0xff; + expect(r.random.int(i2) == -1); + r.next_value = 0xff; + expect(r.random.int(i33) == -1); +} + +test "Random boolean" { + testRandomBoolean(); + comptime testRandomBoolean(); +} +fn testRandomBoolean() void { + var r = SequentialPrng.init(); + expect(r.random.boolean() == false); + expect(r.random.boolean() == true); + expect(r.random.boolean() == false); + expect(r.random.boolean() == true); +} + +test "Random intLessThan" { + @setEvalBranchQuota(10000); + testRandomIntLessThan(); + comptime testRandomIntLessThan(); +} +fn testRandomIntLessThan() void { + var r = SequentialPrng.init(); + r.next_value = 0xff; + expect(r.random.uintLessThan(u8, 4) == 3); + expect(r.next_value == 0); + expect(r.random.uintLessThan(u8, 4) == 0); + expect(r.next_value == 1); + + r.next_value = 0; + expect(r.random.uintLessThan(u64, 32) == 0); + + // trigger the bias rejection code path + r.next_value = 0; + expect(r.random.uintLessThan(u8, 3) == 0); + // verify we incremented twice + expect(r.next_value == 2); + + r.next_value = 0xff; + expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); + r.next_value = 0xff; + expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); + + r.next_value = 0xff; + expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); + r.next_value = 0xff; + expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); + r.next_value = 0xff; + expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1); + + r.next_value = 0xff; + expect(r.random.intRangeLessThan(i3, -4, 0) == -1); + r.next_value = 0xff; + expect(r.random.intRangeLessThan(i3, -2, 2) == 1); +} + +test "Random intAtMost" { + @setEvalBranchQuota(10000); + testRandomIntAtMost(); + comptime testRandomIntAtMost(); +} +fn testRandomIntAtMost() void { + var r = SequentialPrng.init(); + r.next_value = 0xff; + expect(r.random.uintAtMost(u8, 3) == 3); + expect(r.next_value == 0); + expect(r.random.uintAtMost(u8, 3) == 0); + + // trigger the bias rejection code path + r.next_value = 0; + expect(r.random.uintAtMost(u8, 2) == 0); + // verify we incremented twice + expect(r.next_value == 2); + + r.next_value = 0xff; + expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); + r.next_value = 0xff; + expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); + + r.next_value = 0xff; + expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); + r.next_value = 0xff; + expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); + r.next_value = 0xff; + expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1); + + r.next_value = 0xff; + expect(r.random.intRangeAtMost(i3, -4, -1) == -1); + r.next_value = 0xff; + expect(r.random.intRangeAtMost(i3, -2, 1) == 1); + + expect(r.random.uintAtMost(u0, 0) == 0); +} + +test "Random Biased" { + var r = DefaultPrng.init(0); + // Not thoroughly checking the logic here. + // Just want to execute all the paths with different types. + + expect(r.random.uintLessThanBiased(u1, 1) == 0); + expect(r.random.uintLessThanBiased(u32, 10) < 10); + expect(r.random.uintLessThanBiased(u64, 20) < 20); + + expect(r.random.uintAtMostBiased(u0, 0) == 0); + expect(r.random.uintAtMostBiased(u1, 0) <= 0); + expect(r.random.uintAtMostBiased(u32, 10) <= 10); + expect(r.random.uintAtMostBiased(u64, 20) <= 20); + + expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); + expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); + expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); + expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); + expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); + expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); + + // uncomment for broken module error: + //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); + expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); + expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); + expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); + expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); + expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); + expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); +} + +// Generator to extend 64-bit seed values into longer sequences. +// +// The number of cycles is thus limited to 64-bits regardless of the engine, but this +// is still plenty for practical purposes. +const SplitMix64 = struct { + s: u64, + + pub fn init(seed: u64) SplitMix64 { + return SplitMix64{ .s = seed }; + } + + pub fn next(self: *SplitMix64) u64 { + self.s +%= 0x9e3779b97f4a7c15; + + var z = self.s; + z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27)) *% 0x94d049bb133111eb; + return z ^ (z >> 31); + } +}; + +test "splitmix64 sequence" { + var r = SplitMix64.init(0xaeecf86f7878dd75); + + const seq = []const u64{ + 0x5dbd39db0178eb44, + 0xa9900fb66b397da3, + 0x5c1a28b1aeebcf5c, + 0x64a963238f776912, + 0xc6d4177b21d1c0ab, + 0xb2cbdbdb5ea35394, + }; + + for (seq) |s| { + expect(s == r.next()); + } +} + +// PCG32 - http://www.pcg-random.org/ +// +// PRNG +pub const Pcg = struct { + const default_multiplier = 6364136223846793005; + + random: Random, + + s: u64, + i: u64, + + pub fn init(init_s: u64) Pcg { + var pcg = Pcg{ + .random = Random{ .fillFn = fill }, + .s = undefined, + .i = undefined, + }; + + pcg.seed(init_s); + return pcg; + } + + fn next(self: *Pcg) u32 { + const l = self.s; + self.s = l *% default_multiplier +% (self.i | 1); + + const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27); + const rot = @intCast(u32, l >> 59); + + return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31)); + } + + fn seed(self: *Pcg, init_s: u64) void { + // Pcg requires 128-bits of seed. + var gen = SplitMix64.init(init_s); + self.seedTwo(gen.next(), gen.next()); + } + + fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { + self.s = 0; + self.i = (init_s << 1) | 1; + self.s = self.s *% default_multiplier +% self.i; + self.s +%= init_i; + self.s = self.s *% default_multiplier +% self.i; + } + + fn fill(r: *Random, buf: []u8) void { + const self = @fieldParentPtr(Pcg, "random", r); + + var i: usize = 0; + const aligned_len = buf.len - (buf.len & 7); + + // Complete 4 byte segments. + while (i < aligned_len) : (i += 4) { + var n = self.next(); + comptime var j: usize = 0; + inline while (j < 4) : (j += 1) { + buf[i + j] = @truncate(u8, n); + n >>= 8; + } + } + + // Remaining. (cuts the stream) + if (i != buf.len) { + var n = self.next(); + while (i < buf.len) : (i += 1) { + buf[i] = @truncate(u8, n); + n >>= 4; + } + } + } +}; + +test "pcg sequence" { + var r = Pcg.init(0); + const s0: u64 = 0x9394bf54ce5d79de; + const s1: u64 = 0x84e9c579ef59bbf7; + r.seedTwo(s0, s1); + + const seq = []const u32{ + 2881561918, + 3063928540, + 1199791034, + 2487695858, + 1479648952, + 3247963454, + }; + + for (seq) |s| { + expect(s == r.next()); + } +} + +// Xoroshiro128+ - http://xoroshiro.di.unimi.it/ +// +// PRNG +pub const Xoroshiro128 = struct { + random: Random, + + s: [2]u64, + + pub fn init(init_s: u64) Xoroshiro128 { + var x = Xoroshiro128{ + .random = Random{ .fillFn = fill }, + .s = undefined, + }; + + x.seed(init_s); + return x; + } + + fn next(self: *Xoroshiro128) u64 { + const s0 = self.s[0]; + var s1 = self.s[1]; + const r = s0 +% s1; + + s1 ^= s0; + self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14); + self.s[1] = math.rotl(u64, s1, u8(36)); + + return r; + } + + // Skip 2^64 places ahead in the sequence + fn jump(self: *Xoroshiro128) void { + var s0: u64 = 0; + var s1: u64 = 0; + + const table = []const u64{ + 0xbeac0467eba5facb, + 0xd86b048b86aa9922, + }; + + inline for (table) |entry| { + var b: usize = 0; + while (b < 64) : (b += 1) { + if ((entry & (u64(1) << @intCast(u6, b))) != 0) { + s0 ^= self.s[0]; + s1 ^= self.s[1]; + } + _ = self.next(); + } + } + + self.s[0] = s0; + self.s[1] = s1; + } + + fn seed(self: *Xoroshiro128, init_s: u64) void { + // Xoroshiro requires 128-bits of seed. + var gen = SplitMix64.init(init_s); + + self.s[0] = gen.next(); + self.s[1] = gen.next(); + } + + fn fill(r: *Random, buf: []u8) void { + const self = @fieldParentPtr(Xoroshiro128, "random", r); + + var i: usize = 0; + const aligned_len = buf.len - (buf.len & 7); + + // Complete 8 byte segments. + while (i < aligned_len) : (i += 8) { + var n = self.next(); + comptime var j: usize = 0; + inline while (j < 8) : (j += 1) { + buf[i + j] = @truncate(u8, n); + n >>= 8; + } + } + + // Remaining. (cuts the stream) + if (i != buf.len) { + var n = self.next(); + while (i < buf.len) : (i += 1) { + buf[i] = @truncate(u8, n); + n >>= 8; + } + } + } +}; + +test "xoroshiro sequence" { + var r = Xoroshiro128.init(0); + r.s[0] = 0xaeecf86f7878dd75; + r.s[1] = 0x01cd153642e72622; + + const seq1 = []const u64{ + 0xb0ba0da5bb600397, + 0x18a08afde614dccc, + 0xa2635b956a31b929, + 0xabe633c971efa045, + 0x9ac19f9706ca3cac, + 0xf62b426578c1e3fb, + }; + + for (seq1) |s| { + expect(s == r.next()); + } + + r.jump(); + + const seq2 = []const u64{ + 0x95344a13556d3e22, + 0xb4fb32dafa4d00df, + 0xb2011d9ccdcfe2dd, + 0x05679a9b2119b908, + 0xa860a1da7c9cd8a0, + 0x658a96efe3f86550, + }; + + for (seq2) |s| { + expect(s == r.next()); + } +} + +// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html +// +// CSPRNG +// +// Follows the general idea of the implementation from here with a few shortcuts. +// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html +pub const Isaac64 = struct { + random: Random, + + r: [256]u64, + m: [256]u64, + a: u64, + b: u64, + c: u64, + i: usize, + + pub fn init(init_s: u64) Isaac64 { + var isaac = Isaac64{ + .random = Random{ .fillFn = fill }, + .r = undefined, + .m = undefined, + .a = undefined, + .b = undefined, + .c = undefined, + .i = undefined, + }; + + // seed == 0 => same result as the unseeded reference implementation + isaac.seed(init_s, 1); + return isaac; + } + + fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { + 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]; + self.m[base + m1] = y; + + self.b = x +% self.m[(y >> 11) % self.m.len]; + self.r[self.r.len - 1 - base - m1] = self.b; + } + + fn refill(self: *Isaac64) void { + const midpoint = self.r.len / 2; + + self.c +%= 1; + self.b +%= self.c; + + { + var i: usize = 0; + while (i < midpoint) : (i += 4) { + self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); + self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); + self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); + self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); + } + } + + { + var i: usize = 0; + while (i < midpoint) : (i += 4) { + self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); + self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); + self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); + self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); + } + } + + self.i = 0; + } + + fn next(self: *Isaac64) u64 { + if (self.i >= self.r.len) { + self.refill(); + } + + const value = self.r[self.i]; + self.i += 1; + return value; + } + + fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { + // We ignore the multi-pass requirement since we don't currently expose full access to + // seeding the self.m array completely. + mem.set(u64, self.m[0..], 0); + self.m[0] = init_s; + + // prescrambled golden ratio constants + var a = []const u64{ + 0x647c4677a2884b7c, + 0xb9f8b322c73ac862, + 0x8c0ea5053d4712a0, + 0xb29b2e824a595524, + 0x82f053db8355e0ce, + 0x48fe4a0fa5a09315, + 0xae985bf2cbfc89ed, + 0x98f5704f6c44c0ab, + }; + + comptime var i: usize = 0; + inline while (i < rounds) : (i += 1) { + var j: usize = 0; + while (j < self.m.len) : (j += 8) { + comptime var x1: usize = 0; + inline while (x1 < 8) : (x1 += 1) { + a[x1] +%= self.m[j + x1]; + } + + a[0] -%= a[4]; + a[5] ^= a[7] >> 9; + a[7] +%= a[0]; + a[1] -%= a[5]; + a[6] ^= a[0] << 9; + a[0] +%= a[1]; + a[2] -%= a[6]; + a[7] ^= a[1] >> 23; + a[1] +%= a[2]; + a[3] -%= a[7]; + a[0] ^= a[2] << 15; + a[2] +%= a[3]; + a[4] -%= a[0]; + a[1] ^= a[3] >> 14; + a[3] +%= a[4]; + a[5] -%= a[1]; + a[2] ^= a[4] << 20; + a[4] +%= a[5]; + a[6] -%= a[2]; + a[3] ^= a[5] >> 17; + a[5] +%= a[6]; + a[7] -%= a[3]; + a[4] ^= a[6] << 14; + a[6] +%= a[7]; + + comptime var x2: usize = 0; + inline while (x2 < 8) : (x2 += 1) { + self.m[j + x2] = a[x2]; + } + } + } + + mem.set(u64, self.r[0..], 0); + self.a = 0; + self.b = 0; + self.c = 0; + self.i = self.r.len; // trigger refill on first value + } + + fn fill(r: *Random, buf: []u8) void { + const self = @fieldParentPtr(Isaac64, "random", r); + + var i: usize = 0; + const aligned_len = buf.len - (buf.len & 7); + + // Fill complete 64-byte segments + while (i < aligned_len) : (i += 8) { + var n = self.next(); + comptime var j: usize = 0; + inline while (j < 8) : (j += 1) { + buf[i + j] = @truncate(u8, n); + n >>= 8; + } + } + + // Fill trailing, ignoring excess (cut the stream). + if (i != buf.len) { + var n = self.next(); + while (i < buf.len) : (i += 1) { + buf[i] = @truncate(u8, n); + n >>= 8; + } + } + } +}; + +test "isaac64 sequence" { + var r = Isaac64.init(0); + + // from reference implementation + const seq = []const u64{ + 0xf67dfba498e4937c, + 0x84a5066a9204f380, + 0xfee34bd5f5514dbb, + 0x4d1664739b8f80d6, + 0x8607459ab52a14aa, + 0x0e78bc5a98529e49, + 0xfe5332822ad13777, + 0x556c27525e33d01a, + 0x08643ca615f3149f, + 0xd0771faf3cb04714, + 0x30e86f68a37b008d, + 0x3074ebc0488a3adf, + 0x270645ea7a2790bc, + 0x5601a0a8d3763c6a, + 0x2f83071f53f325dd, + 0xb9090f3d42d2d2ea, + }; + + for (seq) |s| { + expect(s == r.next()); + } +} + +// Actual Random helper function tests, pcg engine is assumed correct. +test "Random float" { + var prng = DefaultPrng.init(0); + + var i: usize = 0; + while (i < 1000) : (i += 1) { + const val1 = prng.random.float(f32); + expect(val1 >= 0.0); + expect(val1 < 1.0); + + const val2 = prng.random.float(f64); + expect(val2 >= 0.0); + expect(val2 < 1.0); + } +} + +test "Random shuffle" { + var prng = DefaultPrng.init(0); + + var seq = []const u8{ 0, 1, 2, 3, 4 }; + var seen = []bool{false} ** 5; + + var i: usize = 0; + while (i < 1000) : (i += 1) { + prng.random.shuffle(u8, seq[0..]); + seen[seq[0]] = true; + expect(sumArray(seq[0..]) == 10); + } + + // we should see every entry at the head at least once + for (seen) |e| { + expect(e == true); + } +} + +fn sumArray(s: []const u8) u32 { + var r: u32 = 0; + for (s) |e| + r += e; + return r; +} + +test "Random range" { + var prng = DefaultPrng.init(0); + testRange(&prng.random, -4, 3); + testRange(&prng.random, -4, -1); + testRange(&prng.random, 10, 14); + testRange(&prng.random, -0x80, 0x7f); +} + +fn testRange(r: *Random, start: i8, end: i8) void { + testRangeBias(r, start, end, true); + testRangeBias(r, start, end, false); +} +fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void { + const count = @intCast(usize, i32(end) - i32(start)); + var values_buffer = []bool{false} ** 0x100; + const values = values_buffer[0..count]; + var i: usize = 0; + while (i < count) { + const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); + const index = @intCast(usize, value - start); + if (!values[index]) { + i += 1; + values[index] = true; + } + } +} diff --git a/std/rand/index.zig b/std/rand/index.zig deleted file mode 100644 index 12dd763aee..0000000000 --- a/std/rand/index.zig +++ /dev/null @@ -1,1005 +0,0 @@ -// The engines provided here should be initialized from an external source. For now, getRandomBytes -// from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using -// a normal PRNG will be faster and use substantially less stack space. -// -// ``` -// var buf: [8]u8 = undefined; -// try std.os.getRandomBytes(buf[0..]); -// const seed = mem.readIntSliceLittle(u64, buf[0..8]); -// -// var r = DefaultPrng.init(seed); -// -// const s = r.random.int(u64); -// ``` -// -// TODO(tiehuis): Benchmark these against other reference implementations. - -const std = @import("../index.zig"); -const builtin = @import("builtin"); -const assert = std.debug.assert; -const expect = std.testing.expect; -const mem = std.mem; -const math = std.math; -const ziggurat = @import("ziggurat.zig"); -const maxInt = std.math.maxInt; - -// When you need fast unbiased random numbers -pub const DefaultPrng = Xoroshiro128; - -// When you need cryptographically secure random numbers -pub const DefaultCsprng = Isaac64; - -pub const Random = struct { - fillFn: fn (r: *Random, buf: []u8) void, - - /// Read random bytes into the specified buffer until full. - pub fn bytes(r: *Random, buf: []u8) void { - r.fillFn(r, buf); - } - - pub fn boolean(r: *Random) bool { - return r.int(u1) != 0; - } - - /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. - /// `i` is evenly distributed. - pub fn int(r: *Random, comptime T: type) T { - const UnsignedT = @IntType(false, T.bit_count); - const ByteAlignedT = @IntType(false, @divTrunc(T.bit_count + 7, 8) * 8); - - var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined; - r.bytes(rand_bytes[0..]); - - // use LE instead of native endian for better portability maybe? - // TODO: endian portability is pointless if the underlying prng isn't endian portable. - // TODO: document the endian portability of this library. - const byte_aligned_result = mem.readIntSliceLittle(ByteAlignedT, rand_bytes); - const unsigned_result = @truncate(UnsignedT, byte_aligned_result); - return @bitCast(T, unsigned_result); - } - - /// Constant-time implementation off ::uintLessThan. - /// The results of this function may be biased. - pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T { - comptime assert(T.is_signed == false); - comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! - assert(0 < less_than); - if (T.bit_count <= 32) { - return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than)); - } else { - return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than)); - } - } - - /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. - /// This function assumes that the underlying ::fillFn produces evenly distributed values. - /// Within this assumption, the runtime of this function is exponentially distributed. - /// If ::fillFn were backed by a true random generator, - /// the runtime of this function would technically be unbounded. - /// However, if ::fillFn is backed by any evenly distributed pseudo random number generator, - /// this function is guaranteed to return. - /// If you need deterministic runtime bounds, use `::uintLessThanBiased`. - pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { - comptime assert(T.is_signed == false); - comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! - assert(0 < less_than); - // Small is typically u32 - const Small = @IntType(false, @divTrunc(T.bit_count + 31, 32) * 32); - // Large is typically u64 - const Large = @IntType(false, Small.bit_count * 2); - - // adapted from: - // http://www.pcg-random.org/posts/bounded-rands.html - // "Lemire's (with an extra tweak from me)" - var x: Small = r.int(Small); - var m: Large = Large(x) * Large(less_than); - var l: Small = @truncate(Small, m); - if (l < less_than) { - // TODO: workaround for https://github.com/ziglang/zig/issues/1770 - // should be: - // var t: Small = -%less_than; - var t: Small = @bitCast(Small, -%@bitCast(@IntType(true, Small.bit_count), Small(less_than))); - - if (t >= less_than) { - t -= less_than; - if (t >= less_than) { - t %= less_than; - } - } - while (l < t) { - x = r.int(Small); - m = Large(x) * Large(less_than); - l = @truncate(Small, m); - } - } - return @intCast(T, m >> Small.bit_count); - } - - /// Constant-time implementation off ::uintAtMost. - /// The results of this function may be biased. - pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T { - assert(T.is_signed == false); - if (at_most == maxInt(T)) { - // have the full range - return r.int(T); - } - return r.uintLessThanBiased(T, at_most + 1); - } - - /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. - /// See ::uintLessThan, which this function uses in most cases, - /// for commentary on the runtime of this function. - pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T { - assert(T.is_signed == false); - if (at_most == maxInt(T)) { - // have the full range - return r.int(T); - } - return r.uintLessThan(T, at_most + 1); - } - - /// Constant-time implementation off ::intRangeLessThan. - /// The results of this function may be biased. - pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T { - assert(at_least < less_than); - if (T.is_signed) { - // Two's complement makes this math pretty easy. - const UnsignedT = @IntType(false, T.bit_count); - const lo = @bitCast(UnsignedT, at_least); - const hi = @bitCast(UnsignedT, less_than); - const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); - return @bitCast(T, result); - } else { - // The signed implementation would work fine, but we can use stricter arithmetic operators here. - return at_least + r.uintLessThanBiased(T, less_than - at_least); - } - } - - /// Returns an evenly distributed random integer `at_least <= i < less_than`. - /// See ::uintLessThan, which this function uses in most cases, - /// for commentary on the runtime of this function. - pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T { - assert(at_least < less_than); - if (T.is_signed) { - // Two's complement makes this math pretty easy. - const UnsignedT = @IntType(false, T.bit_count); - const lo = @bitCast(UnsignedT, at_least); - const hi = @bitCast(UnsignedT, less_than); - const result = lo +% r.uintLessThan(UnsignedT, hi -% lo); - return @bitCast(T, result); - } else { - // The signed implementation would work fine, but we can use stricter arithmetic operators here. - return at_least + r.uintLessThan(T, less_than - at_least); - } - } - - /// Constant-time implementation off ::intRangeAtMostBiased. - /// The results of this function may be biased. - pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T { - assert(at_least <= at_most); - if (T.is_signed) { - // Two's complement makes this math pretty easy. - const UnsignedT = @IntType(false, T.bit_count); - const lo = @bitCast(UnsignedT, at_least); - const hi = @bitCast(UnsignedT, at_most); - const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); - return @bitCast(T, result); - } else { - // The signed implementation would work fine, but we can use stricter arithmetic operators here. - return at_least + r.uintAtMostBiased(T, at_most - at_least); - } - } - - /// Returns an evenly distributed random integer `at_least <= i <= at_most`. - /// See ::uintLessThan, which this function uses in most cases, - /// for commentary on the runtime of this function. - pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T { - assert(at_least <= at_most); - if (T.is_signed) { - // Two's complement makes this math pretty easy. - const UnsignedT = @IntType(false, T.bit_count); - const lo = @bitCast(UnsignedT, at_least); - const hi = @bitCast(UnsignedT, at_most); - const result = lo +% r.uintAtMost(UnsignedT, hi -% lo); - return @bitCast(T, result); - } else { - // The signed implementation would work fine, but we can use stricter arithmetic operators here. - return at_least + r.uintAtMost(T, at_most - at_least); - } - } - - /// TODO: deprecated. use ::boolean or ::int instead. - pub fn scalar(r: *Random, comptime T: type) T { - return if (T == bool) r.boolean() else r.int(T); - } - - /// TODO: deprecated. renamed to ::intRangeLessThan - pub fn range(r: *Random, comptime T: type, start: T, end: T) T { - return r.intRangeLessThan(T, start, end); - } - - /// Return a floating point value evenly distributed in the range [0, 1). - pub fn float(r: *Random, comptime T: type) T { - // Generate a uniform value between [1, 2) and scale down to [0, 1). - // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. - switch (T) { - f32 => { - const s = r.int(u32); - const repr = (0x7f << 23) | (s >> 9); - return @bitCast(f32, repr) - 1.0; - }, - f64 => { - const s = r.int(u64); - const repr = (0x3ff << 52) | (s >> 12); - return @bitCast(f64, repr) - 1.0; - }, - else => @compileError("unknown floating point type"), - } - } - - /// Return a floating point value normally distributed with mean = 0, stddev = 1. - /// - /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. - pub fn floatNorm(r: *Random, comptime T: type) T { - const value = ziggurat.next_f64(r, ziggurat.NormDist); - switch (T) { - f32 => return @floatCast(f32, value), - f64 => return value, - else => @compileError("unknown floating point type"), - } - } - - /// Return an exponentially distributed float with a rate parameter of 1. - /// - /// To use a different rate parameter, use: floatExp(...) / desiredRate. - pub fn floatExp(r: *Random, comptime T: type) T { - const value = ziggurat.next_f64(r, ziggurat.ExpDist); - switch (T) { - f32 => return @floatCast(f32, value), - f64 => return value, - else => @compileError("unknown floating point type"), - } - } - - /// Shuffle a slice into a random order. - pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { - if (buf.len < 2) { - return; - } - - var i: usize = 0; - while (i < buf.len - 1) : (i += 1) { - const j = r.intRangeLessThan(usize, i, buf.len); - mem.swap(T, &buf[i], &buf[j]); - } - } -}; - -/// Convert a random integer 0 <= random_int <= maxValue(T), -/// into an integer 0 <= result < less_than. -/// This function introduces a minor bias. -pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { - comptime assert(T.is_signed == false); - const T2 = @IntType(false, T.bit_count * 2); - - // adapted from: - // http://www.pcg-random.org/posts/bounded-rands.html - // "Integer Multiplication (Biased)" - var m: T2 = T2(random_int) * T2(less_than); - return @intCast(T, m >> T.bit_count); -} - -const SequentialPrng = struct { - const Self = @This(); - random: Random, - next_value: u8, - - pub fn init() Self { - return Self{ - .random = Random{ .fillFn = fill }, - .next_value = 0, - }; - } - - fn fill(r: *Random, buf: []u8) void { - const self = @fieldParentPtr(Self, "random", r); - for (buf) |*b| { - b.* = self.next_value; - } - self.next_value +%= 1; - } -}; - -test "Random int" { - testRandomInt(); - comptime testRandomInt(); -} -fn testRandomInt() void { - var r = SequentialPrng.init(); - - expect(r.random.int(u0) == 0); - - r.next_value = 0; - expect(r.random.int(u1) == 0); - expect(r.random.int(u1) == 1); - expect(r.random.int(u2) == 2); - expect(r.random.int(u2) == 3); - expect(r.random.int(u2) == 0); - - r.next_value = 0xff; - expect(r.random.int(u8) == 0xff); - r.next_value = 0x11; - expect(r.random.int(u8) == 0x11); - - r.next_value = 0xff; - expect(r.random.int(u32) == 0xffffffff); - r.next_value = 0x11; - expect(r.random.int(u32) == 0x11111111); - - r.next_value = 0xff; - expect(r.random.int(i32) == -1); - r.next_value = 0x11; - expect(r.random.int(i32) == 0x11111111); - - r.next_value = 0xff; - expect(r.random.int(i8) == -1); - r.next_value = 0x11; - expect(r.random.int(i8) == 0x11); - - r.next_value = 0xff; - expect(r.random.int(u33) == 0x1ffffffff); - r.next_value = 0xff; - expect(r.random.int(i1) == -1); - r.next_value = 0xff; - expect(r.random.int(i2) == -1); - r.next_value = 0xff; - expect(r.random.int(i33) == -1); -} - -test "Random boolean" { - testRandomBoolean(); - comptime testRandomBoolean(); -} -fn testRandomBoolean() void { - var r = SequentialPrng.init(); - expect(r.random.boolean() == false); - expect(r.random.boolean() == true); - expect(r.random.boolean() == false); - expect(r.random.boolean() == true); -} - -test "Random intLessThan" { - @setEvalBranchQuota(10000); - testRandomIntLessThan(); - comptime testRandomIntLessThan(); -} -fn testRandomIntLessThan() void { - var r = SequentialPrng.init(); - r.next_value = 0xff; - expect(r.random.uintLessThan(u8, 4) == 3); - expect(r.next_value == 0); - expect(r.random.uintLessThan(u8, 4) == 0); - expect(r.next_value == 1); - - r.next_value = 0; - expect(r.random.uintLessThan(u64, 32) == 0); - - // trigger the bias rejection code path - r.next_value = 0; - expect(r.random.uintLessThan(u8, 3) == 0); - // verify we incremented twice - expect(r.next_value == 2); - - r.next_value = 0xff; - expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); - r.next_value = 0xff; - expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); - - r.next_value = 0xff; - expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); - r.next_value = 0xff; - expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); - r.next_value = 0xff; - expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1); - - r.next_value = 0xff; - expect(r.random.intRangeLessThan(i3, -4, 0) == -1); - r.next_value = 0xff; - expect(r.random.intRangeLessThan(i3, -2, 2) == 1); -} - -test "Random intAtMost" { - @setEvalBranchQuota(10000); - testRandomIntAtMost(); - comptime testRandomIntAtMost(); -} -fn testRandomIntAtMost() void { - var r = SequentialPrng.init(); - r.next_value = 0xff; - expect(r.random.uintAtMost(u8, 3) == 3); - expect(r.next_value == 0); - expect(r.random.uintAtMost(u8, 3) == 0); - - // trigger the bias rejection code path - r.next_value = 0; - expect(r.random.uintAtMost(u8, 2) == 0); - // verify we incremented twice - expect(r.next_value == 2); - - r.next_value = 0xff; - expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); - r.next_value = 0xff; - expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); - - r.next_value = 0xff; - expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); - r.next_value = 0xff; - expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); - r.next_value = 0xff; - expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1); - - r.next_value = 0xff; - expect(r.random.intRangeAtMost(i3, -4, -1) == -1); - r.next_value = 0xff; - expect(r.random.intRangeAtMost(i3, -2, 1) == 1); - - expect(r.random.uintAtMost(u0, 0) == 0); -} - -test "Random Biased" { - var r = DefaultPrng.init(0); - // Not thoroughly checking the logic here. - // Just want to execute all the paths with different types. - - expect(r.random.uintLessThanBiased(u1, 1) == 0); - expect(r.random.uintLessThanBiased(u32, 10) < 10); - expect(r.random.uintLessThanBiased(u64, 20) < 20); - - expect(r.random.uintAtMostBiased(u0, 0) == 0); - expect(r.random.uintAtMostBiased(u1, 0) <= 0); - expect(r.random.uintAtMostBiased(u32, 10) <= 10); - expect(r.random.uintAtMostBiased(u64, 20) <= 20); - - expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); - expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); - expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); - expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); - expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); - expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); - - // uncomment for broken module error: - //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); - expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); - expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); - expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); - expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); - expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); - expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); -} - -// Generator to extend 64-bit seed values into longer sequences. -// -// The number of cycles is thus limited to 64-bits regardless of the engine, but this -// is still plenty for practical purposes. -const SplitMix64 = struct { - s: u64, - - pub fn init(seed: u64) SplitMix64 { - return SplitMix64{ .s = seed }; - } - - pub fn next(self: *SplitMix64) u64 { - self.s +%= 0x9e3779b97f4a7c15; - - var z = self.s; - z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9; - z = (z ^ (z >> 27)) *% 0x94d049bb133111eb; - return z ^ (z >> 31); - } -}; - -test "splitmix64 sequence" { - var r = SplitMix64.init(0xaeecf86f7878dd75); - - const seq = []const u64{ - 0x5dbd39db0178eb44, - 0xa9900fb66b397da3, - 0x5c1a28b1aeebcf5c, - 0x64a963238f776912, - 0xc6d4177b21d1c0ab, - 0xb2cbdbdb5ea35394, - }; - - for (seq) |s| { - expect(s == r.next()); - } -} - -// PCG32 - http://www.pcg-random.org/ -// -// PRNG -pub const Pcg = struct { - const default_multiplier = 6364136223846793005; - - random: Random, - - s: u64, - i: u64, - - pub fn init(init_s: u64) Pcg { - var pcg = Pcg{ - .random = Random{ .fillFn = fill }, - .s = undefined, - .i = undefined, - }; - - pcg.seed(init_s); - return pcg; - } - - fn next(self: *Pcg) u32 { - const l = self.s; - self.s = l *% default_multiplier +% (self.i | 1); - - const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27); - const rot = @intCast(u32, l >> 59); - - return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31)); - } - - fn seed(self: *Pcg, init_s: u64) void { - // Pcg requires 128-bits of seed. - var gen = SplitMix64.init(init_s); - self.seedTwo(gen.next(), gen.next()); - } - - fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { - self.s = 0; - self.i = (init_s << 1) | 1; - self.s = self.s *% default_multiplier +% self.i; - self.s +%= init_i; - self.s = self.s *% default_multiplier +% self.i; - } - - fn fill(r: *Random, buf: []u8) void { - const self = @fieldParentPtr(Pcg, "random", r); - - var i: usize = 0; - const aligned_len = buf.len - (buf.len & 7); - - // Complete 4 byte segments. - while (i < aligned_len) : (i += 4) { - var n = self.next(); - comptime var j: usize = 0; - inline while (j < 4) : (j += 1) { - buf[i + j] = @truncate(u8, n); - n >>= 8; - } - } - - // Remaining. (cuts the stream) - if (i != buf.len) { - var n = self.next(); - while (i < buf.len) : (i += 1) { - buf[i] = @truncate(u8, n); - n >>= 4; - } - } - } -}; - -test "pcg sequence" { - var r = Pcg.init(0); - const s0: u64 = 0x9394bf54ce5d79de; - const s1: u64 = 0x84e9c579ef59bbf7; - r.seedTwo(s0, s1); - - const seq = []const u32{ - 2881561918, - 3063928540, - 1199791034, - 2487695858, - 1479648952, - 3247963454, - }; - - for (seq) |s| { - expect(s == r.next()); - } -} - -// Xoroshiro128+ - http://xoroshiro.di.unimi.it/ -// -// PRNG -pub const Xoroshiro128 = struct { - random: Random, - - s: [2]u64, - - pub fn init(init_s: u64) Xoroshiro128 { - var x = Xoroshiro128{ - .random = Random{ .fillFn = fill }, - .s = undefined, - }; - - x.seed(init_s); - return x; - } - - fn next(self: *Xoroshiro128) u64 { - const s0 = self.s[0]; - var s1 = self.s[1]; - const r = s0 +% s1; - - s1 ^= s0; - self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14); - self.s[1] = math.rotl(u64, s1, u8(36)); - - return r; - } - - // Skip 2^64 places ahead in the sequence - fn jump(self: *Xoroshiro128) void { - var s0: u64 = 0; - var s1: u64 = 0; - - const table = []const u64{ - 0xbeac0467eba5facb, - 0xd86b048b86aa9922, - }; - - inline for (table) |entry| { - var b: usize = 0; - while (b < 64) : (b += 1) { - if ((entry & (u64(1) << @intCast(u6, b))) != 0) { - s0 ^= self.s[0]; - s1 ^= self.s[1]; - } - _ = self.next(); - } - } - - self.s[0] = s0; - self.s[1] = s1; - } - - fn seed(self: *Xoroshiro128, init_s: u64) void { - // Xoroshiro requires 128-bits of seed. - var gen = SplitMix64.init(init_s); - - self.s[0] = gen.next(); - self.s[1] = gen.next(); - } - - fn fill(r: *Random, buf: []u8) void { - const self = @fieldParentPtr(Xoroshiro128, "random", r); - - var i: usize = 0; - const aligned_len = buf.len - (buf.len & 7); - - // Complete 8 byte segments. - while (i < aligned_len) : (i += 8) { - var n = self.next(); - comptime var j: usize = 0; - inline while (j < 8) : (j += 1) { - buf[i + j] = @truncate(u8, n); - n >>= 8; - } - } - - // Remaining. (cuts the stream) - if (i != buf.len) { - var n = self.next(); - while (i < buf.len) : (i += 1) { - buf[i] = @truncate(u8, n); - n >>= 8; - } - } - } -}; - -test "xoroshiro sequence" { - var r = Xoroshiro128.init(0); - r.s[0] = 0xaeecf86f7878dd75; - r.s[1] = 0x01cd153642e72622; - - const seq1 = []const u64{ - 0xb0ba0da5bb600397, - 0x18a08afde614dccc, - 0xa2635b956a31b929, - 0xabe633c971efa045, - 0x9ac19f9706ca3cac, - 0xf62b426578c1e3fb, - }; - - for (seq1) |s| { - expect(s == r.next()); - } - - r.jump(); - - const seq2 = []const u64{ - 0x95344a13556d3e22, - 0xb4fb32dafa4d00df, - 0xb2011d9ccdcfe2dd, - 0x05679a9b2119b908, - 0xa860a1da7c9cd8a0, - 0x658a96efe3f86550, - }; - - for (seq2) |s| { - expect(s == r.next()); - } -} - -// ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html -// -// CSPRNG -// -// Follows the general idea of the implementation from here with a few shortcuts. -// https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html -pub const Isaac64 = struct { - random: Random, - - r: [256]u64, - m: [256]u64, - a: u64, - b: u64, - c: u64, - i: usize, - - pub fn init(init_s: u64) Isaac64 { - var isaac = Isaac64{ - .random = Random{ .fillFn = fill }, - .r = undefined, - .m = undefined, - .a = undefined, - .b = undefined, - .c = undefined, - .i = undefined, - }; - - // seed == 0 => same result as the unseeded reference implementation - isaac.seed(init_s, 1); - return isaac; - } - - fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { - 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]; - self.m[base + m1] = y; - - self.b = x +% self.m[(y >> 11) % self.m.len]; - self.r[self.r.len - 1 - base - m1] = self.b; - } - - fn refill(self: *Isaac64) void { - const midpoint = self.r.len / 2; - - self.c +%= 1; - self.b +%= self.c; - - { - var i: usize = 0; - while (i < midpoint) : (i += 4) { - self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); - self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); - self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); - self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); - } - } - - { - var i: usize = 0; - while (i < midpoint) : (i += 4) { - self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); - self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); - self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); - self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); - } - } - - self.i = 0; - } - - fn next(self: *Isaac64) u64 { - if (self.i >= self.r.len) { - self.refill(); - } - - const value = self.r[self.i]; - self.i += 1; - return value; - } - - fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { - // We ignore the multi-pass requirement since we don't currently expose full access to - // seeding the self.m array completely. - mem.set(u64, self.m[0..], 0); - self.m[0] = init_s; - - // prescrambled golden ratio constants - var a = []const u64{ - 0x647c4677a2884b7c, - 0xb9f8b322c73ac862, - 0x8c0ea5053d4712a0, - 0xb29b2e824a595524, - 0x82f053db8355e0ce, - 0x48fe4a0fa5a09315, - 0xae985bf2cbfc89ed, - 0x98f5704f6c44c0ab, - }; - - comptime var i: usize = 0; - inline while (i < rounds) : (i += 1) { - var j: usize = 0; - while (j < self.m.len) : (j += 8) { - comptime var x1: usize = 0; - inline while (x1 < 8) : (x1 += 1) { - a[x1] +%= self.m[j + x1]; - } - - a[0] -%= a[4]; - a[5] ^= a[7] >> 9; - a[7] +%= a[0]; - a[1] -%= a[5]; - a[6] ^= a[0] << 9; - a[0] +%= a[1]; - a[2] -%= a[6]; - a[7] ^= a[1] >> 23; - a[1] +%= a[2]; - a[3] -%= a[7]; - a[0] ^= a[2] << 15; - a[2] +%= a[3]; - a[4] -%= a[0]; - a[1] ^= a[3] >> 14; - a[3] +%= a[4]; - a[5] -%= a[1]; - a[2] ^= a[4] << 20; - a[4] +%= a[5]; - a[6] -%= a[2]; - a[3] ^= a[5] >> 17; - a[5] +%= a[6]; - a[7] -%= a[3]; - a[4] ^= a[6] << 14; - a[6] +%= a[7]; - - comptime var x2: usize = 0; - inline while (x2 < 8) : (x2 += 1) { - self.m[j + x2] = a[x2]; - } - } - } - - mem.set(u64, self.r[0..], 0); - self.a = 0; - self.b = 0; - self.c = 0; - self.i = self.r.len; // trigger refill on first value - } - - fn fill(r: *Random, buf: []u8) void { - const self = @fieldParentPtr(Isaac64, "random", r); - - var i: usize = 0; - const aligned_len = buf.len - (buf.len & 7); - - // Fill complete 64-byte segments - while (i < aligned_len) : (i += 8) { - var n = self.next(); - comptime var j: usize = 0; - inline while (j < 8) : (j += 1) { - buf[i + j] = @truncate(u8, n); - n >>= 8; - } - } - - // Fill trailing, ignoring excess (cut the stream). - if (i != buf.len) { - var n = self.next(); - while (i < buf.len) : (i += 1) { - buf[i] = @truncate(u8, n); - n >>= 8; - } - } - } -}; - -test "isaac64 sequence" { - var r = Isaac64.init(0); - - // from reference implementation - const seq = []const u64{ - 0xf67dfba498e4937c, - 0x84a5066a9204f380, - 0xfee34bd5f5514dbb, - 0x4d1664739b8f80d6, - 0x8607459ab52a14aa, - 0x0e78bc5a98529e49, - 0xfe5332822ad13777, - 0x556c27525e33d01a, - 0x08643ca615f3149f, - 0xd0771faf3cb04714, - 0x30e86f68a37b008d, - 0x3074ebc0488a3adf, - 0x270645ea7a2790bc, - 0x5601a0a8d3763c6a, - 0x2f83071f53f325dd, - 0xb9090f3d42d2d2ea, - }; - - for (seq) |s| { - expect(s == r.next()); - } -} - -// Actual Random helper function tests, pcg engine is assumed correct. -test "Random float" { - var prng = DefaultPrng.init(0); - - var i: usize = 0; - while (i < 1000) : (i += 1) { - const val1 = prng.random.float(f32); - expect(val1 >= 0.0); - expect(val1 < 1.0); - - const val2 = prng.random.float(f64); - expect(val2 >= 0.0); - expect(val2 < 1.0); - } -} - -test "Random shuffle" { - var prng = DefaultPrng.init(0); - - var seq = []const u8{ 0, 1, 2, 3, 4 }; - var seen = []bool{false} ** 5; - - var i: usize = 0; - while (i < 1000) : (i += 1) { - prng.random.shuffle(u8, seq[0..]); - seen[seq[0]] = true; - expect(sumArray(seq[0..]) == 10); - } - - // we should see every entry at the head at least once - for (seen) |e| { - expect(e == true); - } -} - -fn sumArray(s: []const u8) u32 { - var r: u32 = 0; - for (s) |e| - r += e; - return r; -} - -test "Random range" { - var prng = DefaultPrng.init(0); - testRange(&prng.random, -4, 3); - testRange(&prng.random, -4, -1); - testRange(&prng.random, 10, 14); - testRange(&prng.random, -0x80, 0x7f); -} - -fn testRange(r: *Random, start: i8, end: i8) void { - testRangeBias(r, start, end, true); - testRangeBias(r, start, end, false); -} -fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void { - const count = @intCast(usize, i32(end) - i32(start)); - var values_buffer = []bool{false} ** 0x100; - const values = values_buffer[0..count]; - var i: usize = 0; - while (i < count) { - const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); - const index = @intCast(usize, value - start); - if (!values[index]) { - i += 1; - values[index] = true; - } - } -} diff --git a/std/rand/ziggurat.zig b/std/rand/ziggurat.zig index 0636a5bfc4..995248415b 100644 --- a/std/rand/ziggurat.zig +++ b/std/rand/ziggurat.zig @@ -8,7 +8,7 @@ // NOTE: This seems interesting but reference code is a bit hard to grok: // https://sbarral.github.io/etf. -const std = @import("../index.zig"); +const std = @import("../std.zig"); const math = std.math; const Random = std.rand.Random; diff --git a/std/rb.zig b/std/rb.zig index 27e60dca6a..0b28550a13 100644 --- a/std/rb.zig +++ b/std/rb.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; // For mem.Compare diff --git a/std/segmented_list.zig b/std/segmented_list.zig index 26b7fa48f5..0600fbba3f 100644 --- a/std/segmented_list.zig +++ b/std/segmented_list.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const assert = std.debug.assert; const testing = std.testing; const Allocator = std.mem.Allocator; diff --git a/std/sort.zig b/std/sort.zig index 86a6724fee..69dc148f31 100644 --- a/std/sort.zig +++ b/std/sort.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const assert = std.debug.assert; const testing = std.testing; const mem = std.mem; diff --git a/std/special/compiler_rt.zig b/std/special/compiler_rt.zig new file mode 100644 index 0000000000..e8a45949e4 --- /dev/null +++ b/std/special/compiler_rt.zig @@ -0,0 +1,1050 @@ +const builtin = @import("builtin"); +const is_test = builtin.is_test; + +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; + + @export("__letf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export("__getf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage); + + if (!is_test) { + // only create these aliases when not testing + @export("__cmptf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export("__eqtf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export("__lttf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); + @export("__netf2", @import("compiler_rt/comparetf2.zig").__letf2, 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("__unordtf2", @import("compiler_rt/comparetf2.zig").__unordtf2, linkage); + + @export("__addtf3", @import("compiler_rt/addXf3.zig").__addtf3, linkage); + @export("__subtf3", @import("compiler_rt/addXf3.zig").__subtf3, 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); + + @export("__floatunditf", @import("compiler_rt/floatunditf.zig").__floatunditf, linkage); + @export("__floatunsitf", @import("compiler_rt/floatunsitf.zig").__floatunsitf, linkage); + + @export("__floatuntitf", @import("compiler_rt/floatuntitf.zig").__floatuntitf, linkage); + @export("__floatuntidf", @import("compiler_rt/floatuntidf.zig").__floatuntidf, linkage); + @export("__floatuntisf", @import("compiler_rt/floatuntisf.zig").__floatuntisf, linkage); + + @export("__extenddftf2", @import("compiler_rt/extendXfYf2.zig").__extenddftf2, linkage); + @export("__extendsftf2", @import("compiler_rt/extendXfYf2.zig").__extendsftf2, linkage); + @export("__extendhfsf2", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); + + @export("__truncsfhf2", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); + @export("__truncdfhf2", @import("compiler_rt/truncXfYf2.zig").__truncdfhf2, linkage); + @export("__trunctfdf2", @import("compiler_rt/truncXfYf2.zig").__trunctfdf2, linkage); + @export("__trunctfsf2", @import("compiler_rt/truncXfYf2.zig").__trunctfsf2, 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); + + @export("__fixunsdfsi", @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi, linkage); + @export("__fixunsdfdi", @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi, linkage); + @export("__fixunsdfti", @import("compiler_rt/fixunsdfti.zig").__fixunsdfti, linkage); + + @export("__fixunstfsi", @import("compiler_rt/fixunstfsi.zig").__fixunstfsi, linkage); + @export("__fixunstfdi", @import("compiler_rt/fixunstfdi.zig").__fixunstfdi, linkage); + @export("__fixunstfti", @import("compiler_rt/fixunstfti.zig").__fixunstfti, linkage); + + @export("__fixdfdi", @import("compiler_rt/fixdfdi.zig").__fixdfdi, linkage); + @export("__fixdfsi", @import("compiler_rt/fixdfsi.zig").__fixdfsi, linkage); + @export("__fixdfti", @import("compiler_rt/fixdfti.zig").__fixdfti, linkage); + @export("__fixsfdi", @import("compiler_rt/fixsfdi.zig").__fixsfdi, linkage); + @export("__fixsfsi", @import("compiler_rt/fixsfsi.zig").__fixsfsi, linkage); + @export("__fixsfti", @import("compiler_rt/fixsfti.zig").__fixsfti, linkage); + @export("__fixtfdi", @import("compiler_rt/fixtfdi.zig").__fixtfdi, linkage); + @export("__fixtfsi", @import("compiler_rt/fixtfsi.zig").__fixtfsi, linkage); + @export("__fixtfti", @import("compiler_rt/fixtfti.zig").__fixtfti, linkage); + + @export("__udivmoddi4", @import("compiler_rt/udivmoddi4.zig").__udivmoddi4, linkage); + + @export("__udivsi3", __udivsi3, linkage); + @export("__udivdi3", __udivdi3, linkage); + @export("__umoddi3", __umoddi3, linkage); + @export("__udivmodsi4", __udivmodsi4, linkage); + + if (is_arm_arch and !is_arm_64) { + @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); + @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); + @export("__aeabi_uidiv", __udivsi3, linkage); + } + if (builtin.os == builtin.Os.windows) { + 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); + } + @export("__divti3", @import("compiler_rt/divti3.zig").__divti3_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); + }, + else => {}, + } + } else { + @export("__divti3", @import("compiler_rt/divti3.zig").__divti3, 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); + } +} + +const std = @import("std"); +const assert = std.debug.assert; +const testing = std.testing; + +const __udivmoddi4 = @import("compiler_rt/udivmoddi4.zig").__udivmoddi4; + +// Avoid dragging in the runtime safety mechanisms into this .o file, +// unless we're trying to test this file. +pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { + @setCold(true); + if (is_test) { + std.debug.panic("{}", msg); + } else { + unreachable; + } +} + +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 __udivdi3(a: u64, b: u64) u64 { + @setRuntimeSafety(is_test); + return __udivmoddi4(a, b, null); +} + +extern fn __umoddi3(a: u64, b: u64) u64 { + @setRuntimeSafety(is_test); + + var r: u64 = undefined; + _ = __udivmoddi4(a, b, &r); + return r; +} + +const AeabiUlDivModResult = extern struct { + quot: u64, + rem: u64, +}; +extern fn __aeabi_uldivmod(numerator: u64, denominator: u64) AeabiUlDivModResult { + @setRuntimeSafety(is_test); + var result: AeabiUlDivModResult = undefined; + result.quot = __udivmoddi4(numerator, denominator, &result.rem); + return result; +} + +const is_arm_64 = switch (builtin.arch) { + builtin.Arch.aarch64v8_3a, + builtin.Arch.aarch64v8_2a, + builtin.Arch.aarch64v8_1a, + builtin.Arch.aarch64v8, + builtin.Arch.aarch64v8r, + builtin.Arch.aarch64v8m_baseline, + builtin.Arch.aarch64v8m_mainline, + builtin.Arch.aarch64_bev8_3a, + builtin.Arch.aarch64_bev8_2a, + builtin.Arch.aarch64_bev8_1a, + builtin.Arch.aarch64_bev8, + builtin.Arch.aarch64_bev8r, + builtin.Arch.aarch64_bev8m_baseline, + builtin.Arch.aarch64_bev8m_mainline, + => true, + else => false, +}; + +const is_arm_arch = switch (builtin.arch) { + builtin.Arch.arm, + builtin.Arch.armeb, + builtin.Arch.aarch64, + builtin.Arch.aarch64_be, + builtin.Arch.thumb, + builtin.Arch.thumbeb, + => true, + 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" + ); +} + +// _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); + + 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 + ); +} + +extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { + @setRuntimeSafety(is_test); + + const d = __udivsi3(a, b); + rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); + return d; +} + +extern fn __udivsi3(n: u32, d: u32) u32 { + @setRuntimeSafety(is_test); + + const n_uword_bits: c_uint = u32.bit_count; + // special cases + if (d == 0) return 0; // ?! + if (n == 0) return 0; + var sr = @bitCast(c_uint, c_int(@clz(d)) - c_int(@clz(n))); + // 0 <= sr <= n_uword_bits - 1 or sr large + if (sr > n_uword_bits - 1) { + // d > r + return 0; + } + if (sr == n_uword_bits - 1) { + // d == 1 + return n; + } + sr += 1; + // 1 <= sr <= n_uword_bits - 1 + // Not a special case + var q: u32 = n << @intCast(u5, n_uword_bits - sr); + var r: u32 = n >> @intCast(u5, sr); + var carry: u32 = 0; + while (sr > 0) : (sr -= 1) { + // r:q = ((r:q) << 1) | carry + r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); + q = (q << 1) | carry; + // carry = 0; + // if (r.all >= d.all) + // { + // r.all -= d.all; + // carry = 1; + // } + const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); + carry = @intCast(u32, s & 1); + r -= d & @bitCast(u32, s); + } + q = (q << 1) | carry; + return q; +} + +test "test_umoddi3" { + test_one_umoddi3(0, 1, 0); + test_one_umoddi3(2, 1, 0); + test_one_umoddi3(0x8000000000000000, 1, 0x0); + test_one_umoddi3(0x8000000000000000, 2, 0x0); + test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); +} + +fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { + const r = __umoddi3(a, b); + testing.expect(r == expected_r); +} + +test "test_udivsi3" { + 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, + 0x00000001, + }, + []u32{ + 0x00000001, + 0x00000002, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x00000003, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000001, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000001, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000001, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000001, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x00000001, + 0x00000002, + }, + []u32{ + 0x00000002, + 0x00000002, + 0x00000001, + }, + []u32{ + 0x00000002, + 0x00000003, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000002, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000002, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000002, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000002, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x00000001, + 0x00000003, + }, + []u32{ + 0x00000003, + 0x00000002, + 0x00000001, + }, + []u32{ + 0x00000003, + 0x00000003, + 0x00000001, + }, + []u32{ + 0x00000003, + 0x00000010, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000003, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000003, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000003, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000003, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x00000001, + 0x00000010, + }, + []u32{ + 0x00000010, + 0x00000002, + 0x00000008, + }, + []u32{ + 0x00000010, + 0x00000003, + 0x00000005, + }, + []u32{ + 0x00000010, + 0x00000010, + 0x00000001, + }, + []u32{ + 0x00000010, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x0747AE14, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x00000010, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x00000010, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x00000010, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x00000010, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0x00000001, + 0x078644FA, + }, + []u32{ + 0x078644FA, + 0x00000002, + 0x03C3227D, + }, + []u32{ + 0x078644FA, + 0x00000003, + 0x028216FE, + }, + []u32{ + 0x078644FA, + 0x00000010, + 0x0078644F, + }, + []u32{ + 0x078644FA, + 0x078644FA, + 0x00000001, + }, + []u32{ + 0x078644FA, + 0x0747AE14, + 0x00000001, + }, + []u32{ + 0x078644FA, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x078644FA, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0x00000001, + 0x0747AE14, + }, + []u32{ + 0x0747AE14, + 0x00000002, + 0x03A3D70A, + }, + []u32{ + 0x0747AE14, + 0x00000003, + 0x026D3A06, + }, + []u32{ + 0x0747AE14, + 0x00000010, + 0x00747AE1, + }, + []u32{ + 0x0747AE14, + 0x078644FA, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0x0747AE14, + 0x00000001, + }, + []u32{ + 0x0747AE14, + 0x7FFFFFFF, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x0747AE14, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0x00000001, + 0x7FFFFFFF, + }, + []u32{ + 0x7FFFFFFF, + 0x00000002, + 0x3FFFFFFF, + }, + []u32{ + 0x7FFFFFFF, + 0x00000003, + 0x2AAAAAAA, + }, + []u32{ + 0x7FFFFFFF, + 0x00000010, + 0x07FFFFFF, + }, + []u32{ + 0x7FFFFFFF, + 0x078644FA, + 0x00000011, + }, + []u32{ + 0x7FFFFFFF, + 0x0747AE14, + 0x00000011, + }, + []u32{ + 0x7FFFFFFF, + 0x7FFFFFFF, + 0x00000001, + }, + []u32{ + 0x7FFFFFFF, + 0x80000000, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x7FFFFFFF, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0x80000000, + 0x00000001, + 0x80000000, + }, + []u32{ + 0x80000000, + 0x00000002, + 0x40000000, + }, + []u32{ + 0x80000000, + 0x00000003, + 0x2AAAAAAA, + }, + []u32{ + 0x80000000, + 0x00000010, + 0x08000000, + }, + []u32{ + 0x80000000, + 0x078644FA, + 0x00000011, + }, + []u32{ + 0x80000000, + 0x0747AE14, + 0x00000011, + }, + []u32{ + 0x80000000, + 0x7FFFFFFF, + 0x00000001, + }, + []u32{ + 0x80000000, + 0x80000000, + 0x00000001, + }, + []u32{ + 0x80000000, + 0xFFFFFFFD, + 0x00000000, + }, + []u32{ + 0x80000000, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0x80000000, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0xFFFFFFFD, + 0x00000001, + 0xFFFFFFFD, + }, + []u32{ + 0xFFFFFFFD, + 0x00000002, + 0x7FFFFFFE, + }, + []u32{ + 0xFFFFFFFD, + 0x00000003, + 0x55555554, + }, + []u32{ + 0xFFFFFFFD, + 0x00000010, + 0x0FFFFFFF, + }, + []u32{ + 0xFFFFFFFD, + 0x078644FA, + 0x00000022, + }, + []u32{ + 0xFFFFFFFD, + 0x0747AE14, + 0x00000023, + }, + []u32{ + 0xFFFFFFFD, + 0x7FFFFFFF, + 0x00000001, + }, + []u32{ + 0xFFFFFFFD, + 0x80000000, + 0x00000001, + }, + []u32{ + 0xFFFFFFFD, + 0xFFFFFFFD, + 0x00000001, + }, + []u32{ + 0xFFFFFFFD, + 0xFFFFFFFE, + 0x00000000, + }, + []u32{ + 0xFFFFFFFD, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0xFFFFFFFE, + 0x00000001, + 0xFFFFFFFE, + }, + []u32{ + 0xFFFFFFFE, + 0x00000002, + 0x7FFFFFFF, + }, + []u32{ + 0xFFFFFFFE, + 0x00000003, + 0x55555554, + }, + []u32{ + 0xFFFFFFFE, + 0x00000010, + 0x0FFFFFFF, + }, + []u32{ + 0xFFFFFFFE, + 0x078644FA, + 0x00000022, + }, + []u32{ + 0xFFFFFFFE, + 0x0747AE14, + 0x00000023, + }, + []u32{ + 0xFFFFFFFE, + 0x7FFFFFFF, + 0x00000002, + }, + []u32{ + 0xFFFFFFFE, + 0x80000000, + 0x00000001, + }, + []u32{ + 0xFFFFFFFE, + 0xFFFFFFFD, + 0x00000001, + }, + []u32{ + 0xFFFFFFFE, + 0xFFFFFFFE, + 0x00000001, + }, + []u32{ + 0xFFFFFFFE, + 0xFFFFFFFF, + 0x00000000, + }, + []u32{ + 0xFFFFFFFF, + 0x00000001, + 0xFFFFFFFF, + }, + []u32{ + 0xFFFFFFFF, + 0x00000002, + 0x7FFFFFFF, + }, + []u32{ + 0xFFFFFFFF, + 0x00000003, + 0x55555555, + }, + []u32{ + 0xFFFFFFFF, + 0x00000010, + 0x0FFFFFFF, + }, + []u32{ + 0xFFFFFFFF, + 0x078644FA, + 0x00000022, + }, + []u32{ + 0xFFFFFFFF, + 0x0747AE14, + 0x00000023, + }, + []u32{ + 0xFFFFFFFF, + 0x7FFFFFFF, + 0x00000002, + }, + []u32{ + 0xFFFFFFFF, + 0x80000000, + 0x00000001, + }, + []u32{ + 0xFFFFFFFF, + 0xFFFFFFFD, + 0x00000001, + }, + []u32{ + 0xFFFFFFFF, + 0xFFFFFFFE, + 0x00000001, + }, + []u32{ + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x00000001, + }, + }; + + for (cases) |case| { + test_one_udivsi3(case[0], case[1], case[2]); + } +} + +fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { + const q: u32 = __udivsi3(a, b); + testing.expect(q == expected_q); +} diff --git a/std/special/compiler_rt/addXf3.zig b/std/special/compiler_rt/addXf3.zig index 09413b2328..c7e80e4e71 100644 --- a/std/special/compiler_rt/addXf3.zig +++ b/std/special/compiler_rt/addXf3.zig @@ -4,7 +4,7 @@ const std = @import("std"); const builtin = @import("builtin"); -const compiler_rt = @import("index.zig"); +const compiler_rt = @import("../compiler_rt.zig"); pub extern fn __addtf3(a: f128, b: f128) f128 { return addXf3(f128, a, b); diff --git a/std/special/compiler_rt/divti3.zig b/std/special/compiler_rt/divti3.zig index 712cccba82..e89a1ada5c 100644 --- a/std/special/compiler_rt/divti3.zig +++ b/std/special/compiler_rt/divti3.zig @@ -1,6 +1,6 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); -const compiler_rt = @import("index.zig"); +const compiler_rt = @import("../compiler_rt.zig"); pub extern fn __divti3(a: i128, b: i128) i128 { @setRuntimeSafety(builtin.is_test); diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig deleted file mode 100644 index 7657ce6118..0000000000 --- a/std/special/compiler_rt/index.zig +++ /dev/null @@ -1,1050 +0,0 @@ -const builtin = @import("builtin"); -const is_test = builtin.is_test; - -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; - - @export("__letf2", @import("comparetf2.zig").__letf2, linkage); - @export("__getf2", @import("comparetf2.zig").__getf2, linkage); - - if (!is_test) { - // only create these aliases when not testing - @export("__cmptf2", @import("comparetf2.zig").__letf2, linkage); - @export("__eqtf2", @import("comparetf2.zig").__letf2, linkage); - @export("__lttf2", @import("comparetf2.zig").__letf2, linkage); - @export("__netf2", @import("comparetf2.zig").__letf2, linkage); - @export("__gttf2", @import("comparetf2.zig").__getf2, linkage); - @export("__gnu_h2f_ieee", @import("extendXfYf2.zig").__extendhfsf2, linkage); - @export("__gnu_f2h_ieee", @import("truncXfYf2.zig").__truncsfhf2, linkage); - } - - @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); - - @export("__addtf3", @import("addXf3.zig").__addtf3, linkage); - @export("__subtf3", @import("addXf3.zig").__subtf3, linkage); - - @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); - @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); - @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); - - @export("__floatunditf", @import("floatunditf.zig").__floatunditf, linkage); - @export("__floatunsitf", @import("floatunsitf.zig").__floatunsitf, linkage); - - @export("__floatuntitf", @import("floatuntitf.zig").__floatuntitf, linkage); - @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); - @export("__floatuntisf", @import("floatuntisf.zig").__floatuntisf, linkage); - - @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); - @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); - @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); - - @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); - @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage); - @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); - @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); - - @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); - @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); - @export("__fixunssfti", @import("fixunssfti.zig").__fixunssfti, linkage); - - @export("__fixunsdfsi", @import("fixunsdfsi.zig").__fixunsdfsi, linkage); - @export("__fixunsdfdi", @import("fixunsdfdi.zig").__fixunsdfdi, linkage); - @export("__fixunsdfti", @import("fixunsdfti.zig").__fixunsdfti, linkage); - - @export("__fixunstfsi", @import("fixunstfsi.zig").__fixunstfsi, linkage); - @export("__fixunstfdi", @import("fixunstfdi.zig").__fixunstfdi, linkage); - @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage); - - @export("__fixdfdi", @import("fixdfdi.zig").__fixdfdi, linkage); - @export("__fixdfsi", @import("fixdfsi.zig").__fixdfsi, linkage); - @export("__fixdfti", @import("fixdfti.zig").__fixdfti, linkage); - @export("__fixsfdi", @import("fixsfdi.zig").__fixsfdi, linkage); - @export("__fixsfsi", @import("fixsfsi.zig").__fixsfsi, linkage); - @export("__fixsfti", @import("fixsfti.zig").__fixsfti, linkage); - @export("__fixtfdi", @import("fixtfdi.zig").__fixtfdi, linkage); - @export("__fixtfsi", @import("fixtfsi.zig").__fixtfsi, linkage); - @export("__fixtfti", @import("fixtfti.zig").__fixtfti, linkage); - - @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage); - - @export("__udivsi3", __udivsi3, linkage); - @export("__udivdi3", __udivdi3, linkage); - @export("__umoddi3", __umoddi3, linkage); - @export("__udivmodsi4", __udivmodsi4, linkage); - - if (is_arm_arch and !is_arm_64) { - @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); - @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); - @export("__aeabi_uidiv", __udivsi3, linkage); - } - if (builtin.os == builtin.Os.windows) { - switch (builtin.arch) { - builtin.Arch.i386 => { - if (!builtin.link_libc) { - @export("_chkstk", _chkstk, strong_linkage); - @export("__chkstk_ms", __chkstk_ms, linkage); - } - @export("_aulldiv", @import("aulldiv.zig")._aulldiv, strong_linkage); - @export("_aullrem", @import("aullrem.zig")._aullrem, strong_linkage); - }, - builtin.Arch.x86_64 => { - if (!builtin.link_libc) { - @export("__chkstk", __chkstk, strong_linkage); - @export("___chkstk_ms", ___chkstk_ms, linkage); - } - @export("__divti3", @import("divti3.zig").__divti3_windows_x86_64, linkage); - @export("__multi3", @import("multi3.zig").__multi3_windows_x86_64, linkage); - @export("__muloti4", @import("muloti4.zig").__muloti4_windows_x86_64, linkage); - @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage); - @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); - @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage); - }, - else => {}, - } - } else { - @export("__divti3", @import("divti3.zig").__divti3, linkage); - @export("__multi3", @import("multi3.zig").__multi3, linkage); - @export("__muloti4", @import("muloti4.zig").__muloti4, linkage); - @export("__udivti3", @import("udivti3.zig").__udivti3, linkage); - @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); - @export("__umodti3", @import("umodti3.zig").__umodti3, linkage); - } -} - -const std = @import("std"); -const assert = std.debug.assert; -const testing = std.testing; - -const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; - -// Avoid dragging in the runtime safety mechanisms into this .o file, -// unless we're trying to test this file. -pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { - @setCold(true); - if (is_test) { - std.debug.panic("{}", msg); - } else { - unreachable; - } -} - -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 __udivdi3(a: u64, b: u64) u64 { - @setRuntimeSafety(is_test); - return __udivmoddi4(a, b, null); -} - -extern fn __umoddi3(a: u64, b: u64) u64 { - @setRuntimeSafety(is_test); - - var r: u64 = undefined; - _ = __udivmoddi4(a, b, &r); - return r; -} - -const AeabiUlDivModResult = extern struct { - quot: u64, - rem: u64, -}; -extern fn __aeabi_uldivmod(numerator: u64, denominator: u64) AeabiUlDivModResult { - @setRuntimeSafety(is_test); - var result: AeabiUlDivModResult = undefined; - result.quot = __udivmoddi4(numerator, denominator, &result.rem); - return result; -} - -const is_arm_64 = switch (builtin.arch) { - builtin.Arch.aarch64v8_3a, - builtin.Arch.aarch64v8_2a, - builtin.Arch.aarch64v8_1a, - builtin.Arch.aarch64v8, - builtin.Arch.aarch64v8r, - builtin.Arch.aarch64v8m_baseline, - builtin.Arch.aarch64v8m_mainline, - builtin.Arch.aarch64_bev8_3a, - builtin.Arch.aarch64_bev8_2a, - builtin.Arch.aarch64_bev8_1a, - builtin.Arch.aarch64_bev8, - builtin.Arch.aarch64_bev8r, - builtin.Arch.aarch64_bev8m_baseline, - builtin.Arch.aarch64_bev8m_mainline, - => true, - else => false, -}; - -const is_arm_arch = switch (builtin.arch) { - builtin.Arch.arm, - builtin.Arch.armeb, - builtin.Arch.aarch64, - builtin.Arch.aarch64_be, - builtin.Arch.thumb, - builtin.Arch.thumbeb, - => true, - 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" - ); -} - -// _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); - - 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 - ); -} - -extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { - @setRuntimeSafety(is_test); - - const d = __udivsi3(a, b); - rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); - return d; -} - -extern fn __udivsi3(n: u32, d: u32) u32 { - @setRuntimeSafety(is_test); - - const n_uword_bits: c_uint = u32.bit_count; - // special cases - if (d == 0) return 0; // ?! - if (n == 0) return 0; - var sr = @bitCast(c_uint, c_int(@clz(d)) - c_int(@clz(n))); - // 0 <= sr <= n_uword_bits - 1 or sr large - if (sr > n_uword_bits - 1) { - // d > r - return 0; - } - if (sr == n_uword_bits - 1) { - // d == 1 - return n; - } - sr += 1; - // 1 <= sr <= n_uword_bits - 1 - // Not a special case - var q: u32 = n << @intCast(u5, n_uword_bits - sr); - var r: u32 = n >> @intCast(u5, sr); - var carry: u32 = 0; - while (sr > 0) : (sr -= 1) { - // r:q = ((r:q) << 1) | carry - r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); - q = (q << 1) | carry; - // carry = 0; - // if (r.all >= d.all) - // { - // r.all -= d.all; - // carry = 1; - // } - const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); - carry = @intCast(u32, s & 1); - r -= d & @bitCast(u32, s); - } - q = (q << 1) | carry; - return q; -} - -test "test_umoddi3" { - test_one_umoddi3(0, 1, 0); - test_one_umoddi3(2, 1, 0); - test_one_umoddi3(0x8000000000000000, 1, 0x0); - test_one_umoddi3(0x8000000000000000, 2, 0x0); - test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); -} - -fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { - const r = __umoddi3(a, b); - testing.expect(r == expected_r); -} - -test "test_udivsi3" { - 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, - 0x00000001, - }, - []u32{ - 0x00000001, - 0x00000002, - 0x00000000, - }, - []u32{ - 0x00000001, - 0x00000003, - 0x00000000, - }, - []u32{ - 0x00000001, - 0x00000010, - 0x00000000, - }, - []u32{ - 0x00000001, - 0x078644FA, - 0x00000000, - }, - []u32{ - 0x00000001, - 0x0747AE14, - 0x00000000, - }, - []u32{ - 0x00000001, - 0x7FFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000001, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x00000001, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x00000001, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x00000001, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000002, - 0x00000001, - 0x00000002, - }, - []u32{ - 0x00000002, - 0x00000002, - 0x00000001, - }, - []u32{ - 0x00000002, - 0x00000003, - 0x00000000, - }, - []u32{ - 0x00000002, - 0x00000010, - 0x00000000, - }, - []u32{ - 0x00000002, - 0x078644FA, - 0x00000000, - }, - []u32{ - 0x00000002, - 0x0747AE14, - 0x00000000, - }, - []u32{ - 0x00000002, - 0x7FFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000002, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x00000002, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x00000002, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x00000002, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000003, - 0x00000001, - 0x00000003, - }, - []u32{ - 0x00000003, - 0x00000002, - 0x00000001, - }, - []u32{ - 0x00000003, - 0x00000003, - 0x00000001, - }, - []u32{ - 0x00000003, - 0x00000010, - 0x00000000, - }, - []u32{ - 0x00000003, - 0x078644FA, - 0x00000000, - }, - []u32{ - 0x00000003, - 0x0747AE14, - 0x00000000, - }, - []u32{ - 0x00000003, - 0x7FFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000003, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x00000003, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x00000003, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x00000003, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000010, - 0x00000001, - 0x00000010, - }, - []u32{ - 0x00000010, - 0x00000002, - 0x00000008, - }, - []u32{ - 0x00000010, - 0x00000003, - 0x00000005, - }, - []u32{ - 0x00000010, - 0x00000010, - 0x00000001, - }, - []u32{ - 0x00000010, - 0x078644FA, - 0x00000000, - }, - []u32{ - 0x00000010, - 0x0747AE14, - 0x00000000, - }, - []u32{ - 0x00000010, - 0x7FFFFFFF, - 0x00000000, - }, - []u32{ - 0x00000010, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x00000010, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x00000010, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x00000010, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x078644FA, - 0x00000001, - 0x078644FA, - }, - []u32{ - 0x078644FA, - 0x00000002, - 0x03C3227D, - }, - []u32{ - 0x078644FA, - 0x00000003, - 0x028216FE, - }, - []u32{ - 0x078644FA, - 0x00000010, - 0x0078644F, - }, - []u32{ - 0x078644FA, - 0x078644FA, - 0x00000001, - }, - []u32{ - 0x078644FA, - 0x0747AE14, - 0x00000001, - }, - []u32{ - 0x078644FA, - 0x7FFFFFFF, - 0x00000000, - }, - []u32{ - 0x078644FA, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x078644FA, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x078644FA, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x078644FA, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x0747AE14, - 0x00000001, - 0x0747AE14, - }, - []u32{ - 0x0747AE14, - 0x00000002, - 0x03A3D70A, - }, - []u32{ - 0x0747AE14, - 0x00000003, - 0x026D3A06, - }, - []u32{ - 0x0747AE14, - 0x00000010, - 0x00747AE1, - }, - []u32{ - 0x0747AE14, - 0x078644FA, - 0x00000000, - }, - []u32{ - 0x0747AE14, - 0x0747AE14, - 0x00000001, - }, - []u32{ - 0x0747AE14, - 0x7FFFFFFF, - 0x00000000, - }, - []u32{ - 0x0747AE14, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x0747AE14, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x0747AE14, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x0747AE14, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x7FFFFFFF, - 0x00000001, - 0x7FFFFFFF, - }, - []u32{ - 0x7FFFFFFF, - 0x00000002, - 0x3FFFFFFF, - }, - []u32{ - 0x7FFFFFFF, - 0x00000003, - 0x2AAAAAAA, - }, - []u32{ - 0x7FFFFFFF, - 0x00000010, - 0x07FFFFFF, - }, - []u32{ - 0x7FFFFFFF, - 0x078644FA, - 0x00000011, - }, - []u32{ - 0x7FFFFFFF, - 0x0747AE14, - 0x00000011, - }, - []u32{ - 0x7FFFFFFF, - 0x7FFFFFFF, - 0x00000001, - }, - []u32{ - 0x7FFFFFFF, - 0x80000000, - 0x00000000, - }, - []u32{ - 0x7FFFFFFF, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x7FFFFFFF, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x7FFFFFFF, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0x80000000, - 0x00000001, - 0x80000000, - }, - []u32{ - 0x80000000, - 0x00000002, - 0x40000000, - }, - []u32{ - 0x80000000, - 0x00000003, - 0x2AAAAAAA, - }, - []u32{ - 0x80000000, - 0x00000010, - 0x08000000, - }, - []u32{ - 0x80000000, - 0x078644FA, - 0x00000011, - }, - []u32{ - 0x80000000, - 0x0747AE14, - 0x00000011, - }, - []u32{ - 0x80000000, - 0x7FFFFFFF, - 0x00000001, - }, - []u32{ - 0x80000000, - 0x80000000, - 0x00000001, - }, - []u32{ - 0x80000000, - 0xFFFFFFFD, - 0x00000000, - }, - []u32{ - 0x80000000, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0x80000000, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0xFFFFFFFD, - 0x00000001, - 0xFFFFFFFD, - }, - []u32{ - 0xFFFFFFFD, - 0x00000002, - 0x7FFFFFFE, - }, - []u32{ - 0xFFFFFFFD, - 0x00000003, - 0x55555554, - }, - []u32{ - 0xFFFFFFFD, - 0x00000010, - 0x0FFFFFFF, - }, - []u32{ - 0xFFFFFFFD, - 0x078644FA, - 0x00000022, - }, - []u32{ - 0xFFFFFFFD, - 0x0747AE14, - 0x00000023, - }, - []u32{ - 0xFFFFFFFD, - 0x7FFFFFFF, - 0x00000001, - }, - []u32{ - 0xFFFFFFFD, - 0x80000000, - 0x00000001, - }, - []u32{ - 0xFFFFFFFD, - 0xFFFFFFFD, - 0x00000001, - }, - []u32{ - 0xFFFFFFFD, - 0xFFFFFFFE, - 0x00000000, - }, - []u32{ - 0xFFFFFFFD, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0xFFFFFFFE, - 0x00000001, - 0xFFFFFFFE, - }, - []u32{ - 0xFFFFFFFE, - 0x00000002, - 0x7FFFFFFF, - }, - []u32{ - 0xFFFFFFFE, - 0x00000003, - 0x55555554, - }, - []u32{ - 0xFFFFFFFE, - 0x00000010, - 0x0FFFFFFF, - }, - []u32{ - 0xFFFFFFFE, - 0x078644FA, - 0x00000022, - }, - []u32{ - 0xFFFFFFFE, - 0x0747AE14, - 0x00000023, - }, - []u32{ - 0xFFFFFFFE, - 0x7FFFFFFF, - 0x00000002, - }, - []u32{ - 0xFFFFFFFE, - 0x80000000, - 0x00000001, - }, - []u32{ - 0xFFFFFFFE, - 0xFFFFFFFD, - 0x00000001, - }, - []u32{ - 0xFFFFFFFE, - 0xFFFFFFFE, - 0x00000001, - }, - []u32{ - 0xFFFFFFFE, - 0xFFFFFFFF, - 0x00000000, - }, - []u32{ - 0xFFFFFFFF, - 0x00000001, - 0xFFFFFFFF, - }, - []u32{ - 0xFFFFFFFF, - 0x00000002, - 0x7FFFFFFF, - }, - []u32{ - 0xFFFFFFFF, - 0x00000003, - 0x55555555, - }, - []u32{ - 0xFFFFFFFF, - 0x00000010, - 0x0FFFFFFF, - }, - []u32{ - 0xFFFFFFFF, - 0x078644FA, - 0x00000022, - }, - []u32{ - 0xFFFFFFFF, - 0x0747AE14, - 0x00000023, - }, - []u32{ - 0xFFFFFFFF, - 0x7FFFFFFF, - 0x00000002, - }, - []u32{ - 0xFFFFFFFF, - 0x80000000, - 0x00000001, - }, - []u32{ - 0xFFFFFFFF, - 0xFFFFFFFD, - 0x00000001, - }, - []u32{ - 0xFFFFFFFF, - 0xFFFFFFFE, - 0x00000001, - }, - []u32{ - 0xFFFFFFFF, - 0xFFFFFFFF, - 0x00000001, - }, - }; - - for (cases) |case| { - test_one_udivsi3(case[0], case[1], case[2]); - } -} - -fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { - const q: u32 = __udivsi3(a, b); - testing.expect(q == expected_q); -} diff --git a/std/special/compiler_rt/muloti4.zig b/std/special/compiler_rt/muloti4.zig index 866077c80c..fd6855072b 100644 --- a/std/special/compiler_rt/muloti4.zig +++ b/std/special/compiler_rt/muloti4.zig @@ -1,6 +1,6 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); -const compiler_rt = @import("index.zig"); +const compiler_rt = @import("../compiler_rt.zig"); pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 { @setRuntimeSafety(builtin.is_test); diff --git a/std/special/compiler_rt/multi3.zig b/std/special/compiler_rt/multi3.zig index 1152a8bda0..a0c84adaf4 100644 --- a/std/special/compiler_rt/multi3.zig +++ b/std/special/compiler_rt/multi3.zig @@ -1,5 +1,5 @@ const builtin = @import("builtin"); -const compiler_rt = @import("index.zig"); +const compiler_rt = @import("../compiler_rt.zig"); // Ported from git@github.com:llvm-project/llvm-project-20170507.git // ae684fad6d34858c014c94da69c15e7774a633c3 diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig index 3fa596442f..6a037d3bae 100644 --- a/std/special/compiler_rt/udivmodti4.zig +++ b/std/special/compiler_rt/udivmodti4.zig @@ -1,6 +1,6 @@ const udivmod = @import("udivmod.zig").udivmod; const builtin = @import("builtin"); -const compiler_rt = @import("index.zig"); +const compiler_rt = @import("../compiler_rt.zig"); pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { @setRuntimeSafety(builtin.is_test); diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig index 9551e63a6f..12aca8b036 100644 --- a/std/special/compiler_rt/umodti3.zig +++ b/std/special/compiler_rt/umodti3.zig @@ -1,6 +1,6 @@ const udivmodti4 = @import("udivmodti4.zig"); const builtin = @import("builtin"); -const compiler_rt = @import("index.zig"); +const compiler_rt = @import("../compiler_rt.zig"); pub extern fn __umodti3(a: u128, b: u128) u128 { @setRuntimeSafety(builtin.is_test); diff --git a/std/spinlock.zig b/std/spinlock.zig index 75fdf9f6e1..905460a2d0 100644 --- a/std/spinlock.zig +++ b/std/spinlock.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; const AtomicRmwOp = builtin.AtomicRmwOp; diff --git a/std/statically_initialized_mutex.zig b/std/statically_initialized_mutex.zig index 16bcd7adaf..cfcaf036d3 100644 --- a/std/statically_initialized_mutex.zig +++ b/std/statically_initialized_mutex.zig @@ -1,4 +1,4 @@ -const std = @import("index.zig"); +const std = @import("std.zig"); const builtin = @import("builtin"); const AtomicOrder = builtin.AtomicOrder; const AtomicRmwOp = builtin.AtomicRmwOp; @@ -13,7 +13,7 @@ const windows = std.os.windows; /// On Windows, this mutex allocates resources when it is /// first used, and the resources cannot be freed. /// On Linux, this is an alias of std.Mutex. -pub const StaticallyInitializedMutex = switch(builtin.os) { +pub const StaticallyInitializedMutex = switch (builtin.os) { builtin.Os.linux => std.Mutex, builtin.Os.windows => struct { lock: windows.CRITICAL_SECTION, @@ -28,7 +28,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { }; pub fn init() StaticallyInitializedMutex { - return StaticallyInitializedMutex { + return StaticallyInitializedMutex{ .lock = undefined, .init_once = windows.INIT_ONCE_STATIC_INIT, }; @@ -54,7 +54,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { pub fn acquire(self: *StaticallyInitializedMutex) Held { assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); windows.EnterCriticalSection(&self.lock); - return Held { .mutex = self }; + return Held{ .mutex = self }; } }, else => std.Mutex, @@ -89,10 +89,7 @@ test "std.StaticallyInitializedMutex" { var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); var a = &fixed_buffer_allocator.allocator; - - var context = TestContext{ - .data = 0, - }; + var context = TestContext{ .data = 0 }; if (builtin.single_threaded) { TestContext.worker(&context); diff --git a/std/std.zig b/std/std.zig new file mode 100644 index 0000000000..4a81fc7c35 --- /dev/null +++ b/std/std.zig @@ -0,0 +1,95 @@ +pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList; +pub const ArrayList = @import("array_list.zig").ArrayList; +pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; +pub const BufMap = @import("buf_map.zig").BufMap; +pub const BufSet = @import("buf_set.zig").BufSet; +pub const Buffer = @import("buffer.zig").Buffer; +pub const BufferOutStream = @import("io.zig").BufferOutStream; +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 PriorityQueue = @import("priority_queue.zig").PriorityQueue; +pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; +pub const SegmentedList = @import("segmented_list.zig").SegmentedList; +pub const SpinLock = @import("spinlock.zig").SpinLock; + +pub const atomic = @import("atomic.zig"); +pub const base64 = @import("base64.zig"); +pub const build = @import("build.zig"); +pub const c = @import("c.zig"); +pub const coff = @import("coff.zig"); +pub const crypto = @import("crypto.zig"); +pub const cstr = @import("cstr.zig"); +pub const debug = @import("debug.zig"); +pub const dwarf = @import("dwarf.zig"); +pub const elf = @import("elf.zig"); +pub const empty_import = @import("empty.zig"); +pub const event = @import("event.zig"); +pub const fmt = @import("fmt.zig"); +pub const hash = @import("hash.zig"); +pub const hash_map = @import("hash_map.zig"); +pub const heap = @import("heap.zig"); +pub const io = @import("io.zig"); +pub const json = @import("json.zig"); +pub const lazyInit = @import("lazy_init.zig").lazyInit; +pub const macho = @import("macho.zig"); +pub const math = @import("math.zig"); +pub const mem = @import("mem.zig"); +pub const meta = @import("meta.zig"); +pub const net = @import("net.zig"); +pub const os = @import("os.zig"); +pub const pdb = @import("pdb.zig"); +pub const rand = @import("rand.zig"); +pub const rb = @import("rb.zig"); +pub const sort = @import("sort.zig"); +pub const testing = @import("testing.zig"); +pub const unicode = @import("unicode.zig"); +pub const zig = @import("zig.zig"); + +test "std" { + // run tests from these + _ = @import("array_list.zig"); + _ = @import("atomic.zig"); + _ = @import("buf_map.zig"); + _ = @import("buf_set.zig"); + _ = @import("buffer.zig"); + _ = @import("hash_map.zig"); + _ = @import("linked_list.zig"); + _ = @import("mutex.zig"); + _ = @import("statically_initialized_mutex.zig"); + _ = @import("segmented_list.zig"); + _ = @import("spinlock.zig"); + + _ = @import("base64.zig"); + _ = @import("build.zig"); + _ = @import("c.zig"); + _ = @import("coff.zig"); + _ = @import("crypto.zig"); + _ = @import("cstr.zig"); + _ = @import("debug.zig"); + _ = @import("dwarf.zig"); + _ = @import("dynamic_library.zig"); + _ = @import("elf.zig"); + _ = @import("empty.zig"); + _ = @import("event.zig"); + _ = @import("fmt.zig"); + _ = @import("hash.zig"); + _ = @import("heap.zig"); + _ = @import("io.zig"); + _ = @import("json.zig"); + _ = @import("lazy_init.zig"); + _ = @import("macho.zig"); + _ = @import("math.zig"); + _ = @import("mem.zig"); + _ = @import("meta.zig"); + _ = @import("net.zig"); + _ = @import("os.zig"); + _ = @import("pdb.zig"); + _ = @import("priority_queue.zig"); + _ = @import("rand.zig"); + _ = @import("sort.zig"); + _ = @import("testing.zig"); + _ = @import("unicode.zig"); + _ = @import("zig.zig"); +} diff --git a/std/testing.zig b/std/testing.zig index 359a787960..be414181f2 100644 --- a/std/testing.zig +++ b/std/testing.zig @@ -1,6 +1,6 @@ const builtin = @import("builtin"); const TypeId = builtin.TypeId; -const std = @import("index.zig"); +const std = @import("std.zig"); /// This function is intended to be used only in tests. It prints diagnostics to stderr /// and then aborts when actual_error_union is not expected_error. diff --git a/std/unicode.zig b/std/unicode.zig index fccdf513b9..37a73d7500 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -1,4 +1,4 @@ -const std = @import("./index.zig"); +const std = @import("./std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const testing = std.testing; diff --git a/std/zig.zig b/std/zig.zig new file mode 100644 index 0000000000..2d4978a4ae --- /dev/null +++ b/std/zig.zig @@ -0,0 +1,16 @@ +const tokenizer = @import("zig/tokenizer.zig"); +pub const Token = tokenizer.Token; +pub const Tokenizer = tokenizer.Tokenizer; +pub const parse = @import("zig/parse.zig").parse; +pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStringLiteral; +pub const render = @import("zig/render.zig").render; +pub const ast = @import("zig/ast.zig"); + +test "std.zig tests" { + _ = @import("zig/ast.zig"); + _ = @import("zig/parse.zig"); + _ = @import("zig/render.zig"); + _ = @import("zig/tokenizer.zig"); + _ = @import("zig/parse_string_literal.zig"); +} + diff --git a/std/zig/ast.zig b/std/zig/ast.zig index d33e30801b..421c657619 100644 --- a/std/zig/ast.zig +++ b/std/zig/ast.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; const testing = std.testing; const SegmentedList = std.SegmentedList; diff --git a/std/zig/bench.zig b/std/zig/bench.zig index 6a9e8495bd..ed6ae9a128 100644 --- a/std/zig/bench.zig +++ b/std/zig/bench.zig @@ -5,7 +5,7 @@ const Tokenizer = std.zig.Tokenizer; const Parser = std.zig.Parser; const io = std.io; -const source = @embedFile("../os/index.zig"); +const source = @embedFile("../os.zig"); var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; pub fn main() !void { diff --git a/std/zig/index.zig b/std/zig/index.zig deleted file mode 100644 index da84bc5bb0..0000000000 --- a/std/zig/index.zig +++ /dev/null @@ -1,16 +0,0 @@ -const tokenizer = @import("tokenizer.zig"); -pub const Token = tokenizer.Token; -pub const Tokenizer = tokenizer.Tokenizer; -pub const parse = @import("parse.zig").parse; -pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral; -pub const render = @import("render.zig").render; -pub const ast = @import("ast.zig"); - -test "std.zig tests" { - _ = @import("ast.zig"); - _ = @import("parse.zig"); - _ = @import("render.zig"); - _ = @import("tokenizer.zig"); - _ = @import("parse_string_literal.zig"); -} - diff --git a/std/zig/parse.zig b/std/zig/parse.zig index 867dd11592..e957d1d3e1 100644 --- a/std/zig/parse.zig +++ b/std/zig/parse.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; const mem = std.mem; const ast = std.zig.ast; diff --git a/std/zig/parse_string_literal.zig b/std/zig/parse_string_literal.zig index 00c92a7651..acae0b64c7 100644 --- a/std/zig/parse_string_literal.zig +++ b/std/zig/parse_string_literal.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const assert = std.debug.assert; const State = enum { diff --git a/std/zig/render.zig b/std/zig/render.zig index 1abc845b7c..50e928c0d8 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const builtin = @import("builtin"); const assert = std.debug.assert; const mem = std.mem; diff --git a/std/zig/tokenizer.zig b/std/zig/tokenizer.zig index 877e4e8db3..79fd1c5612 100644 --- a/std/zig/tokenizer.zig +++ b/std/zig/tokenizer.zig @@ -1,4 +1,4 @@ -const std = @import("../index.zig"); +const std = @import("../std.zig"); const mem = std.mem; pub const Token = struct { diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index c232003db7..9940a76af1 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -56,14 +56,14 @@ comptime { _ = @import("behavior/math.zig"); _ = @import("behavior/merge_error_sets.zig"); _ = @import("behavior/misc.zig"); - _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); + _ = @import("behavior/namespace_depends_on_compile_var.zig"); _ = @import("behavior/new_stack_call.zig"); _ = @import("behavior/null.zig"); _ = @import("behavior/optional.zig"); _ = @import("behavior/pointers.zig"); _ = @import("behavior/popcount.zig"); _ = @import("behavior/ptrcast.zig"); - _ = @import("behavior/pub_enum/index.zig"); + _ = @import("behavior/pub_enum.zig"); _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); _ = @import("behavior/reflection.zig"); _ = @import("behavior/sizeof_and_typeof.zig"); diff --git a/test/stage1/behavior/namespace_depends_on_compile_var.zig b/test/stage1/behavior/namespace_depends_on_compile_var.zig new file mode 100644 index 0000000000..4c4fc4eefe --- /dev/null +++ b/test/stage1/behavior/namespace_depends_on_compile_var.zig @@ -0,0 +1,14 @@ +const builtin = @import("builtin"); +const expect = @import("std").testing.expect; + +test "namespace depends on compile var" { + if (some_namespace.a_bool) { + expect(some_namespace.a_bool); + } else { + expect(!some_namespace.a_bool); + } +} +const some_namespace = switch (builtin.os) { + builtin.Os.linux => @import("namespace_depends_on_compile_var/a.zig"), + else => @import("namespace_depends_on_compile_var/b.zig"), +}; diff --git a/test/stage1/behavior/namespace_depends_on_compile_var/index.zig b/test/stage1/behavior/namespace_depends_on_compile_var/index.zig deleted file mode 100644 index 32feaced10..0000000000 --- a/test/stage1/behavior/namespace_depends_on_compile_var/index.zig +++ /dev/null @@ -1,14 +0,0 @@ -const builtin = @import("builtin"); -const expect = @import("std").testing.expect; - -test "namespace depends on compile var" { - if (some_namespace.a_bool) { - expect(some_namespace.a_bool); - } else { - expect(!some_namespace.a_bool); - } -} -const some_namespace = switch (builtin.os) { - builtin.Os.linux => @import("a.zig"), - else => @import("b.zig"), -}; diff --git a/test/stage1/behavior/pub_enum.zig b/test/stage1/behavior/pub_enum.zig new file mode 100644 index 0000000000..6275be7a01 --- /dev/null +++ b/test/stage1/behavior/pub_enum.zig @@ -0,0 +1,13 @@ +const other = @import("pub_enum/other.zig"); +const expect = @import("std").testing.expect; + +test "pub enum" { + pubEnumTest(other.APubEnum.Two); +} +fn pubEnumTest(foo: other.APubEnum) void { + expect(foo == other.APubEnum.Two); +} + +test "cast with imported symbol" { + expect(other.size_t(42) == 42); +} diff --git a/test/stage1/behavior/pub_enum/index.zig b/test/stage1/behavior/pub_enum/index.zig deleted file mode 100644 index 15e4114c2d..0000000000 --- a/test/stage1/behavior/pub_enum/index.zig +++ /dev/null @@ -1,13 +0,0 @@ -const other = @import("other.zig"); -const expect = @import("std").testing.expect; - -test "pub enum" { - pubEnumTest(other.APubEnum.Two); -} -fn pubEnumTest(foo: other.APubEnum) void { - expect(foo == other.APubEnum.Two); -} - -test "cast with imported symbol" { - expect(other.size_t(42) == 42); -} -- cgit v1.2.3