| author | |
| committer | |
| log | e40245570422c137f7239f411128973cc217389e |
| tree | 93ab6cef5d0744ca93aaf9fc768608f7ce8a26f3 |
| parent | f7835000b62fb5b501f1d84c90f56e2aa11bc55a |
| signature |
216 files changed, 15402 insertions(+), 15420 deletions(-)
.builds/freebsd.yml+8-8| ... | @@ -13,28 +13,28 @@ tasks: | ... | @@ -13,28 +13,28 @@ tasks: |
| 13 | - test: | | 13 | - test: | |
| 14 | cd zig/build | 14 | cd zig/build |
| 15 | bin/zig test ../test/stage1/behavior.zig | 15 | bin/zig test ../test/stage1/behavior.zig |
| 16 | bin/zig test ../std/special/compiler_rt/index.zig | 16 | bin/zig test ../std/special/compiler_rt.zig |
| 17 | 17 | ||
| 18 | bin/zig test ../test/stage1/behavior.zig --library c | 18 | bin/zig test ../test/stage1/behavior.zig --library c |
| 19 | bin/zig test ../std/special/compiler_rt/index.zig --library c | 19 | bin/zig test ../std/special/compiler_rt.zig --library c |
| 20 | 20 | ||
| 21 | bin/zig test ../test/stage1/behavior.zig --release-fast | 21 | bin/zig test ../test/stage1/behavior.zig --release-fast |
| 22 | bin/zig test ../std/special/compiler_rt/index.zig --release-fast | 22 | bin/zig test ../std/special/compiler_rt.zig --release-fast |
| 23 | 23 | ||
| 24 | bin/zig test ../test/stage1/behavior.zig --release-fast --library c | 24 | bin/zig test ../test/stage1/behavior.zig --release-fast --library c |
| 25 | bin/zig test ../std/special/compiler_rt/index.zig --release-fast --library c | 25 | bin/zig test ../std/special/compiler_rt.zig --release-fast --library c |
| 26 | 26 | ||
| 27 | bin/zig test ../test/stage1/behavior.zig --release-small --library c | 27 | bin/zig test ../test/stage1/behavior.zig --release-small --library c |
| 28 | bin/zig test ../std/special/compiler_rt/index.zig --release-small --library c | 28 | bin/zig test ../std/special/compiler_rt.zig --release-small --library c |
| 29 | 29 | ||
| 30 | bin/zig test ../test/stage1/behavior.zig --release-small | 30 | bin/zig test ../test/stage1/behavior.zig --release-small |
| 31 | bin/zig test ../std/special/compiler_rt/index.zig --release-small | 31 | bin/zig test ../std/special/compiler_rt.zig --release-small |
| 32 | 32 | ||
| 33 | bin/zig test ../test/stage1/behavior.zig --release-safe | 33 | bin/zig test ../test/stage1/behavior.zig --release-safe |
| 34 | bin/zig test ../std/special/compiler_rt/index.zig --release-safe | 34 | bin/zig test ../std/special/compiler_rt.zig --release-safe |
| 35 | 35 | ||
| 36 | bin/zig test ../test/stage1/behavior.zig --release-safe --library c | 36 | bin/zig test ../test/stage1/behavior.zig --release-safe --library c |
| 37 | bin/zig test ../std/special/compiler_rt/index.zig --release-safe --library c | 37 | bin/zig test ../std/special/compiler_rt.zig --release-safe --library c |
| 38 | # TODO enable all tests | 38 | # TODO enable all tests |
| 39 | #bin/zig build --build-file ../build.zig test | 39 | #bin/zig build --build-file ../build.zig test |
| 40 | # TODO integrate with the download page updater and make a | 40 | # TODO integrate with the download page updater and make a |
CMakeLists.txt+20-20| ... | @@ -439,7 +439,7 @@ set(ZIG_CPP_SOURCES | ... | @@ -439,7 +439,7 @@ set(ZIG_CPP_SOURCES |
| 439 | 439 | ||
| 440 | set(ZIG_STD_FILES | 440 | set(ZIG_STD_FILES |
| 441 | "array_list.zig" | 441 | "array_list.zig" |
| 442 | "atomic/index.zig" | 442 | "atomic.zig" |
| 443 | "atomic/int.zig" | 443 | "atomic/int.zig" |
| 444 | "atomic/queue.zig" | 444 | "atomic/queue.zig" |
| 445 | "atomic/stack.zig" | 445 | "atomic/stack.zig" |
| ... | @@ -449,17 +449,17 @@ set(ZIG_STD_FILES | ... | @@ -449,17 +449,17 @@ set(ZIG_STD_FILES |
| 449 | "buffer.zig" | 449 | "buffer.zig" |
| 450 | "build.zig" | 450 | "build.zig" |
| 451 | "build/fmt.zig" | 451 | "build/fmt.zig" |
| 452 | "c.zig" | ||
| 452 | "c/darwin.zig" | 453 | "c/darwin.zig" |
| 453 | "c/freebsd.zig" | 454 | "c/freebsd.zig" |
| 454 | "c/index.zig" | ||
| 455 | "c/linux.zig" | 455 | "c/linux.zig" |
| 456 | "c/netbsd.zig" | 456 | "c/netbsd.zig" |
| 457 | "c/windows.zig" | 457 | "c/windows.zig" |
| 458 | "coff.zig" | 458 | "coff.zig" |
| 459 | "crypto.zig" | ||
| 459 | "crypto/blake2.zig" | 460 | "crypto/blake2.zig" |
| 460 | "crypto/chacha20.zig" | 461 | "crypto/chacha20.zig" |
| 461 | "crypto/hmac.zig" | 462 | "crypto/hmac.zig" |
| 462 | "crypto/index.zig" | ||
| 463 | "crypto/md5.zig" | 463 | "crypto/md5.zig" |
| 464 | "crypto/poly1305.zig" | 464 | "crypto/poly1305.zig" |
| 465 | "crypto/sha1.zig" | 465 | "crypto/sha1.zig" |
| ... | @@ -467,8 +467,8 @@ set(ZIG_STD_FILES | ... | @@ -467,8 +467,8 @@ set(ZIG_STD_FILES |
| 467 | "crypto/sha3.zig" | 467 | "crypto/sha3.zig" |
| 468 | "crypto/x25519.zig" | 468 | "crypto/x25519.zig" |
| 469 | "cstr.zig" | 469 | "cstr.zig" |
| 470 | "debug.zig" | ||
| 470 | "debug/failing_allocator.zig" | 471 | "debug/failing_allocator.zig" |
| 471 | "debug/index.zig" | ||
| 472 | "dwarf.zig" | 472 | "dwarf.zig" |
| 473 | "dynamic_library.zig" | 473 | "dynamic_library.zig" |
| 474 | "elf.zig" | 474 | "elf.zig" |
| ... | @@ -485,25 +485,25 @@ set(ZIG_STD_FILES | ... | @@ -485,25 +485,25 @@ set(ZIG_STD_FILES |
| 485 | "event/net.zig" | 485 | "event/net.zig" |
| 486 | "event/rwlock.zig" | 486 | "event/rwlock.zig" |
| 487 | "event/rwlocked.zig" | 487 | "event/rwlocked.zig" |
| 488 | "fmt.zig" | ||
| 489 | "fmt/errol.zig" | ||
| 488 | "fmt/errol/enum3.zig" | 490 | "fmt/errol/enum3.zig" |
| 489 | "fmt/errol/index.zig" | ||
| 490 | "fmt/errol/lookup.zig" | 491 | "fmt/errol/lookup.zig" |
| 491 | "fmt/index.zig" | ||
| 492 | "fmt/parse_float.zig" | 492 | "fmt/parse_float.zig" |
| 493 | "hash.zig" | ||
| 493 | "hash/adler.zig" | 494 | "hash/adler.zig" |
| 494 | "hash/crc.zig" | 495 | "hash/crc.zig" |
| 495 | "hash/fnv.zig" | 496 | "hash/fnv.zig" |
| 496 | "hash/index.zig" | ||
| 497 | "hash/siphash.zig" | 497 | "hash/siphash.zig" |
| 498 | "hash_map.zig" | 498 | "hash_map.zig" |
| 499 | "heap.zig" | 499 | "heap.zig" |
| 500 | "index.zig" | ||
| 501 | "io.zig" | 500 | "io.zig" |
| 502 | "io/seekable_stream.zig" | 501 | "io/seekable_stream.zig" |
| 503 | "json.zig" | 502 | "json.zig" |
| 504 | "lazy_init.zig" | 503 | "lazy_init.zig" |
| 505 | "linked_list.zig" | 504 | "linked_list.zig" |
| 506 | "macho.zig" | 505 | "macho.zig" |
| 506 | "math.zig" | ||
| 507 | "math/acos.zig" | 507 | "math/acos.zig" |
| 508 | "math/acosh.zig" | 508 | "math/acosh.zig" |
| 509 | "math/asin.zig" | 509 | "math/asin.zig" |
| ... | @@ -511,10 +511,11 @@ set(ZIG_STD_FILES | ... | @@ -511,10 +511,11 @@ set(ZIG_STD_FILES |
| 511 | "math/atan.zig" | 511 | "math/atan.zig" |
| 512 | "math/atan2.zig" | 512 | "math/atan2.zig" |
| 513 | "math/atanh.zig" | 513 | "math/atanh.zig" |
| 514 | "math/big/index.zig" | 514 | "math/big.zig" |
| 515 | "math/big/int.zig" | 515 | "math/big/int.zig" |
| 516 | "math/cbrt.zig" | 516 | "math/cbrt.zig" |
| 517 | "math/ceil.zig" | 517 | "math/ceil.zig" |
| 518 | "math/complex.zig" | ||
| 518 | "math/complex/abs.zig" | 519 | "math/complex/abs.zig" |
| 519 | "math/complex/acos.zig" | 520 | "math/complex/acos.zig" |
| 520 | "math/complex/acosh.zig" | 521 | "math/complex/acosh.zig" |
| ... | @@ -527,7 +528,6 @@ set(ZIG_STD_FILES | ... | @@ -527,7 +528,6 @@ set(ZIG_STD_FILES |
| 527 | "math/complex/cos.zig" | 528 | "math/complex/cos.zig" |
| 528 | "math/complex/cosh.zig" | 529 | "math/complex/cosh.zig" |
| 529 | "math/complex/exp.zig" | 530 | "math/complex/exp.zig" |
| 530 | "math/complex/index.zig" | ||
| 531 | "math/complex/ldexp.zig" | 531 | "math/complex/ldexp.zig" |
| 532 | "math/complex/log.zig" | 532 | "math/complex/log.zig" |
| 533 | "math/complex/pow.zig" | 533 | "math/complex/pow.zig" |
| ... | @@ -550,7 +550,6 @@ set(ZIG_STD_FILES | ... | @@ -550,7 +550,6 @@ set(ZIG_STD_FILES |
| 550 | "math/frexp.zig" | 550 | "math/frexp.zig" |
| 551 | "math/hypot.zig" | 551 | "math/hypot.zig" |
| 552 | "math/ilogb.zig" | 552 | "math/ilogb.zig" |
| 553 | "math/index.zig" | ||
| 554 | "math/inf.zig" | 553 | "math/inf.zig" |
| 555 | "math/isfinite.zig" | 554 | "math/isfinite.zig" |
| 556 | "math/isinf.zig" | 555 | "math/isinf.zig" |
| ... | @@ -575,33 +574,33 @@ set(ZIG_STD_FILES | ... | @@ -575,33 +574,33 @@ set(ZIG_STD_FILES |
| 575 | "math/tanh.zig" | 574 | "math/tanh.zig" |
| 576 | "math/trunc.zig" | 575 | "math/trunc.zig" |
| 577 | "mem.zig" | 576 | "mem.zig" |
| 578 | "meta/index.zig" | 577 | "meta.zig" |
| 579 | "meta/trait.zig" | 578 | "meta/trait.zig" |
| 580 | "mutex.zig" | 579 | "mutex.zig" |
| 581 | "net.zig" | 580 | "net.zig" |
| 581 | "os.zig" | ||
| 582 | "os/child_process.zig" | 582 | "os/child_process.zig" |
| 583 | "os/darwin.zig" | 583 | "os/darwin.zig" |
| 584 | "os/darwin/errno.zig" | 584 | "os/darwin/errno.zig" |
| 585 | "os/epoch.zig" | 585 | "os/epoch.zig" |
| 586 | "os/file.zig" | 586 | "os/file.zig" |
| 587 | "os/freebsd.zig" | ||
| 587 | "os/freebsd/errno.zig" | 588 | "os/freebsd/errno.zig" |
| 588 | "os/freebsd/index.zig" | ||
| 589 | "os/get_app_data_dir.zig" | 589 | "os/get_app_data_dir.zig" |
| 590 | "os/get_user_id.zig" | 590 | "os/get_user_id.zig" |
| 591 | "os/index.zig" | 591 | "os/linux.zig" |
| 592 | "os/linux/arm64.zig" | 592 | "os/linux/arm64.zig" |
| 593 | "os/linux/errno.zig" | 593 | "os/linux/errno.zig" |
| 594 | "os/linux/index.zig" | ||
| 595 | "os/linux/vdso.zig" | 594 | "os/linux/vdso.zig" |
| 596 | "os/linux/x86_64.zig" | 595 | "os/linux/x86_64.zig" |
| 596 | "os/netbsd.zig" | ||
| 597 | "os/netbsd/errno.zig" | 597 | "os/netbsd/errno.zig" |
| 598 | "os/netbsd/index.zig" | ||
| 599 | "os/path.zig" | 598 | "os/path.zig" |
| 600 | "os/time.zig" | 599 | "os/time.zig" |
| 601 | "os/uefi.zig" | 600 | "os/uefi.zig" |
| 601 | "os/windows.zig" | ||
| 602 | "os/windows/advapi32.zig" | 602 | "os/windows/advapi32.zig" |
| 603 | "os/windows/error.zig" | 603 | "os/windows/error.zig" |
| 604 | "os/windows/index.zig" | ||
| 605 | "os/windows/kernel32.zig" | 604 | "os/windows/kernel32.zig" |
| 606 | "os/windows/ntdll.zig" | 605 | "os/windows/ntdll.zig" |
| 607 | "os/windows/ole32.zig" | 606 | "os/windows/ole32.zig" |
| ... | @@ -610,7 +609,7 @@ set(ZIG_STD_FILES | ... | @@ -610,7 +609,7 @@ set(ZIG_STD_FILES |
| 610 | "os/zen.zig" | 609 | "os/zen.zig" |
| 611 | "pdb.zig" | 610 | "pdb.zig" |
| 612 | "priority_queue.zig" | 611 | "priority_queue.zig" |
| 613 | "rand/index.zig" | 612 | "rand.zig" |
| 614 | "rand/ziggurat.zig" | 613 | "rand/ziggurat.zig" |
| 615 | "segmented_list.zig" | 614 | "segmented_list.zig" |
| 616 | "sort.zig" | 615 | "sort.zig" |
| ... | @@ -619,6 +618,7 @@ set(ZIG_STD_FILES | ... | @@ -619,6 +618,7 @@ set(ZIG_STD_FILES |
| 619 | "special/bootstrap_windows_tls.zig" | 618 | "special/bootstrap_windows_tls.zig" |
| 620 | "special/build_runner.zig" | 619 | "special/build_runner.zig" |
| 621 | "special/builtin.zig" | 620 | "special/builtin.zig" |
| 621 | "special/compiler_rt.zig" | ||
| 622 | "special/compiler_rt/addXf3.zig" | 622 | "special/compiler_rt/addXf3.zig" |
| 623 | "special/compiler_rt/aulldiv.zig" | 623 | "special/compiler_rt/aulldiv.zig" |
| 624 | "special/compiler_rt/aullrem.zig" | 624 | "special/compiler_rt/aullrem.zig" |
| ... | @@ -653,7 +653,6 @@ set(ZIG_STD_FILES | ... | @@ -653,7 +653,6 @@ set(ZIG_STD_FILES |
| 653 | "special/compiler_rt/floatuntidf.zig" | 653 | "special/compiler_rt/floatuntidf.zig" |
| 654 | "special/compiler_rt/floatuntisf.zig" | 654 | "special/compiler_rt/floatuntisf.zig" |
| 655 | "special/compiler_rt/floatuntitf.zig" | 655 | "special/compiler_rt/floatuntitf.zig" |
| 656 | "special/compiler_rt/index.zig" | ||
| 657 | "special/compiler_rt/muloti4.zig" | 656 | "special/compiler_rt/muloti4.zig" |
| 658 | "special/compiler_rt/multi3.zig" | 657 | "special/compiler_rt/multi3.zig" |
| 659 | "special/compiler_rt/truncXfYf2.zig" | 658 | "special/compiler_rt/truncXfYf2.zig" |
| ... | @@ -671,10 +670,11 @@ set(ZIG_STD_FILES | ... | @@ -671,10 +670,11 @@ set(ZIG_STD_FILES |
| 671 | "special/test_runner.zig" | 670 | "special/test_runner.zig" |
| 672 | "spinlock.zig" | 671 | "spinlock.zig" |
| 673 | "statically_initialized_mutex.zig" | 672 | "statically_initialized_mutex.zig" |
| 673 | "std.zig" | ||
| 674 | "testing.zig" | 674 | "testing.zig" |
| 675 | "unicode.zig" | 675 | "unicode.zig" |
| 676 | "zig.zig" | ||
| 676 | "zig/ast.zig" | 677 | "zig/ast.zig" |
| 677 | "zig/index.zig" | ||
| 678 | "zig/parse.zig" | 678 | "zig/parse.zig" |
| 679 | "zig/parse_string_literal.zig" | 679 | "zig/parse_string_literal.zig" |
| 680 | "zig/render.zig" | 680 | "zig/render.zig" |
build.zig+2-2| ... | @@ -115,9 +115,9 @@ pub fn build(b: *Builder) !void { | ... | @@ -115,9 +115,9 @@ pub fn build(b: *Builder) !void { |
| 115 | 115 | ||
| 116 | test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); | 116 | test_step.dependOn(tests.addPkgTests(b, test_filter, "test/stage1/behavior.zig", "behavior", "Run the behavior tests", modes)); |
| 117 | 117 | ||
| 118 | test_step.dependOn(tests.addPkgTests(b, test_filter, "std/index.zig", "std", "Run the standard library tests", modes)); | 118 | test_step.dependOn(tests.addPkgTests(b, test_filter, "std/std.zig", "std", "Run the standard library tests", modes)); |
| 119 | 119 | ||
| 120 | test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", modes)); | 120 | test_step.dependOn(tests.addPkgTests(b, test_filter, "std/special/compiler_rt.zig", "compiler-rt", "Run the compiler_rt tests", modes)); |
| 121 | 121 | ||
| 122 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); | 122 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); |
| 123 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes)); | 123 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes)); |
src-self-hosted/compilation.zig+1-1| ... | @@ -533,7 +533,7 @@ pub const Compilation = struct { | ... | @@ -533,7 +533,7 @@ pub const Compilation = struct { |
| 533 | const basename = std.os.path.basename(root_src); | 533 | const basename = std.os.path.basename(root_src); |
| 534 | 534 | ||
| 535 | comp.root_package = try Package.create(comp.arena(), dirname, basename); | 535 | comp.root_package = try Package.create(comp.arena(), dirname, basename); |
| 536 | comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig"); | 536 | comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "std.zig"); |
| 537 | try comp.root_package.add("std", comp.std_package); | 537 | try comp.root_package.add("std", comp.std_package); |
| 538 | } else { | 538 | } else { |
| 539 | comp.root_package = try Package.create(comp.arena(), ".", ""); | 539 | comp.root_package = try Package.create(comp.arena(), ".", ""); |
src-self-hosted/introspect.zig+1-1| ... | @@ -11,7 +11,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![ | ... | @@ -11,7 +11,7 @@ pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![ |
| 11 | const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" }); | 11 | const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" }); |
| 12 | errdefer allocator.free(test_zig_dir); | 12 | errdefer allocator.free(test_zig_dir); |
| 13 | 13 | ||
| 14 | const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "index.zig" }); | 14 | const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "std.zig" }); |
| 15 | defer allocator.free(test_index_file); | 15 | defer allocator.free(test_index_file); |
| 16 | 16 | ||
| 17 | var file = try os.File.openRead(test_index_file); | 17 | var file = try os.File.openRead(test_index_file); |
src/codegen.cpp+1-1| ... | @@ -162,7 +162,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget | ... | @@ -162,7 +162,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); | 164 | g->root_package = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); |
| 165 | g->std_package = new_package(buf_ptr(g->zig_std_dir), "index.zig", "std"); | 165 | g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); |
| 166 | g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); | 166 | g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); |
| 167 | } else { | 167 | } else { |
| 168 | g->root_package = new_package(".", "", ""); | 168 | g->root_package = new_package(".", "", ""); |
src/compiler.cpp+3-3| ... | @@ -78,8 +78,8 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { | ... | @@ -78,8 +78,8 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { |
| 78 | Buf std_buf = BUF_INIT; | 78 | Buf std_buf = BUF_INIT; |
| 79 | buf_init_from_str(&std_buf, "std"); | 79 | buf_init_from_str(&std_buf, "std"); |
| 80 | 80 | ||
| 81 | Buf index_zig_buf = BUF_INIT; | 81 | Buf std_zig_buf = BUF_INIT; |
| 82 | buf_init_from_str(&index_zig_buf, "index.zig"); | 82 | buf_init_from_str(&std_zig_buf, "std.zig"); |
| 83 | 83 | ||
| 84 | Buf test_lib_dir = BUF_INIT; | 84 | Buf test_lib_dir = BUF_INIT; |
| 85 | Buf test_zig_dir = BUF_INIT; | 85 | Buf test_zig_dir = BUF_INIT; |
| ... | @@ -89,7 +89,7 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { | ... | @@ -89,7 +89,7 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { |
| 89 | os_path_join(test_path, &lib_buf, &test_lib_dir); | 89 | os_path_join(test_path, &lib_buf, &test_lib_dir); |
| 90 | os_path_join(&test_lib_dir, &zig_buf, &test_zig_dir); | 90 | os_path_join(&test_lib_dir, &zig_buf, &test_zig_dir); |
| 91 | os_path_join(&test_zig_dir, &std_buf, &test_std_dir); | 91 | os_path_join(&test_zig_dir, &std_buf, &test_std_dir); |
| 92 | os_path_join(&test_std_dir, &index_zig_buf, &test_index_file); | 92 | os_path_join(&test_std_dir, &std_zig_buf, &test_index_file); |
| 93 | 93 | ||
| 94 | int err; | 94 | int err; |
| 95 | bool exists; | 95 | bool exists; |
src/link.cpp+1-3| ... | @@ -87,10 +87,8 @@ static Buf *build_a(CodeGen *parent_gen, const char *aname) { | ... | @@ -87,10 +87,8 @@ static Buf *build_a(CodeGen *parent_gen, const char *aname) { |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | static Buf *build_compiler_rt(CodeGen *parent_gen) { | 89 | static Buf *build_compiler_rt(CodeGen *parent_gen) { |
| 90 | Buf *dir_path = buf_alloc(); | ||
| 91 | os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt"), dir_path); | ||
| 92 | Buf *full_path = buf_alloc(); | 90 | Buf *full_path = buf_alloc(); |
| 93 | os_path_join(dir_path, buf_create_from_str("index.zig"), full_path); | 91 | os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path); |
| 94 | 92 | ||
| 95 | return build_a_raw(parent_gen, "compiler_rt", full_path); | 93 | return build_a_raw(parent_gen, "compiler_rt", full_path); |
| 96 | } | 94 | } |
std/array_list.zig+2-2| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const assert = debug.assert; | 3 | const assert = debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
| ... | @@ -406,6 +406,6 @@ const Item = struct { | ... | @@ -406,6 +406,6 @@ const Item = struct { |
| 406 | 406 | ||
| 407 | test "std.ArrayList: ArrayList(T) of struct T" { | 407 | test "std.ArrayList: ArrayList(T) of struct T" { |
| 408 | var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(debug.global_allocator) }; | 408 | var root = Item{ .integer = 1, .sub_items = ArrayList(Item).init(debug.global_allocator) }; |
| 409 | try root.sub_items.append( Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) } ); | 409 | try root.sub_items.append(Item{ .integer = 42, .sub_items = ArrayList(Item).init(debug.global_allocator) }); |
| 410 | testing.expect(root.sub_items.items[0].integer == 42); | 410 | testing.expect(root.sub_items.items[0].integer == 42); |
| 411 | } | 411 | } |
std/atomic.zig created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | pub const Stack = @import("atomic/stack.zig").Stack; | ||
| 2 | pub const Queue = @import("atomic/queue.zig").Queue; | ||
| 3 | pub const Int = @import("atomic/int.zig").Int; | ||
| 4 | |||
| 5 | test "std.atomic" { | ||
| 6 | _ = @import("atomic/stack.zig"); | ||
| 7 | _ = @import("atomic/queue.zig"); | ||
| 8 | _ = @import("atomic/int.zig"); | ||
| 9 | } | ||
std/atomic/index.zig deleted-9| ... | @@ -1,9 +0,0 @@ | ||
| 1 | pub const Stack = @import("stack.zig").Stack; | ||
| 2 | pub const Queue = @import("queue.zig").Queue; | ||
| 3 | pub const Int = @import("int.zig").Int; | ||
| 4 | |||
| 5 | test "std.atomic" { | ||
| 6 | _ = @import("stack.zig"); | ||
| 7 | _ = @import("queue.zig"); | ||
| 8 | _ = @import("int.zig"); | ||
| 9 | } | ||
std/atomic/queue.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
std/atomic/stack.zig+1-1| ... | @@ -68,7 +68,7 @@ pub fn Stack(comptime T: type) type { | ... | @@ -68,7 +68,7 @@ pub fn Stack(comptime T: type) type { |
| 68 | }; | 68 | }; |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | const std = @import("../index.zig"); | 71 | const std = @import("../std.zig"); |
| 72 | const Context = struct { | 72 | const Context = struct { |
| 73 | allocator: *std.mem.Allocator, | 73 | allocator: *std.mem.Allocator, |
| 74 | stack: *Stack(i32), | 74 | stack: *Stack(i32), |
std/base64.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/buf_map.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const HashMap = std.HashMap; | 2 | const HashMap = std.HashMap; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const Allocator = mem.Allocator; | 4 | const Allocator = mem.Allocator; |
std/buf_set.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const HashMap = @import("hash_map.zig").HashMap; | 2 | const HashMap = @import("hash_map.zig").HashMap; |
| 3 | const mem = @import("mem.zig"); | 3 | const mem = @import("mem.zig"); |
| 4 | const Allocator = mem.Allocator; | 4 | const Allocator = mem.Allocator; |
std/buffer.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const Allocator = mem.Allocator; | 4 | const Allocator = mem.Allocator; |
std/build.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/build/fmt.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const build = @import("../build.zig"); | 2 | const build = @import("../build.zig"); |
| 3 | const Step = build.Step; | 3 | const Step = build.Step; |
| 4 | const Builder = build.Builder; | 4 | const Builder = build.Builder; |
std/c.zig created+68| ... | @@ -0,0 +1,68 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const Os = builtin.Os; | ||
| 3 | |||
| 4 | pub use switch (builtin.os) { | ||
| 5 | Os.linux => @import("c/linux.zig"), | ||
| 6 | Os.windows => @import("c/windows.zig"), | ||
| 7 | Os.macosx, Os.ios => @import("c/darwin.zig"), | ||
| 8 | Os.freebsd => @import("c/freebsd.zig"), | ||
| 9 | Os.netbsd => @import("c/netbsd.zig"), | ||
| 10 | else => empty_import, | ||
| 11 | }; | ||
| 12 | const empty_import = @import("empty.zig"); | ||
| 13 | |||
| 14 | // TODO https://github.com/ziglang/zig/issues/265 on this whole file | ||
| 15 | |||
| 16 | pub extern "c" fn abort() noreturn; | ||
| 17 | pub extern "c" fn exit(code: c_int) noreturn; | ||
| 18 | pub extern "c" fn isatty(fd: c_int) c_int; | ||
| 19 | pub extern "c" fn close(fd: c_int) c_int; | ||
| 20 | pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; | ||
| 21 | pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; | ||
| 22 | pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; | ||
| 23 | pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; | ||
| 24 | pub extern "c" fn raise(sig: c_int) c_int; | ||
| 25 | pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; | ||
| 26 | pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize; | ||
| 27 | pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; | ||
| 28 | pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; | ||
| 29 | pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize; | ||
| 30 | pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; | ||
| 31 | pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; | ||
| 32 | pub extern "c" fn unlink(path: [*]const u8) c_int; | ||
| 33 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | ||
| 34 | pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; | ||
| 35 | pub extern "c" fn fork() c_int; | ||
| 36 | pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; | ||
| 37 | pub extern "c" fn pipe(fds: *[2]c_int) c_int; | ||
| 38 | pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; | ||
| 39 | pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; | ||
| 40 | pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; | ||
| 41 | pub extern "c" fn chdir(path: [*]const u8) c_int; | ||
| 42 | pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; | ||
| 43 | pub extern "c" fn dup(fd: c_int) c_int; | ||
| 44 | pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; | ||
| 45 | pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; | ||
| 46 | pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; | ||
| 47 | pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; | ||
| 48 | pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; | ||
| 49 | pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; | ||
| 50 | pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; | ||
| 51 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; | ||
| 52 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; | ||
| 53 | pub extern "c" fn rmdir(path: [*]const u8) c_int; | ||
| 54 | |||
| 55 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; | ||
| 56 | pub extern "c" fn malloc(usize) ?*c_void; | ||
| 57 | pub extern "c" fn realloc(*c_void, usize) ?*c_void; | ||
| 58 | pub extern "c" fn free(*c_void) void; | ||
| 59 | pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; | ||
| 60 | |||
| 61 | 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; | ||
| 62 | pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; | ||
| 63 | pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; | ||
| 64 | pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; | ||
| 65 | pub extern "pthread" fn pthread_self() pthread_t; | ||
| 66 | pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; | ||
| 67 | |||
| 68 | pub const pthread_t = *@OpaqueType(); | ||
std/c/darwin.zig+1-1| ... | @@ -163,7 +163,7 @@ pub const Kevent = extern struct { | ... | @@ -163,7 +163,7 @@ pub const Kevent = extern struct { |
| 163 | // sys/types.h on macos uses #pragma pack(4) so these checks are | 163 | // sys/types.h on macos uses #pragma pack(4) so these checks are |
| 164 | // to make sure the struct is laid out the same. These values were | 164 | // to make sure the struct is laid out the same. These values were |
| 165 | // produced from C code using the offsetof macro. | 165 | // produced from C code using the offsetof macro. |
| 166 | const std = @import("../index.zig"); | 166 | const std = @import("../std.zig"); |
| 167 | const assert = std.debug.assert; | 167 | const assert = std.debug.assert; |
| 168 | 168 | ||
| 169 | comptime { | 169 | comptime { |
std/c/index.zig deleted-68| ... | @@ -1,68 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const Os = builtin.Os; | ||
| 3 | |||
| 4 | pub use switch (builtin.os) { | ||
| 5 | Os.linux => @import("linux.zig"), | ||
| 6 | Os.windows => @import("windows.zig"), | ||
| 7 | Os.macosx, Os.ios => @import("darwin.zig"), | ||
| 8 | Os.freebsd => @import("freebsd.zig"), | ||
| 9 | Os.netbsd => @import("netbsd.zig"), | ||
| 10 | else => empty_import, | ||
| 11 | }; | ||
| 12 | const empty_import = @import("../empty.zig"); | ||
| 13 | |||
| 14 | // TODO https://github.com/ziglang/zig/issues/265 on this whole file | ||
| 15 | |||
| 16 | pub extern "c" fn abort() noreturn; | ||
| 17 | pub extern "c" fn exit(code: c_int) noreturn; | ||
| 18 | pub extern "c" fn isatty(fd: c_int) c_int; | ||
| 19 | pub extern "c" fn close(fd: c_int) c_int; | ||
| 20 | pub extern "c" fn fstat(fd: c_int, buf: *Stat) c_int; | ||
| 21 | pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int; | ||
| 22 | pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; | ||
| 23 | pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; | ||
| 24 | pub extern "c" fn raise(sig: c_int) c_int; | ||
| 25 | pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize; | ||
| 26 | pub extern "c" fn pread(fd: c_int, buf: *c_void, nbyte: usize, offset: u64) isize; | ||
| 27 | pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; | ||
| 28 | pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize; | ||
| 29 | pub extern "c" fn pwrite(fd: c_int, buf: *const c_void, nbyte: usize, offset: u64) isize; | ||
| 30 | pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void; | ||
| 31 | pub extern "c" fn munmap(addr: *c_void, len: usize) c_int; | ||
| 32 | pub extern "c" fn unlink(path: [*]const u8) c_int; | ||
| 33 | pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; | ||
| 34 | pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; | ||
| 35 | pub extern "c" fn fork() c_int; | ||
| 36 | pub extern "c" fn access(path: [*]const u8, mode: c_uint) c_int; | ||
| 37 | pub extern "c" fn pipe(fds: *[2]c_int) c_int; | ||
| 38 | pub extern "c" fn mkdir(path: [*]const u8, mode: c_uint) c_int; | ||
| 39 | pub extern "c" fn symlink(existing: [*]const u8, new: [*]const u8) c_int; | ||
| 40 | pub extern "c" fn rename(old: [*]const u8, new: [*]const u8) c_int; | ||
| 41 | pub extern "c" fn chdir(path: [*]const u8) c_int; | ||
| 42 | pub extern "c" fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) c_int; | ||
| 43 | pub extern "c" fn dup(fd: c_int) c_int; | ||
| 44 | pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) c_int; | ||
| 45 | pub extern "c" fn readlink(noalias path: [*]const u8, noalias buf: [*]u8, bufsize: usize) isize; | ||
| 46 | pub extern "c" fn realpath(noalias file_name: [*]const u8, noalias resolved_name: [*]u8) ?[*]u8; | ||
| 47 | pub extern "c" fn sigprocmask(how: c_int, noalias set: *const sigset_t, noalias oset: ?*sigset_t) c_int; | ||
| 48 | pub extern "c" fn gettimeofday(noalias tv: ?*timeval, noalias tz: ?*timezone) c_int; | ||
| 49 | pub extern "c" fn sigaction(sig: c_int, noalias act: *const Sigaction, noalias oact: ?*Sigaction) c_int; | ||
| 50 | pub extern "c" fn nanosleep(rqtp: *const timespec, rmtp: ?*timespec) c_int; | ||
| 51 | pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int; | ||
| 52 | pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; | ||
| 53 | pub extern "c" fn rmdir(path: [*]const u8) c_int; | ||
| 54 | |||
| 55 | pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void; | ||
| 56 | pub extern "c" fn malloc(usize) ?*c_void; | ||
| 57 | pub extern "c" fn realloc(*c_void, usize) ?*c_void; | ||
| 58 | pub extern "c" fn free(*c_void) void; | ||
| 59 | pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int; | ||
| 60 | |||
| 61 | 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; | ||
| 62 | pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; | ||
| 63 | pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int; | ||
| 64 | pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; | ||
| 65 | pub extern "pthread" fn pthread_self() pthread_t; | ||
| 66 | pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; | ||
| 67 | |||
| 68 | pub const pthread_t = *@OpaqueType(); | ||
std/coff.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | 2 | const std = @import("std.zig"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const os = std.os; | 5 | const os = std.os; |
std/crypto.zig created+45| ... | @@ -0,0 +1,45 @@ | ||
| 1 | pub const Md5 = @import("crypto/md5.zig").Md5; | ||
| 2 | pub const Sha1 = @import("crypto/sha1.zig").Sha1; | ||
| 3 | |||
| 4 | const sha2 = @import("crypto/sha2.zig"); | ||
| 5 | pub const Sha224 = sha2.Sha224; | ||
| 6 | pub const Sha256 = sha2.Sha256; | ||
| 7 | pub const Sha384 = sha2.Sha384; | ||
| 8 | pub const Sha512 = sha2.Sha512; | ||
| 9 | |||
| 10 | const sha3 = @import("crypto/sha3.zig"); | ||
| 11 | pub const Sha3_224 = sha3.Sha3_224; | ||
| 12 | pub const Sha3_256 = sha3.Sha3_256; | ||
| 13 | pub const Sha3_384 = sha3.Sha3_384; | ||
| 14 | pub const Sha3_512 = sha3.Sha3_512; | ||
| 15 | |||
| 16 | const blake2 = @import("crypto/blake2.zig"); | ||
| 17 | pub const Blake2s224 = blake2.Blake2s224; | ||
| 18 | pub const Blake2s256 = blake2.Blake2s256; | ||
| 19 | pub const Blake2b384 = blake2.Blake2b384; | ||
| 20 | pub const Blake2b512 = blake2.Blake2b512; | ||
| 21 | |||
| 22 | const hmac = @import("crypto/hmac.zig"); | ||
| 23 | pub const HmacMd5 = hmac.HmacMd5; | ||
| 24 | pub const HmacSha1 = hmac.HmacSha1; | ||
| 25 | pub const HmacSha256 = hmac.HmacSha256; | ||
| 26 | pub const HmacBlake2s256 = hmac.HmacBlake2s256; | ||
| 27 | |||
| 28 | const import_chaCha20 = @import("crypto/chacha20.zig"); | ||
| 29 | pub const chaCha20IETF = import_chaCha20.chaCha20IETF; | ||
| 30 | pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; | ||
| 31 | |||
| 32 | pub const Poly1305 = @import("crypto/poly1305.zig").Poly1305; | ||
| 33 | pub const X25519 = @import("crypto/x25519.zig").X25519; | ||
| 34 | |||
| 35 | test "crypto" { | ||
| 36 | _ = @import("crypto/blake2.zig"); | ||
| 37 | _ = @import("crypto/chacha20.zig"); | ||
| 38 | _ = @import("crypto/hmac.zig"); | ||
| 39 | _ = @import("crypto/md5.zig"); | ||
| 40 | _ = @import("crypto/poly1305.zig"); | ||
| 41 | _ = @import("crypto/sha1.zig"); | ||
| 42 | _ = @import("crypto/sha2.zig"); | ||
| 43 | _ = @import("crypto/sha3.zig"); | ||
| 44 | _ = @import("crypto/x25519.zig"); | ||
| 45 | } | ||
std/crypto/blake2.zig+2-2| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const mem = @import("../mem.zig"); | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | 2 | const math = @import("../math.zig"); |
| 3 | const endian = @import("../endian.zig"); | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | 4 | const debug = @import("../debug.zig"); |
| 5 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 6 | const htest = @import("test.zig"); | 6 | const htest = @import("test.zig"); |
| 7 | 7 |
std/crypto/chacha20.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | // Based on public domain Supercop by Daniel J. Bernstein | 1 | // Based on public domain Supercop by Daniel J. Bernstein |
| 2 | 2 | ||
| 3 | const std = @import("../index.zig"); | 3 | const std = @import("../std.zig"); |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
| 5 | const endian = std.endian; | 5 | const endian = std.endian; |
| 6 | const assert = std.debug.assert; | 6 | const assert = std.debug.assert; |
std/crypto/hmac.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const crypto = std.crypto; | 2 | const crypto = std.crypto; |
| 3 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/crypto/index.zig deleted-45| ... | @@ -1,45 +0,0 @@ | ||
| 1 | pub const Md5 = @import("md5.zig").Md5; | ||
| 2 | pub const Sha1 = @import("sha1.zig").Sha1; | ||
| 3 | |||
| 4 | const sha2 = @import("sha2.zig"); | ||
| 5 | pub const Sha224 = sha2.Sha224; | ||
| 6 | pub const Sha256 = sha2.Sha256; | ||
| 7 | pub const Sha384 = sha2.Sha384; | ||
| 8 | pub const Sha512 = sha2.Sha512; | ||
| 9 | |||
| 10 | const sha3 = @import("sha3.zig"); | ||
| 11 | pub const Sha3_224 = sha3.Sha3_224; | ||
| 12 | pub const Sha3_256 = sha3.Sha3_256; | ||
| 13 | pub const Sha3_384 = sha3.Sha3_384; | ||
| 14 | pub const Sha3_512 = sha3.Sha3_512; | ||
| 15 | |||
| 16 | const blake2 = @import("blake2.zig"); | ||
| 17 | pub const Blake2s224 = blake2.Blake2s224; | ||
| 18 | pub const Blake2s256 = blake2.Blake2s256; | ||
| 19 | pub const Blake2b384 = blake2.Blake2b384; | ||
| 20 | pub const Blake2b512 = blake2.Blake2b512; | ||
| 21 | |||
| 22 | const hmac = @import("hmac.zig"); | ||
| 23 | pub const HmacMd5 = hmac.HmacMd5; | ||
| 24 | pub const HmacSha1 = hmac.HmacSha1; | ||
| 25 | pub const HmacSha256 = hmac.HmacSha256; | ||
| 26 | pub const HmacBlake2s256 = hmac.HmacBlake2s256; | ||
| 27 | |||
| 28 | const import_chaCha20 = @import("chacha20.zig"); | ||
| 29 | pub const chaCha20IETF = import_chaCha20.chaCha20IETF; | ||
| 30 | pub const chaCha20With64BitNonce = import_chaCha20.chaCha20With64BitNonce; | ||
| 31 | |||
| 32 | pub const Poly1305 = @import("poly1305.zig").Poly1305; | ||
| 33 | pub const X25519 = @import("x25519.zig").X25519; | ||
| 34 | |||
| 35 | test "crypto" { | ||
| 36 | _ = @import("blake2.zig"); | ||
| 37 | _ = @import("chacha20.zig"); | ||
| 38 | _ = @import("hmac.zig"); | ||
| 39 | _ = @import("md5.zig"); | ||
| 40 | _ = @import("poly1305.zig"); | ||
| 41 | _ = @import("sha1.zig"); | ||
| 42 | _ = @import("sha2.zig"); | ||
| 43 | _ = @import("sha3.zig"); | ||
| 44 | _ = @import("x25519.zig"); | ||
| 45 | } | ||
std/crypto/md5.zig+3-3| ... | @@ -1,9 +1,9 @@ | ... | @@ -1,9 +1,9 @@ |
| 1 | const mem = @import("../mem.zig"); | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | 2 | const math = @import("../math.zig"); |
| 3 | const endian = @import("../endian.zig"); | 3 | const endian = @import("../endian.zig"); |
| 4 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
| 5 | const debug = @import("../debug/index.zig"); | 5 | const debug = @import("../debug.zig"); |
| 6 | const fmt = @import("../fmt/index.zig"); | 6 | const fmt = @import("../fmt.zig"); |
| 7 | 7 | ||
| 8 | const RoundParam = struct { | 8 | const RoundParam = struct { |
| 9 | a: usize, | 9 | a: usize, |
std/crypto/poly1305.zig+1-1| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | // | 2 | // |
| 3 | // https://monocypher.org/ | 3 | // https://monocypher.org/ |
| 4 | 4 | ||
| 5 | const std = @import("../index.zig"); | 5 | const std = @import("../std.zig"); |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | 7 | ||
| 8 | const Endian = builtin.Endian; | 8 | const Endian = builtin.Endian; |
std/crypto/sha1.zig+2-2| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const mem = @import("../mem.zig"); | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | 2 | const math = @import("../math.zig"); |
| 3 | const endian = @import("../endian.zig"); | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | 4 | const debug = @import("../debug.zig"); |
| 5 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 6 | 6 | ||
| 7 | const RoundParam = struct { | 7 | const RoundParam = struct { |
std/crypto/sha2.zig+2-2| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const mem = @import("../mem.zig"); | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | 2 | const math = @import("../math.zig"); |
| 3 | const endian = @import("../endian.zig"); | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | 4 | const debug = @import("../debug.zig"); |
| 5 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 6 | const htest = @import("test.zig"); | 6 | const htest = @import("test.zig"); |
| 7 | 7 |
std/crypto/sha3.zig+2-2| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const mem = @import("../mem.zig"); | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | 2 | const math = @import("../math.zig"); |
| 3 | const endian = @import("../endian.zig"); | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | 4 | const debug = @import("../debug.zig"); |
| 5 | const builtin = @import("builtin"); | 5 | const builtin = @import("builtin"); |
| 6 | const htest = @import("test.zig"); | 6 | const htest = @import("test.zig"); |
| 7 | 7 |
std/crypto/test.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const fmt = std.fmt; | 4 | const fmt = std.fmt; |
std/crypto/throughput_test.zig+1-1| ... | @@ -2,7 +2,7 @@ const builtin = @import("builtin"); | ... | @@ -2,7 +2,7 @@ const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | const time = std.os.time; | 3 | const time = std.os.time; |
| 4 | const Timer = time.Timer; | 4 | const Timer = time.Timer; |
| 5 | const crypto = @import("index.zig"); | 5 | const crypto = @import("../crypto.zig"); |
| 6 | 6 | ||
| 7 | const KiB = 1024; | 7 | const KiB = 1024; |
| 8 | const MiB = 1024 * KiB; | 8 | const MiB = 1024 * KiB; |
std/crypto/x25519.zig+1-1| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | // | 2 | // |
| 3 | // https://monocypher.org/ | 3 | // https://monocypher.org/ |
| 4 | 4 | ||
| 5 | const std = @import("../index.zig"); | 5 | const std = @import("../std.zig"); |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const fmt = std.fmt; | 7 | const fmt = std.fmt; |
| 8 | 8 |
std/cstr.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/debug.zig created+2205| ... | @@ -0,0 +1,2205 @@ | ||
| 1 | const std = @import("std.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const mem = std.mem; | ||
| 4 | const io = std.io; | ||
| 5 | const os = std.os; | ||
| 6 | const elf = std.elf; | ||
| 7 | const DW = std.dwarf; | ||
| 8 | const macho = std.macho; | ||
| 9 | const coff = std.coff; | ||
| 10 | const pdb = std.pdb; | ||
| 11 | const windows = os.windows; | ||
| 12 | const ArrayList = std.ArrayList; | ||
| 13 | const builtin = @import("builtin"); | ||
| 14 | const maxInt = std.math.maxInt; | ||
| 15 | |||
| 16 | pub const FailingAllocator = @import("debug/failing_allocator.zig").FailingAllocator; | ||
| 17 | pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator; | ||
| 18 | |||
| 19 | pub const runtime_safety = switch (builtin.mode) { | ||
| 20 | builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, | ||
| 21 | builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, | ||
| 22 | }; | ||
| 23 | |||
| 24 | const Module = struct { | ||
| 25 | mod_info: pdb.ModInfo, | ||
| 26 | module_name: []u8, | ||
| 27 | obj_file_name: []u8, | ||
| 28 | |||
| 29 | populated: bool, | ||
| 30 | symbols: []u8, | ||
| 31 | subsect_info: []u8, | ||
| 32 | checksum_offset: ?usize, | ||
| 33 | }; | ||
| 34 | |||
| 35 | /// Tries to write to stderr, unbuffered, and ignores any error returned. | ||
| 36 | /// Does not append a newline. | ||
| 37 | var stderr_file: os.File = undefined; | ||
| 38 | var stderr_file_out_stream: os.File.OutStream = undefined; | ||
| 39 | |||
| 40 | var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; | ||
| 41 | var stderr_mutex = std.Mutex.init(); | ||
| 42 | pub fn warn(comptime fmt: []const u8, args: ...) void { | ||
| 43 | const held = stderr_mutex.acquire(); | ||
| 44 | defer held.release(); | ||
| 45 | const stderr = getStderrStream() catch return; | ||
| 46 | stderr.print(fmt, args) catch return; | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn getStderrStream() !*io.OutStream(os.File.WriteError) { | ||
| 50 | if (stderr_stream) |st| { | ||
| 51 | return st; | ||
| 52 | } else { | ||
| 53 | stderr_file = try io.getStdErr(); | ||
| 54 | stderr_file_out_stream = stderr_file.outStream(); | ||
| 55 | const st = &stderr_file_out_stream.stream; | ||
| 56 | stderr_stream = st; | ||
| 57 | return st; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// TODO multithreaded awareness | ||
| 62 | var self_debug_info: ?DebugInfo = null; | ||
| 63 | |||
| 64 | pub fn getSelfDebugInfo() !*DebugInfo { | ||
| 65 | if (self_debug_info) |*info| { | ||
| 66 | return info; | ||
| 67 | } else { | ||
| 68 | self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator()); | ||
| 69 | return &self_debug_info.?; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | fn wantTtyColor() bool { | ||
| 74 | var bytes: [128]u8 = undefined; | ||
| 75 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; | ||
| 76 | return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. | ||
| 80 | /// TODO multithreaded awareness | ||
| 81 | pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ||
| 82 | const stderr = getStderrStream() catch return; | ||
| 83 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 84 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | ||
| 85 | return; | ||
| 86 | }; | ||
| 87 | writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { | ||
| 88 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | ||
| 89 | return; | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Returns a slice with the same pointer as addresses, with a potentially smaller len. | ||
| 94 | /// On Windows, when first_address is not null, we ask for at least 32 stack frames, | ||
| 95 | /// and then try to find the first address. If addresses.len is more than 32, we | ||
| 96 | /// capture that many stack frames exactly, and then look for the first address, | ||
| 97 | /// chopping off the irrelevant frames and shifting so that the returned addresses pointer | ||
| 98 | /// equals the passed in addresses pointer. | ||
| 99 | pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { | ||
| 100 | switch (builtin.os) { | ||
| 101 | builtin.Os.windows => { | ||
| 102 | const addrs = stack_trace.instruction_addresses; | ||
| 103 | const u32_addrs_len = @intCast(u32, addrs.len); | ||
| 104 | const first_addr = first_address orelse { | ||
| 105 | stack_trace.index = windows.RtlCaptureStackBackTrace( | ||
| 106 | 0, | ||
| 107 | u32_addrs_len, | ||
| 108 | @ptrCast(**c_void, addrs.ptr), | ||
| 109 | null, | ||
| 110 | ); | ||
| 111 | return; | ||
| 112 | }; | ||
| 113 | var addr_buf_stack: [32]usize = undefined; | ||
| 114 | const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; | ||
| 115 | const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); | ||
| 116 | const first_index = for (addr_buf[0..n]) |addr, i| { | ||
| 117 | if (addr == first_addr) { | ||
| 118 | break i; | ||
| 119 | } | ||
| 120 | } else { | ||
| 121 | stack_trace.index = 0; | ||
| 122 | return; | ||
| 123 | }; | ||
| 124 | const slice = addr_buf[first_index..n]; | ||
| 125 | // We use a for loop here because slice and addrs may alias. | ||
| 126 | for (slice) |addr, i| { | ||
| 127 | addrs[i] = addr; | ||
| 128 | } | ||
| 129 | stack_trace.index = slice.len; | ||
| 130 | }, | ||
| 131 | else => { | ||
| 132 | var it = StackIterator.init(first_address); | ||
| 133 | for (stack_trace.instruction_addresses) |*addr, i| { | ||
| 134 | addr.* = it.next() orelse { | ||
| 135 | stack_trace.index = i; | ||
| 136 | return; | ||
| 137 | }; | ||
| 138 | } | ||
| 139 | stack_trace.index = stack_trace.instruction_addresses.len; | ||
| 140 | }, | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. | ||
| 145 | /// TODO multithreaded awareness | ||
| 146 | pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { | ||
| 147 | const stderr = getStderrStream() catch return; | ||
| 148 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 149 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | ||
| 150 | return; | ||
| 151 | }; | ||
| 152 | writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { | ||
| 153 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | ||
| 154 | return; | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// This function invokes undefined behavior when `ok` is `false`. | ||
| 159 | /// In Debug and ReleaseSafe modes, calls to this function are always | ||
| 160 | /// generated, and the `unreachable` statement triggers a panic. | ||
| 161 | /// In ReleaseFast and ReleaseSmall modes, calls to this function are | ||
| 162 | /// optimized away, and in fact the optimizer is able to use the assertion | ||
| 163 | /// in its heuristics. | ||
| 164 | /// Inside a test block, it is best to use the `std.testing` module rather | ||
| 165 | /// than this function, because this function may not detect a test failure | ||
| 166 | /// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert | ||
| 167 | /// function is the correct function to use. | ||
| 168 | pub fn assert(ok: bool) void { | ||
| 169 | if (!ok) unreachable; // assertion failure | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn panic(comptime format: []const u8, args: ...) noreturn { | ||
| 173 | @setCold(true); | ||
| 174 | const first_trace_addr = @returnAddress(); | ||
| 175 | panicExtra(null, first_trace_addr, format, args); | ||
| 176 | } | ||
| 177 | |||
| 178 | /// TODO multithreaded awareness | ||
| 179 | var panicking: u8 = 0; // TODO make this a bool | ||
| 180 | |||
| 181 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { | ||
| 182 | @setCold(true); | ||
| 183 | |||
| 184 | if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { | ||
| 185 | // Panicked during a panic. | ||
| 186 | |||
| 187 | // TODO detect if a different thread caused the panic, because in that case | ||
| 188 | // we would want to return here instead of calling abort, so that the thread | ||
| 189 | // which first called panic can finish printing a stack trace. | ||
| 190 | os.abort(); | ||
| 191 | } | ||
| 192 | const stderr = getStderrStream() catch os.abort(); | ||
| 193 | stderr.print(format ++ "\n", args) catch os.abort(); | ||
| 194 | if (trace) |t| { | ||
| 195 | dumpStackTrace(t.*); | ||
| 196 | } | ||
| 197 | dumpCurrentStackTrace(first_trace_addr); | ||
| 198 | |||
| 199 | os.abort(); | ||
| 200 | } | ||
| 201 | |||
| 202 | const RED = "\x1b[31;1m"; | ||
| 203 | const GREEN = "\x1b[32;1m"; | ||
| 204 | const CYAN = "\x1b[36;1m"; | ||
| 205 | const WHITE = "\x1b[37;1m"; | ||
| 206 | const DIM = "\x1b[2m"; | ||
| 207 | const RESET = "\x1b[0m"; | ||
| 208 | |||
| 209 | pub fn writeStackTrace( | ||
| 210 | stack_trace: builtin.StackTrace, | ||
| 211 | out_stream: var, | ||
| 212 | allocator: *mem.Allocator, | ||
| 213 | debug_info: *DebugInfo, | ||
| 214 | tty_color: bool, | ||
| 215 | ) !void { | ||
| 216 | var frame_index: usize = 0; | ||
| 217 | var frames_left: usize = stack_trace.index; | ||
| 218 | |||
| 219 | while (frames_left != 0) : ({ | ||
| 220 | frames_left -= 1; | ||
| 221 | frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; | ||
| 222 | }) { | ||
| 223 | const return_address = stack_trace.instruction_addresses[frame_index]; | ||
| 224 | try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | pub const StackIterator = struct { | ||
| 229 | first_addr: ?usize, | ||
| 230 | fp: usize, | ||
| 231 | |||
| 232 | pub fn init(first_addr: ?usize) StackIterator { | ||
| 233 | return StackIterator{ | ||
| 234 | .first_addr = first_addr, | ||
| 235 | .fp = @frameAddress(), | ||
| 236 | }; | ||
| 237 | } | ||
| 238 | |||
| 239 | fn next(self: *StackIterator) ?usize { | ||
| 240 | if (self.fp == 0) return null; | ||
| 241 | self.fp = @intToPtr(*const usize, self.fp).*; | ||
| 242 | if (self.fp == 0) return null; | ||
| 243 | |||
| 244 | if (self.first_addr) |addr| { | ||
| 245 | while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { | ||
| 246 | const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; | ||
| 247 | if (addr == return_address) { | ||
| 248 | self.first_addr = null; | ||
| 249 | return return_address; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; | ||
| 255 | return return_address; | ||
| 256 | } | ||
| 257 | }; | ||
| 258 | |||
| 259 | pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { | ||
| 260 | switch (builtin.os) { | ||
| 261 | builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), | ||
| 262 | else => {}, | ||
| 263 | } | ||
| 264 | var it = StackIterator.init(start_addr); | ||
| 265 | while (it.next()) |return_address| { | ||
| 266 | try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn writeCurrentStackTraceWindows( | ||
| 271 | out_stream: var, | ||
| 272 | debug_info: *DebugInfo, | ||
| 273 | tty_color: bool, | ||
| 274 | start_addr: ?usize, | ||
| 275 | ) !void { | ||
| 276 | var addr_buf: [1024]usize = undefined; | ||
| 277 | const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); | ||
| 278 | const addrs = addr_buf[0..n]; | ||
| 279 | var start_i: usize = if (start_addr) |saddr| blk: { | ||
| 280 | for (addrs) |addr, i| { | ||
| 281 | if (addr == saddr) break :blk i; | ||
| 282 | } | ||
| 283 | return; | ||
| 284 | } else 0; | ||
| 285 | for (addrs[start_i..]) |addr| { | ||
| 286 | try printSourceAtAddress(debug_info, out_stream, addr, tty_color); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | ||
| 291 | switch (builtin.os) { | ||
| 292 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), | ||
| 293 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), | ||
| 294 | builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), | ||
| 295 | else => return error.UnsupportedOperatingSystem, | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { | ||
| 300 | const allocator = getDebugInfoAllocator(); | ||
| 301 | const base_address = os.getBaseAddress(); | ||
| 302 | const relative_address = relocated_address - base_address; | ||
| 303 | |||
| 304 | var coff_section: *coff.Section = undefined; | ||
| 305 | const mod_index = for (di.sect_contribs) |sect_contrib| { | ||
| 306 | if (sect_contrib.Section > di.coff.sections.len) continue; | ||
| 307 | // Remember that SectionContribEntry.Section is 1-based. | ||
| 308 | coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; | ||
| 309 | |||
| 310 | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; | ||
| 311 | const vaddr_end = vaddr_start + sect_contrib.Size; | ||
| 312 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 313 | break sect_contrib.ModuleIndex; | ||
| 314 | } | ||
| 315 | } else { | ||
| 316 | // we have no information to add to the address | ||
| 317 | if (tty_color) { | ||
| 318 | try out_stream.print("???:?:?: "); | ||
| 319 | setTtyColor(TtyColor.Dim); | ||
| 320 | try out_stream.print("0x{x} in ??? (???)", relocated_address); | ||
| 321 | setTtyColor(TtyColor.Reset); | ||
| 322 | try out_stream.print("\n\n\n"); | ||
| 323 | } else { | ||
| 324 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); | ||
| 325 | } | ||
| 326 | return; | ||
| 327 | }; | ||
| 328 | |||
| 329 | const mod = &di.modules[mod_index]; | ||
| 330 | try populateModule(di, mod); | ||
| 331 | const obj_basename = os.path.basename(mod.obj_file_name); | ||
| 332 | |||
| 333 | var symbol_i: usize = 0; | ||
| 334 | const symbol_name = while (symbol_i != mod.symbols.len) { | ||
| 335 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); | ||
| 336 | if (prefix.RecordLen < 2) | ||
| 337 | return error.InvalidDebugInfo; | ||
| 338 | switch (prefix.RecordKind) { | ||
| 339 | pdb.SymbolKind.S_LPROC32 => { | ||
| 340 | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); | ||
| 341 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; | ||
| 342 | const vaddr_end = vaddr_start + proc_sym.CodeSize; | ||
| 343 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 344 | break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); | ||
| 345 | } | ||
| 346 | }, | ||
| 347 | else => {}, | ||
| 348 | } | ||
| 349 | symbol_i += prefix.RecordLen + @sizeOf(u16); | ||
| 350 | if (symbol_i > mod.symbols.len) | ||
| 351 | return error.InvalidDebugInfo; | ||
| 352 | } else "???"; | ||
| 353 | |||
| 354 | const subsect_info = mod.subsect_info; | ||
| 355 | |||
| 356 | var sect_offset: usize = 0; | ||
| 357 | var skip_len: usize = undefined; | ||
| 358 | const opt_line_info = subsections: { | ||
| 359 | const checksum_offset = mod.checksum_offset orelse break :subsections null; | ||
| 360 | while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { | ||
| 361 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); | ||
| 362 | skip_len = subsect_hdr.Length; | ||
| 363 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 364 | |||
| 365 | switch (subsect_hdr.Kind) { | ||
| 366 | pdb.DebugSubsectionKind.Lines => { | ||
| 367 | var line_index = sect_offset; | ||
| 368 | |||
| 369 | const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); | ||
| 370 | if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; | ||
| 371 | line_index += @sizeOf(pdb.LineFragmentHeader); | ||
| 372 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; | ||
| 373 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; | ||
| 374 | |||
| 375 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) | ||
| 376 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, | ||
| 377 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. | ||
| 378 | const subsection_end_index = sect_offset + subsect_hdr.Length; | ||
| 379 | while (line_index < subsection_end_index) { | ||
| 380 | const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); | ||
| 381 | line_index += @sizeOf(pdb.LineBlockFragmentHeader); | ||
| 382 | const start_line_index = line_index; | ||
| 383 | |||
| 384 | const has_column = line_hdr.Flags.LF_HaveColumns; | ||
| 385 | |||
| 386 | if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { | ||
| 387 | // All line entries are stored inside their line block by ascending start address. | ||
| 388 | // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address. | ||
| 389 | // This is done with a simple linear search. | ||
| 390 | var line_i: u32 = 0; | ||
| 391 | while (line_i < block_hdr.NumLines) : (line_i += 1) { | ||
| 392 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); | ||
| 393 | line_index += @sizeOf(pdb.LineNumberEntry); | ||
| 394 | |||
| 395 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; | ||
| 396 | if (relative_address <= vaddr_start) { | ||
| 397 | break; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | // line_i == 0 would mean that no matching LineNumberEntry was found. | ||
| 402 | if (line_i > 0) { | ||
| 403 | const subsect_index = checksum_offset + block_hdr.NameIndex; | ||
| 404 | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); | ||
| 405 | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; | ||
| 406 | try di.pdb.string_table.seekTo(strtab_offset); | ||
| 407 | const source_file_name = try di.pdb.string_table.readNullTermString(allocator); | ||
| 408 | |||
| 409 | const line_entry_idx = line_i - 1; | ||
| 410 | |||
| 411 | const column = if (has_column) blk: { | ||
| 412 | const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; | ||
| 413 | const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; | ||
| 414 | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); | ||
| 415 | break :blk col_num_entry.StartColumn; | ||
| 416 | } else 0; | ||
| 417 | |||
| 418 | const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); | ||
| 419 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); | ||
| 420 | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); | ||
| 421 | |||
| 422 | break :subsections LineInfo{ | ||
| 423 | .allocator = allocator, | ||
| 424 | .file_name = source_file_name, | ||
| 425 | .line = flags.Start, | ||
| 426 | .column = column, | ||
| 427 | }; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | // Checking that we are not reading garbage after the (possibly) multiple block fragments. | ||
| 433 | if (line_index != subsection_end_index) { | ||
| 434 | return error.InvalidDebugInfo; | ||
| 435 | } | ||
| 436 | }, | ||
| 437 | else => {}, | ||
| 438 | } | ||
| 439 | |||
| 440 | if (sect_offset > subsect_info.len) | ||
| 441 | return error.InvalidDebugInfo; | ||
| 442 | } else { | ||
| 443 | break :subsections null; | ||
| 444 | } | ||
| 445 | }; | ||
| 446 | |||
| 447 | if (tty_color) { | ||
| 448 | setTtyColor(TtyColor.White); | ||
| 449 | if (opt_line_info) |li| { | ||
| 450 | try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column); | ||
| 451 | } else { | ||
| 452 | try out_stream.print("???:?:?"); | ||
| 453 | } | ||
| 454 | setTtyColor(TtyColor.Reset); | ||
| 455 | try out_stream.print(": "); | ||
| 456 | setTtyColor(TtyColor.Dim); | ||
| 457 | try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); | ||
| 458 | setTtyColor(TtyColor.Reset); | ||
| 459 | |||
| 460 | if (opt_line_info) |line_info| { | ||
| 461 | try out_stream.print("\n"); | ||
| 462 | if (printLineFromFileAnyOs(out_stream, line_info)) { | ||
| 463 | if (line_info.column == 0) { | ||
| 464 | try out_stream.write("\n"); | ||
| 465 | } else { | ||
| 466 | { | ||
| 467 | var col_i: usize = 1; | ||
| 468 | while (col_i < line_info.column) : (col_i += 1) { | ||
| 469 | try out_stream.writeByte(' '); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | setTtyColor(TtyColor.Green); | ||
| 473 | try out_stream.write("^"); | ||
| 474 | setTtyColor(TtyColor.Reset); | ||
| 475 | try out_stream.write("\n"); | ||
| 476 | } | ||
| 477 | } else |err| switch (err) { | ||
| 478 | error.EndOfFile => {}, | ||
| 479 | else => return err, | ||
| 480 | } | ||
| 481 | } else { | ||
| 482 | try out_stream.print("\n\n\n"); | ||
| 483 | } | ||
| 484 | } else { | ||
| 485 | if (opt_line_info) |li| { | ||
| 486 | try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); | ||
| 487 | } else { | ||
| 488 | try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | const TtyColor = enum { | ||
| 494 | Red, | ||
| 495 | Green, | ||
| 496 | Cyan, | ||
| 497 | White, | ||
| 498 | Dim, | ||
| 499 | Bold, | ||
| 500 | Reset, | ||
| 501 | }; | ||
| 502 | |||
| 503 | /// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt | ||
| 504 | fn setTtyColor(tty_color: TtyColor) void { | ||
| 505 | if (os.supportsAnsiEscapeCodes(stderr_file.handle)) { | ||
| 506 | switch (tty_color) { | ||
| 507 | TtyColor.Red => { | ||
| 508 | stderr_file.write(RED) catch return; | ||
| 509 | }, | ||
| 510 | TtyColor.Green => { | ||
| 511 | stderr_file.write(GREEN) catch return; | ||
| 512 | }, | ||
| 513 | TtyColor.Cyan => { | ||
| 514 | stderr_file.write(CYAN) catch return; | ||
| 515 | }, | ||
| 516 | TtyColor.White, TtyColor.Bold => { | ||
| 517 | stderr_file.write(WHITE) catch return; | ||
| 518 | }, | ||
| 519 | TtyColor.Dim => { | ||
| 520 | stderr_file.write(DIM) catch return; | ||
| 521 | }, | ||
| 522 | TtyColor.Reset => { | ||
| 523 | stderr_file.write(RESET) catch return; | ||
| 524 | }, | ||
| 525 | } | ||
| 526 | } else { | ||
| 527 | const S = struct { | ||
| 528 | var attrs: windows.WORD = undefined; | ||
| 529 | var init_attrs = false; | ||
| 530 | }; | ||
| 531 | if (!S.init_attrs) { | ||
| 532 | S.init_attrs = true; | ||
| 533 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | ||
| 534 | // TODO handle error | ||
| 535 | _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); | ||
| 536 | S.attrs = info.wAttributes; | ||
| 537 | } | ||
| 538 | |||
| 539 | // TODO handle errors | ||
| 540 | switch (tty_color) { | ||
| 541 | TtyColor.Red => { | ||
| 542 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY); | ||
| 543 | }, | ||
| 544 | TtyColor.Green => { | ||
| 545 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY); | ||
| 546 | }, | ||
| 547 | TtyColor.Cyan => { | ||
| 548 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); | ||
| 549 | }, | ||
| 550 | TtyColor.White, TtyColor.Bold => { | ||
| 551 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); | ||
| 552 | }, | ||
| 553 | TtyColor.Dim => { | ||
| 554 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); | ||
| 555 | }, | ||
| 556 | TtyColor.Reset => { | ||
| 557 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); | ||
| 558 | }, | ||
| 559 | } | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | fn populateModule(di: *DebugInfo, mod: *Module) !void { | ||
| 564 | if (mod.populated) | ||
| 565 | return; | ||
| 566 | const allocator = getDebugInfoAllocator(); | ||
| 567 | |||
| 568 | if (mod.mod_info.C11ByteSize != 0) | ||
| 569 | return error.InvalidDebugInfo; | ||
| 570 | |||
| 571 | if (mod.mod_info.C13ByteSize == 0) | ||
| 572 | return error.MissingDebugInfo; | ||
| 573 | |||
| 574 | const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; | ||
| 575 | |||
| 576 | const signature = try modi.stream.readIntLittle(u32); | ||
| 577 | if (signature != 4) | ||
| 578 | return error.InvalidDebugInfo; | ||
| 579 | |||
| 580 | mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); | ||
| 581 | try modi.stream.readNoEof(mod.symbols); | ||
| 582 | |||
| 583 | mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); | ||
| 584 | try modi.stream.readNoEof(mod.subsect_info); | ||
| 585 | |||
| 586 | var sect_offset: usize = 0; | ||
| 587 | var skip_len: usize = undefined; | ||
| 588 | while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { | ||
| 589 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]); | ||
| 590 | skip_len = subsect_hdr.Length; | ||
| 591 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 592 | |||
| 593 | switch (subsect_hdr.Kind) { | ||
| 594 | pdb.DebugSubsectionKind.FileChecksums => { | ||
| 595 | mod.checksum_offset = sect_offset; | ||
| 596 | break; | ||
| 597 | }, | ||
| 598 | else => {}, | ||
| 599 | } | ||
| 600 | |||
| 601 | if (sect_offset > mod.subsect_info.len) | ||
| 602 | return error.InvalidDebugInfo; | ||
| 603 | } | ||
| 604 | |||
| 605 | mod.populated = true; | ||
| 606 | } | ||
| 607 | |||
| 608 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { | ||
| 609 | var min: usize = 0; | ||
| 610 | var max: usize = symbols.len - 1; // Exclude sentinel. | ||
| 611 | while (min < max) { | ||
| 612 | const mid = min + (max - min) / 2; | ||
| 613 | const curr = &symbols[mid]; | ||
| 614 | const next = &symbols[mid + 1]; | ||
| 615 | if (address >= next.address()) { | ||
| 616 | min = mid + 1; | ||
| 617 | } else if (address < curr.address()) { | ||
| 618 | max = mid; | ||
| 619 | } else { | ||
| 620 | return curr; | ||
| 621 | } | ||
| 622 | } | ||
| 623 | return null; | ||
| 624 | } | ||
| 625 | |||
| 626 | fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | ||
| 627 | const base_addr = std.os.getBaseAddress(); | ||
| 628 | const adjusted_addr = 0x100000000 + (address - base_addr); | ||
| 629 | |||
| 630 | const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { | ||
| 631 | if (tty_color) { | ||
| 632 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); | ||
| 633 | } else { | ||
| 634 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); | ||
| 635 | } | ||
| 636 | return; | ||
| 637 | }; | ||
| 638 | |||
| 639 | const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx); | ||
| 640 | const compile_unit_name = if (symbol.ofile) |ofile| blk: { | ||
| 641 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); | ||
| 642 | break :blk os.path.basename(ofile_path); | ||
| 643 | } else "???"; | ||
| 644 | if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { | ||
| 645 | defer line_info.deinit(); | ||
| 646 | try printLineInfo( | ||
| 647 | out_stream, | ||
| 648 | line_info, | ||
| 649 | address, | ||
| 650 | symbol_name, | ||
| 651 | compile_unit_name, | ||
| 652 | tty_color, | ||
| 653 | printLineFromFileAnyOs, | ||
| 654 | ); | ||
| 655 | } else |err| switch (err) { | ||
| 656 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 657 | if (tty_color) { | ||
| 658 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); | ||
| 659 | } else { | ||
| 660 | try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name); | ||
| 661 | } | ||
| 662 | }, | ||
| 663 | else => return err, | ||
| 664 | } | ||
| 665 | } | ||
| 666 | |||
| 667 | /// This function works in freestanding mode. | ||
| 668 | /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void | ||
| 669 | pub fn printSourceAtAddressDwarf( | ||
| 670 | debug_info: *DwarfInfo, | ||
| 671 | out_stream: var, | ||
| 672 | address: usize, | ||
| 673 | tty_color: bool, | ||
| 674 | comptime printLineFromFile: var, | ||
| 675 | ) !void { | ||
| 676 | const compile_unit = findCompileUnit(debug_info, address) catch { | ||
| 677 | if (tty_color) { | ||
| 678 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); | ||
| 679 | } else { | ||
| 680 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); | ||
| 681 | } | ||
| 682 | return; | ||
| 683 | }; | ||
| 684 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | ||
| 685 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { | ||
| 686 | defer line_info.deinit(); | ||
| 687 | const symbol_name = "???"; | ||
| 688 | try printLineInfo( | ||
| 689 | out_stream, | ||
| 690 | line_info, | ||
| 691 | address, | ||
| 692 | symbol_name, | ||
| 693 | compile_unit_name, | ||
| 694 | tty_color, | ||
| 695 | printLineFromFile, | ||
| 696 | ); | ||
| 697 | } else |err| switch (err) { | ||
| 698 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 699 | if (tty_color) { | ||
| 700 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); | ||
| 701 | } else { | ||
| 702 | try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); | ||
| 703 | } | ||
| 704 | }, | ||
| 705 | else => return err, | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 709 | pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | ||
| 710 | return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); | ||
| 711 | } | ||
| 712 | |||
| 713 | fn printLineInfo( | ||
| 714 | out_stream: var, | ||
| 715 | line_info: LineInfo, | ||
| 716 | address: usize, | ||
| 717 | symbol_name: []const u8, | ||
| 718 | compile_unit_name: []const u8, | ||
| 719 | tty_color: bool, | ||
| 720 | comptime printLineFromFile: var, | ||
| 721 | ) !void { | ||
| 722 | if (tty_color) { | ||
| 723 | try out_stream.print( | ||
| 724 | WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", | ||
| 725 | line_info.file_name, | ||
| 726 | line_info.line, | ||
| 727 | line_info.column, | ||
| 728 | address, | ||
| 729 | symbol_name, | ||
| 730 | compile_unit_name, | ||
| 731 | ); | ||
| 732 | if (printLineFromFile(out_stream, line_info)) { | ||
| 733 | if (line_info.column == 0) { | ||
| 734 | try out_stream.write("\n"); | ||
| 735 | } else { | ||
| 736 | { | ||
| 737 | var col_i: usize = 1; | ||
| 738 | while (col_i < line_info.column) : (col_i += 1) { | ||
| 739 | try out_stream.writeByte(' '); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | ||
| 743 | } | ||
| 744 | } else |err| switch (err) { | ||
| 745 | error.EndOfFile => {}, | ||
| 746 | else => return err, | ||
| 747 | } | ||
| 748 | } else { | ||
| 749 | try out_stream.print( | ||
| 750 | "{}:{}:{}: 0x{x} in {} ({})\n", | ||
| 751 | line_info.file_name, | ||
| 752 | line_info.line, | ||
| 753 | line_info.column, | ||
| 754 | address, | ||
| 755 | symbol_name, | ||
| 756 | compile_unit_name, | ||
| 757 | ); | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | // TODO use this | ||
| 762 | pub const OpenSelfDebugInfoError = error{ | ||
| 763 | MissingDebugInfo, | ||
| 764 | OutOfMemory, | ||
| 765 | UnsupportedOperatingSystem, | ||
| 766 | }; | ||
| 767 | |||
| 768 | pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { | ||
| 769 | switch (builtin.os) { | ||
| 770 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator), | ||
| 771 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), | ||
| 772 | builtin.Os.windows => return openSelfDebugInfoWindows(allocator), | ||
| 773 | else => return error.UnsupportedOperatingSystem, | ||
| 774 | } | ||
| 775 | } | ||
| 776 | |||
| 777 | fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | ||
| 778 | const self_file = try os.openSelfExe(); | ||
| 779 | defer self_file.close(); | ||
| 780 | |||
| 781 | const coff_obj = try allocator.create(coff.Coff); | ||
| 782 | coff_obj.* = coff.Coff{ | ||
| 783 | .in_file = self_file, | ||
| 784 | .allocator = allocator, | ||
| 785 | .coff_header = undefined, | ||
| 786 | .pe_header = undefined, | ||
| 787 | .sections = undefined, | ||
| 788 | .guid = undefined, | ||
| 789 | .age = undefined, | ||
| 790 | }; | ||
| 791 | |||
| 792 | var di = DebugInfo{ | ||
| 793 | .coff = coff_obj, | ||
| 794 | .pdb = undefined, | ||
| 795 | .sect_contribs = undefined, | ||
| 796 | .modules = undefined, | ||
| 797 | }; | ||
| 798 | |||
| 799 | try di.coff.loadHeader(); | ||
| 800 | |||
| 801 | var path_buf: [windows.MAX_PATH]u8 = undefined; | ||
| 802 | const len = try di.coff.getPdbPath(path_buf[0..]); | ||
| 803 | const raw_path = path_buf[0..len]; | ||
| 804 | |||
| 805 | const path = try os.path.resolve(allocator, [][]const u8{raw_path}); | ||
| 806 | |||
| 807 | try di.pdb.openFile(di.coff, path); | ||
| 808 | |||
| 809 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | ||
| 810 | const version = try pdb_stream.stream.readIntLittle(u32); | ||
| 811 | const signature = try pdb_stream.stream.readIntLittle(u32); | ||
| 812 | const age = try pdb_stream.stream.readIntLittle(u32); | ||
| 813 | var guid: [16]u8 = undefined; | ||
| 814 | try pdb_stream.stream.readNoEof(guid[0..]); | ||
| 815 | if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) | ||
| 816 | return error.InvalidDebugInfo; | ||
| 817 | // We validated the executable and pdb match. | ||
| 818 | |||
| 819 | const string_table_index = str_tab_index: { | ||
| 820 | const name_bytes_len = try pdb_stream.stream.readIntLittle(u32); | ||
| 821 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | ||
| 822 | try pdb_stream.stream.readNoEof(name_bytes); | ||
| 823 | |||
| 824 | const HashTableHeader = packed struct { | ||
| 825 | Size: u32, | ||
| 826 | Capacity: u32, | ||
| 827 | |||
| 828 | fn maxLoad(cap: u32) u32 { | ||
| 829 | return cap * 2 / 3 + 1; | ||
| 830 | } | ||
| 831 | }; | ||
| 832 | const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader); | ||
| 833 | if (hash_tbl_hdr.Capacity == 0) | ||
| 834 | return error.InvalidDebugInfo; | ||
| 835 | |||
| 836 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | ||
| 837 | return error.InvalidDebugInfo; | ||
| 838 | |||
| 839 | const present = try readSparseBitVector(&pdb_stream.stream, allocator); | ||
| 840 | if (present.len != hash_tbl_hdr.Size) | ||
| 841 | return error.InvalidDebugInfo; | ||
| 842 | const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); | ||
| 843 | |||
| 844 | const Bucket = struct { | ||
| 845 | first: u32, | ||
| 846 | second: u32, | ||
| 847 | }; | ||
| 848 | const bucket_list = try allocator.alloc(Bucket, present.len); | ||
| 849 | for (present) |_| { | ||
| 850 | const name_offset = try pdb_stream.stream.readIntLittle(u32); | ||
| 851 | const name_index = try pdb_stream.stream.readIntLittle(u32); | ||
| 852 | const name = mem.toSlice(u8, name_bytes.ptr + name_offset); | ||
| 853 | if (mem.eql(u8, name, "/names")) { | ||
| 854 | break :str_tab_index name_index; | ||
| 855 | } | ||
| 856 | } | ||
| 857 | return error.MissingDebugInfo; | ||
| 858 | }; | ||
| 859 | |||
| 860 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; | ||
| 861 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | ||
| 862 | |||
| 863 | const dbi = di.pdb.dbi; | ||
| 864 | |||
| 865 | // Dbi Header | ||
| 866 | const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); | ||
| 867 | const mod_info_size = dbi_stream_header.ModInfoSize; | ||
| 868 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | ||
| 869 | |||
| 870 | var modules = ArrayList(Module).init(allocator); | ||
| 871 | |||
| 872 | // Module Info Substream | ||
| 873 | var mod_info_offset: usize = 0; | ||
| 874 | while (mod_info_offset != mod_info_size) { | ||
| 875 | const mod_info = try dbi.stream.readStruct(pdb.ModInfo); | ||
| 876 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | ||
| 877 | |||
| 878 | const module_name = try dbi.readNullTermString(allocator); | ||
| 879 | this_record_len += module_name.len + 1; | ||
| 880 | |||
| 881 | const obj_file_name = try dbi.readNullTermString(allocator); | ||
| 882 | this_record_len += obj_file_name.len + 1; | ||
| 883 | |||
| 884 | const march_forward_bytes = this_record_len % 4; | ||
| 885 | if (march_forward_bytes != 0) { | ||
| 886 | try dbi.seekForward(march_forward_bytes); | ||
| 887 | this_record_len += march_forward_bytes; | ||
| 888 | } | ||
| 889 | |||
| 890 | try modules.append(Module{ | ||
| 891 | .mod_info = mod_info, | ||
| 892 | .module_name = module_name, | ||
| 893 | .obj_file_name = obj_file_name, | ||
| 894 | |||
| 895 | .populated = false, | ||
| 896 | .symbols = undefined, | ||
| 897 | .subsect_info = undefined, | ||
| 898 | .checksum_offset = null, | ||
| 899 | }); | ||
| 900 | |||
| 901 | mod_info_offset += this_record_len; | ||
| 902 | if (mod_info_offset > mod_info_size) | ||
| 903 | return error.InvalidDebugInfo; | ||
| 904 | } | ||
| 905 | |||
| 906 | di.modules = modules.toOwnedSlice(); | ||
| 907 | |||
| 908 | // Section Contribution Substream | ||
| 909 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | ||
| 910 | var sect_cont_offset: usize = 0; | ||
| 911 | if (section_contrib_size != 0) { | ||
| 912 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32)); | ||
| 913 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | ||
| 914 | return error.InvalidDebugInfo; | ||
| 915 | sect_cont_offset += @sizeOf(u32); | ||
| 916 | } | ||
| 917 | while (sect_cont_offset != section_contrib_size) { | ||
| 918 | const entry = try sect_contribs.addOne(); | ||
| 919 | entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry); | ||
| 920 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | ||
| 921 | |||
| 922 | if (sect_cont_offset > section_contrib_size) | ||
| 923 | return error.InvalidDebugInfo; | ||
| 924 | } | ||
| 925 | |||
| 926 | di.sect_contribs = sect_contribs.toOwnedSlice(); | ||
| 927 | |||
| 928 | return di; | ||
| 929 | } | ||
| 930 | |||
| 931 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { | ||
| 932 | const num_words = try stream.readIntLittle(u32); | ||
| 933 | var word_i: usize = 0; | ||
| 934 | var list = ArrayList(usize).init(allocator); | ||
| 935 | while (word_i != num_words) : (word_i += 1) { | ||
| 936 | const word = try stream.readIntLittle(u32); | ||
| 937 | var bit_i: u5 = 0; | ||
| 938 | while (true) : (bit_i += 1) { | ||
| 939 | if (word & (u32(1) << bit_i) != 0) { | ||
| 940 | try list.append(word_i * 32 + bit_i); | ||
| 941 | } | ||
| 942 | if (bit_i == maxInt(u5)) break; | ||
| 943 | } | ||
| 944 | } | ||
| 945 | return list.toOwnedSlice(); | ||
| 946 | } | ||
| 947 | |||
| 948 | fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { | ||
| 949 | const elf_header = (try elf_file.findSection(name)) orelse return null; | ||
| 950 | return DwarfInfo.Section{ | ||
| 951 | .offset = elf_header.offset, | ||
| 952 | .size = elf_header.size, | ||
| 953 | }; | ||
| 954 | } | ||
| 955 | |||
| 956 | /// Initialize DWARF info. The caller has the responsibility to initialize most | ||
| 957 | /// the DwarfInfo fields before calling. These fields can be left undefined: | ||
| 958 | /// * abbrev_table_list | ||
| 959 | /// * compile_unit_list | ||
| 960 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { | ||
| 961 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | ||
| 962 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | ||
| 963 | try scanAllCompileUnits(di); | ||
| 964 | } | ||
| 965 | |||
| 966 | pub fn openElfDebugInfo( | ||
| 967 | allocator: *mem.Allocator, | ||
| 968 | elf_seekable_stream: *DwarfSeekableStream, | ||
| 969 | elf_in_stream: *DwarfInStream, | ||
| 970 | ) !DwarfInfo { | ||
| 971 | var efile: elf.Elf = undefined; | ||
| 972 | try efile.openStream(allocator, elf_seekable_stream, elf_in_stream); | ||
| 973 | errdefer efile.close(); | ||
| 974 | |||
| 975 | var di = DwarfInfo{ | ||
| 976 | .dwarf_seekable_stream = elf_seekable_stream, | ||
| 977 | .dwarf_in_stream = elf_in_stream, | ||
| 978 | .endian = efile.endian, | ||
| 979 | .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, | ||
| 980 | .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, | ||
| 981 | .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, | ||
| 982 | .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, | ||
| 983 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), | ||
| 984 | .abbrev_table_list = undefined, | ||
| 985 | .compile_unit_list = undefined, | ||
| 986 | }; | ||
| 987 | try openDwarfDebugInfo(&di, allocator); | ||
| 988 | return di; | ||
| 989 | } | ||
| 990 | |||
| 991 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { | ||
| 992 | const S = struct { | ||
| 993 | var self_exe_file: os.File = undefined; | ||
| 994 | var self_exe_seekable_stream: os.File.SeekableStream = undefined; | ||
| 995 | var self_exe_in_stream: os.File.InStream = undefined; | ||
| 996 | }; | ||
| 997 | S.self_exe_file = try os.openSelfExe(); | ||
| 998 | errdefer S.self_exe_file.close(); | ||
| 999 | |||
| 1000 | S.self_exe_seekable_stream = S.self_exe_file.seekableStream(); | ||
| 1001 | S.self_exe_in_stream = S.self_exe_file.inStream(); | ||
| 1002 | |||
| 1003 | return openElfDebugInfo( | ||
| 1004 | allocator, | ||
| 1005 | // TODO https://github.com/ziglang/zig/issues/764 | ||
| 1006 | @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream), | ||
| 1007 | // TODO https://github.com/ziglang/zig/issues/764 | ||
| 1008 | @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream), | ||
| 1009 | ); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | ||
| 1013 | const hdr = &std.c._mh_execute_header; | ||
| 1014 | assert(hdr.magic == std.macho.MH_MAGIC_64); | ||
| 1015 | |||
| 1016 | const hdr_base = @ptrCast([*]u8, hdr); | ||
| 1017 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); | ||
| 1018 | var ncmd: u32 = hdr.ncmds; | ||
| 1019 | const symtab = while (ncmd != 0) : (ncmd -= 1) { | ||
| 1020 | const lc = @ptrCast(*std.macho.load_command, ptr); | ||
| 1021 | switch (lc.cmd) { | ||
| 1022 | std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), | ||
| 1023 | else => {}, | ||
| 1024 | } | ||
| 1025 | ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 | ||
| 1026 | } else { | ||
| 1027 | return error.MissingDebugInfo; | ||
| 1028 | }; | ||
| 1029 | const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; | ||
| 1030 | const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; | ||
| 1031 | |||
| 1032 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); | ||
| 1033 | |||
| 1034 | var ofile: ?*macho.nlist_64 = null; | ||
| 1035 | var reloc: u64 = 0; | ||
| 1036 | var symbol_index: usize = 0; | ||
| 1037 | var last_len: u64 = 0; | ||
| 1038 | for (syms) |*sym| { | ||
| 1039 | if (sym.n_type & std.macho.N_STAB != 0) { | ||
| 1040 | switch (sym.n_type) { | ||
| 1041 | std.macho.N_OSO => { | ||
| 1042 | ofile = sym; | ||
| 1043 | reloc = 0; | ||
| 1044 | }, | ||
| 1045 | std.macho.N_FUN => { | ||
| 1046 | if (sym.n_sect == 0) { | ||
| 1047 | last_len = sym.n_value; | ||
| 1048 | } else { | ||
| 1049 | symbols_buf[symbol_index] = MachoSymbol{ | ||
| 1050 | .nlist = sym, | ||
| 1051 | .ofile = ofile, | ||
| 1052 | .reloc = reloc, | ||
| 1053 | }; | ||
| 1054 | symbol_index += 1; | ||
| 1055 | } | ||
| 1056 | }, | ||
| 1057 | std.macho.N_BNSYM => { | ||
| 1058 | if (reloc == 0) { | ||
| 1059 | reloc = sym.n_value; | ||
| 1060 | } | ||
| 1061 | }, | ||
| 1062 | else => continue, | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | const sentinel = try allocator.create(macho.nlist_64); | ||
| 1067 | sentinel.* = macho.nlist_64{ | ||
| 1068 | .n_strx = 0, | ||
| 1069 | .n_type = 36, | ||
| 1070 | .n_sect = 0, | ||
| 1071 | .n_desc = 0, | ||
| 1072 | .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, | ||
| 1073 | }; | ||
| 1074 | |||
| 1075 | const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index); | ||
| 1076 | |||
| 1077 | // Even though lld emits symbols in ascending order, this debug code | ||
| 1078 | // should work for programs linked in any valid way. | ||
| 1079 | // This sort is so that we can binary search later. | ||
| 1080 | std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); | ||
| 1081 | |||
| 1082 | return DebugInfo{ | ||
| 1083 | .ofiles = DebugInfo.OFileTable.init(allocator), | ||
| 1084 | .symbols = symbols, | ||
| 1085 | .strings = strings, | ||
| 1086 | }; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | ||
| 1090 | var f = try os.File.openRead(line_info.file_name); | ||
| 1091 | defer f.close(); | ||
| 1092 | // TODO fstat and make sure that the file has the correct size | ||
| 1093 | |||
| 1094 | var buf: [os.page_size]u8 = undefined; | ||
| 1095 | var line: usize = 1; | ||
| 1096 | var column: usize = 1; | ||
| 1097 | var abs_index: usize = 0; | ||
| 1098 | while (true) { | ||
| 1099 | const amt_read = try f.read(buf[0..]); | ||
| 1100 | const slice = buf[0..amt_read]; | ||
| 1101 | |||
| 1102 | for (slice) |byte| { | ||
| 1103 | if (line == line_info.line) { | ||
| 1104 | try out_stream.writeByte(byte); | ||
| 1105 | if (byte == '\n') { | ||
| 1106 | return; | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | if (byte == '\n') { | ||
| 1110 | line += 1; | ||
| 1111 | column = 1; | ||
| 1112 | } else { | ||
| 1113 | column += 1; | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | if (amt_read < buf.len) return error.EndOfFile; | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | const MachoSymbol = struct { | ||
| 1122 | nlist: *macho.nlist_64, | ||
| 1123 | ofile: ?*macho.nlist_64, | ||
| 1124 | reloc: u64, | ||
| 1125 | |||
| 1126 | /// Returns the address from the macho file | ||
| 1127 | fn address(self: MachoSymbol) u64 { | ||
| 1128 | return self.nlist.n_value; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool { | ||
| 1132 | return lhs.address() < rhs.address(); | ||
| 1133 | } | ||
| 1134 | }; | ||
| 1135 | |||
| 1136 | const MachOFile = struct { | ||
| 1137 | bytes: []align(@alignOf(macho.mach_header_64)) const u8, | ||
| 1138 | sect_debug_info: ?*const macho.section_64, | ||
| 1139 | sect_debug_line: ?*const macho.section_64, | ||
| 1140 | }; | ||
| 1141 | |||
| 1142 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); | ||
| 1143 | pub const DwarfInStream = io.InStream(anyerror); | ||
| 1144 | |||
| 1145 | pub const DwarfInfo = struct { | ||
| 1146 | dwarf_seekable_stream: *DwarfSeekableStream, | ||
| 1147 | dwarf_in_stream: *DwarfInStream, | ||
| 1148 | endian: builtin.Endian, | ||
| 1149 | debug_info: Section, | ||
| 1150 | debug_abbrev: Section, | ||
| 1151 | debug_str: Section, | ||
| 1152 | debug_line: Section, | ||
| 1153 | debug_ranges: ?Section, | ||
| 1154 | abbrev_table_list: ArrayList(AbbrevTableHeader), | ||
| 1155 | compile_unit_list: ArrayList(CompileUnit), | ||
| 1156 | |||
| 1157 | pub const Section = struct { | ||
| 1158 | offset: usize, | ||
| 1159 | size: usize, | ||
| 1160 | }; | ||
| 1161 | |||
| 1162 | pub fn allocator(self: DwarfInfo) *mem.Allocator { | ||
| 1163 | return self.abbrev_table_list.allocator; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | pub fn readString(self: *DwarfInfo) ![]u8 { | ||
| 1167 | return readStringRaw(self.allocator(), self.dwarf_in_stream); | ||
| 1168 | } | ||
| 1169 | }; | ||
| 1170 | |||
| 1171 | pub const DebugInfo = switch (builtin.os) { | ||
| 1172 | builtin.Os.macosx => struct { | ||
| 1173 | symbols: []const MachoSymbol, | ||
| 1174 | strings: []const u8, | ||
| 1175 | ofiles: OFileTable, | ||
| 1176 | |||
| 1177 | const OFileTable = std.HashMap( | ||
| 1178 | *macho.nlist_64, | ||
| 1179 | MachOFile, | ||
| 1180 | std.hash_map.getHashPtrAddrFn(*macho.nlist_64), | ||
| 1181 | std.hash_map.getTrivialEqlFn(*macho.nlist_64), | ||
| 1182 | ); | ||
| 1183 | |||
| 1184 | pub fn allocator(self: DebugInfo) *mem.Allocator { | ||
| 1185 | return self.ofiles.allocator; | ||
| 1186 | } | ||
| 1187 | }, | ||
| 1188 | builtin.Os.uefi, builtin.Os.windows => struct { | ||
| 1189 | pdb: pdb.Pdb, | ||
| 1190 | coff: *coff.Coff, | ||
| 1191 | sect_contribs: []pdb.SectionContribEntry, | ||
| 1192 | modules: []Module, | ||
| 1193 | }, | ||
| 1194 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo, | ||
| 1195 | else => @compileError("Unsupported OS"), | ||
| 1196 | }; | ||
| 1197 | |||
| 1198 | const PcRange = struct { | ||
| 1199 | start: u64, | ||
| 1200 | end: u64, | ||
| 1201 | }; | ||
| 1202 | |||
| 1203 | const CompileUnit = struct { | ||
| 1204 | version: u16, | ||
| 1205 | is_64: bool, | ||
| 1206 | die: *Die, | ||
| 1207 | index: usize, | ||
| 1208 | pc_range: ?PcRange, | ||
| 1209 | }; | ||
| 1210 | |||
| 1211 | const AbbrevTable = ArrayList(AbbrevTableEntry); | ||
| 1212 | |||
| 1213 | const AbbrevTableHeader = struct { | ||
| 1214 | // offset from .debug_abbrev | ||
| 1215 | offset: u64, | ||
| 1216 | table: AbbrevTable, | ||
| 1217 | }; | ||
| 1218 | |||
| 1219 | const AbbrevTableEntry = struct { | ||
| 1220 | has_children: bool, | ||
| 1221 | abbrev_code: u64, | ||
| 1222 | tag_id: u64, | ||
| 1223 | attrs: ArrayList(AbbrevAttr), | ||
| 1224 | }; | ||
| 1225 | |||
| 1226 | const AbbrevAttr = struct { | ||
| 1227 | attr_id: u64, | ||
| 1228 | form_id: u64, | ||
| 1229 | }; | ||
| 1230 | |||
| 1231 | const FormValue = union(enum) { | ||
| 1232 | Address: u64, | ||
| 1233 | Block: []u8, | ||
| 1234 | Const: Constant, | ||
| 1235 | ExprLoc: []u8, | ||
| 1236 | Flag: bool, | ||
| 1237 | SecOffset: u64, | ||
| 1238 | Ref: []u8, | ||
| 1239 | RefAddr: u64, | ||
| 1240 | RefSig8: u64, | ||
| 1241 | String: []u8, | ||
| 1242 | StrPtr: u64, | ||
| 1243 | }; | ||
| 1244 | |||
| 1245 | const Constant = struct { | ||
| 1246 | payload: []u8, | ||
| 1247 | signed: bool, | ||
| 1248 | |||
| 1249 | fn asUnsignedLe(self: *const Constant) !u64 { | ||
| 1250 | if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; | ||
| 1251 | if (self.signed) return error.InvalidDebugInfo; | ||
| 1252 | return mem.readVarInt(u64, self.payload, builtin.Endian.Little); | ||
| 1253 | } | ||
| 1254 | }; | ||
| 1255 | |||
| 1256 | const Die = struct { | ||
| 1257 | tag_id: u64, | ||
| 1258 | has_children: bool, | ||
| 1259 | attrs: ArrayList(Attr), | ||
| 1260 | |||
| 1261 | const Attr = struct { | ||
| 1262 | id: u64, | ||
| 1263 | value: FormValue, | ||
| 1264 | }; | ||
| 1265 | |||
| 1266 | fn getAttr(self: *const Die, id: u64) ?*const FormValue { | ||
| 1267 | for (self.attrs.toSliceConst()) |*attr| { | ||
| 1268 | if (attr.id == id) return &attr.value; | ||
| 1269 | } | ||
| 1270 | return null; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | fn getAttrAddr(self: *const Die, id: u64) !u64 { | ||
| 1274 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1275 | return switch (form_value.*) { | ||
| 1276 | FormValue.Address => |value| value, | ||
| 1277 | else => error.InvalidDebugInfo, | ||
| 1278 | }; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | fn getAttrSecOffset(self: *const Die, id: u64) !u64 { | ||
| 1282 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1283 | return switch (form_value.*) { | ||
| 1284 | FormValue.Const => |value| value.asUnsignedLe(), | ||
| 1285 | FormValue.SecOffset => |value| value, | ||
| 1286 | else => error.InvalidDebugInfo, | ||
| 1287 | }; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { | ||
| 1291 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1292 | return switch (form_value.*) { | ||
| 1293 | FormValue.Const => |value| value.asUnsignedLe(), | ||
| 1294 | else => error.InvalidDebugInfo, | ||
| 1295 | }; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { | ||
| 1299 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1300 | return switch (form_value.*) { | ||
| 1301 | FormValue.String => |value| value, | ||
| 1302 | FormValue.StrPtr => |offset| getString(di, offset), | ||
| 1303 | else => error.InvalidDebugInfo, | ||
| 1304 | }; | ||
| 1305 | } | ||
| 1306 | }; | ||
| 1307 | |||
| 1308 | const FileEntry = struct { | ||
| 1309 | file_name: []const u8, | ||
| 1310 | dir_index: usize, | ||
| 1311 | mtime: usize, | ||
| 1312 | len_bytes: usize, | ||
| 1313 | }; | ||
| 1314 | |||
| 1315 | pub const LineInfo = struct { | ||
| 1316 | line: usize, | ||
| 1317 | column: usize, | ||
| 1318 | file_name: []const u8, | ||
| 1319 | allocator: ?*mem.Allocator, | ||
| 1320 | |||
| 1321 | fn deinit(self: LineInfo) void { | ||
| 1322 | const allocator = self.allocator orelse return; | ||
| 1323 | allocator.free(self.file_name); | ||
| 1324 | } | ||
| 1325 | }; | ||
| 1326 | |||
| 1327 | const LineNumberProgram = struct { | ||
| 1328 | address: usize, | ||
| 1329 | file: usize, | ||
| 1330 | line: isize, | ||
| 1331 | column: usize, | ||
| 1332 | is_stmt: bool, | ||
| 1333 | basic_block: bool, | ||
| 1334 | end_sequence: bool, | ||
| 1335 | |||
| 1336 | target_address: usize, | ||
| 1337 | include_dirs: []const []const u8, | ||
| 1338 | file_entries: *ArrayList(FileEntry), | ||
| 1339 | |||
| 1340 | prev_address: usize, | ||
| 1341 | prev_file: usize, | ||
| 1342 | prev_line: isize, | ||
| 1343 | prev_column: usize, | ||
| 1344 | prev_is_stmt: bool, | ||
| 1345 | prev_basic_block: bool, | ||
| 1346 | prev_end_sequence: bool, | ||
| 1347 | |||
| 1348 | pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { | ||
| 1349 | return LineNumberProgram{ | ||
| 1350 | .address = 0, | ||
| 1351 | .file = 1, | ||
| 1352 | .line = 1, | ||
| 1353 | .column = 0, | ||
| 1354 | .is_stmt = is_stmt, | ||
| 1355 | .basic_block = false, | ||
| 1356 | .end_sequence = false, | ||
| 1357 | .include_dirs = include_dirs, | ||
| 1358 | .file_entries = file_entries, | ||
| 1359 | .target_address = target_address, | ||
| 1360 | .prev_address = 0, | ||
| 1361 | .prev_file = undefined, | ||
| 1362 | .prev_line = undefined, | ||
| 1363 | .prev_column = undefined, | ||
| 1364 | .prev_is_stmt = undefined, | ||
| 1365 | .prev_basic_block = undefined, | ||
| 1366 | .prev_end_sequence = undefined, | ||
| 1367 | }; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { | ||
| 1371 | if (self.target_address >= self.prev_address and self.target_address < self.address) { | ||
| 1372 | const file_entry = if (self.prev_file == 0) { | ||
| 1373 | return error.MissingDebugInfo; | ||
| 1374 | } else if (self.prev_file - 1 >= self.file_entries.len) { | ||
| 1375 | return error.InvalidDebugInfo; | ||
| 1376 | } else | ||
| 1377 | &self.file_entries.items[self.prev_file - 1]; | ||
| 1378 | |||
| 1379 | const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { | ||
| 1380 | return error.InvalidDebugInfo; | ||
| 1381 | } else | ||
| 1382 | self.include_dirs[file_entry.dir_index]; | ||
| 1383 | const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); | ||
| 1384 | errdefer self.file_entries.allocator.free(file_name); | ||
| 1385 | return LineInfo{ | ||
| 1386 | .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, | ||
| 1387 | .column = self.prev_column, | ||
| 1388 | .file_name = file_name, | ||
| 1389 | .allocator = self.file_entries.allocator, | ||
| 1390 | }; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | self.prev_address = self.address; | ||
| 1394 | self.prev_file = self.file; | ||
| 1395 | self.prev_line = self.line; | ||
| 1396 | self.prev_column = self.column; | ||
| 1397 | self.prev_is_stmt = self.is_stmt; | ||
| 1398 | self.prev_basic_block = self.basic_block; | ||
| 1399 | self.prev_end_sequence = self.end_sequence; | ||
| 1400 | return null; | ||
| 1401 | } | ||
| 1402 | }; | ||
| 1403 | |||
| 1404 | fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { | ||
| 1405 | var buf = ArrayList(u8).init(allocator); | ||
| 1406 | while (true) { | ||
| 1407 | const byte = try in_stream.readByte(); | ||
| 1408 | if (byte == 0) break; | ||
| 1409 | try buf.append(byte); | ||
| 1410 | } | ||
| 1411 | return buf.toSlice(); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | fn getString(di: *DwarfInfo, offset: u64) ![]u8 { | ||
| 1415 | const pos = di.debug_str.offset + offset; | ||
| 1416 | try di.dwarf_seekable_stream.seekTo(pos); | ||
| 1417 | return di.readString(); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { | ||
| 1421 | const buf = try allocator.alloc(u8, size); | ||
| 1422 | errdefer allocator.free(buf); | ||
| 1423 | if ((try in_stream.read(buf)) < size) return error.EndOfFile; | ||
| 1424 | return buf; | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | ||
| 1428 | const buf = try readAllocBytes(allocator, in_stream, size); | ||
| 1429 | return FormValue{ .Block = buf }; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | ||
| 1433 | const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size); | ||
| 1434 | return parseFormValueBlockLen(allocator, in_stream, block_len); | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { | ||
| 1438 | return FormValue{ | ||
| 1439 | .Const = Constant{ | ||
| 1440 | .signed = signed, | ||
| 1441 | .payload = try readAllocBytes(allocator, in_stream, size), | ||
| 1442 | }, | ||
| 1443 | }; | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { | ||
| 1447 | return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32)); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | fn parseFormValueTargetAddrSize(in_stream: var) !u64 { | ||
| 1451 | return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | ||
| 1455 | const buf = try readAllocBytes(allocator, in_stream, size); | ||
| 1456 | return FormValue{ .Ref = buf }; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue { | ||
| 1460 | const block_len = try in_stream.readIntLittle(T); | ||
| 1461 | return parseFormValueRefLen(allocator, in_stream, block_len); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { | ||
| 1465 | return switch (form_id) { | ||
| 1466 | DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, | ||
| 1467 | DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), | ||
| 1468 | DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), | ||
| 1469 | DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), | ||
| 1470 | DW.FORM_block => x: { | ||
| 1471 | const block_len = try readULeb128(in_stream); | ||
| 1472 | return parseFormValueBlockLen(allocator, in_stream, block_len); | ||
| 1473 | }, | ||
| 1474 | DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), | ||
| 1475 | DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), | ||
| 1476 | DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), | ||
| 1477 | DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), | ||
| 1478 | DW.FORM_udata, DW.FORM_sdata => { | ||
| 1479 | const block_len = try readULeb128(in_stream); | ||
| 1480 | const signed = form_id == DW.FORM_sdata; | ||
| 1481 | return parseFormValueConstant(allocator, in_stream, signed, block_len); | ||
| 1482 | }, | ||
| 1483 | DW.FORM_exprloc => { | ||
| 1484 | const size = try readULeb128(in_stream); | ||
| 1485 | const buf = try readAllocBytes(allocator, in_stream, size); | ||
| 1486 | return FormValue{ .ExprLoc = buf }; | ||
| 1487 | }, | ||
| 1488 | DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 }, | ||
| 1489 | DW.FORM_flag_present => FormValue{ .Flag = true }, | ||
| 1490 | DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, | ||
| 1491 | |||
| 1492 | DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), | ||
| 1493 | DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), | ||
| 1494 | DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, u32), | ||
| 1495 | DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64), | ||
| 1496 | DW.FORM_ref_udata => { | ||
| 1497 | const ref_len = try readULeb128(in_stream); | ||
| 1498 | return parseFormValueRefLen(allocator, in_stream, ref_len); | ||
| 1499 | }, | ||
| 1500 | |||
| 1501 | DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, | ||
| 1502 | DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLittle(u64) }, | ||
| 1503 | |||
| 1504 | DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) }, | ||
| 1505 | DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, | ||
| 1506 | DW.FORM_indirect => { | ||
| 1507 | const child_form_id = try readULeb128(in_stream); | ||
| 1508 | return parseFormValue(allocator, in_stream, child_form_id, is_64); | ||
| 1509 | }, | ||
| 1510 | else => error.InvalidDebugInfo, | ||
| 1511 | }; | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { | ||
| 1515 | var result = AbbrevTable.init(di.allocator()); | ||
| 1516 | while (true) { | ||
| 1517 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | ||
| 1518 | if (abbrev_code == 0) return result; | ||
| 1519 | try result.append(AbbrevTableEntry{ | ||
| 1520 | .abbrev_code = abbrev_code, | ||
| 1521 | .tag_id = try readULeb128(di.dwarf_in_stream), | ||
| 1522 | .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, | ||
| 1523 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), | ||
| 1524 | }); | ||
| 1525 | const attrs = &result.items[result.len - 1].attrs; | ||
| 1526 | |||
| 1527 | while (true) { | ||
| 1528 | const attr_id = try readULeb128(di.dwarf_in_stream); | ||
| 1529 | const form_id = try readULeb128(di.dwarf_in_stream); | ||
| 1530 | if (attr_id == 0 and form_id == 0) break; | ||
| 1531 | try attrs.append(AbbrevAttr{ | ||
| 1532 | .attr_id = attr_id, | ||
| 1533 | .form_id = form_id, | ||
| 1534 | }); | ||
| 1535 | } | ||
| 1536 | } | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, | ||
| 1540 | /// seeks in the stream and parses it. | ||
| 1541 | fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { | ||
| 1542 | for (di.abbrev_table_list.toSlice()) |*header| { | ||
| 1543 | if (header.offset == abbrev_offset) { | ||
| 1544 | return &header.table; | ||
| 1545 | } | ||
| 1546 | } | ||
| 1547 | try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); | ||
| 1548 | try di.abbrev_table_list.append(AbbrevTableHeader{ | ||
| 1549 | .offset = abbrev_offset, | ||
| 1550 | .table = try parseAbbrevTable(di), | ||
| 1551 | }); | ||
| 1552 | return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { | ||
| 1556 | for (abbrev_table.toSliceConst()) |*table_entry| { | ||
| 1557 | if (table_entry.abbrev_code == abbrev_code) return table_entry; | ||
| 1558 | } | ||
| 1559 | return null; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | ||
| 1563 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | ||
| 1564 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; | ||
| 1565 | |||
| 1566 | var result = Die{ | ||
| 1567 | .tag_id = table_entry.tag_id, | ||
| 1568 | .has_children = table_entry.has_children, | ||
| 1569 | .attrs = ArrayList(Die.Attr).init(di.allocator()), | ||
| 1570 | }; | ||
| 1571 | try result.attrs.resize(table_entry.attrs.len); | ||
| 1572 | for (table_entry.attrs.toSliceConst()) |attr, i| { | ||
| 1573 | result.attrs.items[i] = Die.Attr{ | ||
| 1574 | .id = attr.attr_id, | ||
| 1575 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), | ||
| 1576 | }; | ||
| 1577 | } | ||
| 1578 | return result; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { | ||
| 1582 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; | ||
| 1583 | const gop = try di.ofiles.getOrPut(ofile); | ||
| 1584 | const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { | ||
| 1585 | errdefer _ = di.ofiles.remove(ofile); | ||
| 1586 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); | ||
| 1587 | |||
| 1588 | gop.kv.value = MachOFile{ | ||
| 1589 | .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)), | ||
| 1590 | .sect_debug_info = null, | ||
| 1591 | .sect_debug_line = null, | ||
| 1592 | }; | ||
| 1593 | const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr); | ||
| 1594 | if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; | ||
| 1595 | |||
| 1596 | const hdr_base = @ptrCast([*]const u8, hdr); | ||
| 1597 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); | ||
| 1598 | var ncmd: u32 = hdr.ncmds; | ||
| 1599 | const segcmd = while (ncmd != 0) : (ncmd -= 1) { | ||
| 1600 | const lc = @ptrCast(*const std.macho.load_command, ptr); | ||
| 1601 | switch (lc.cmd) { | ||
| 1602 | std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), | ||
| 1603 | else => {}, | ||
| 1604 | } | ||
| 1605 | ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 | ||
| 1606 | } else { | ||
| 1607 | return error.MissingDebugInfo; | ||
| 1608 | }; | ||
| 1609 | const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; | ||
| 1610 | for (sections) |*sect| { | ||
| 1611 | if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and | ||
| 1612 | (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) | ||
| 1613 | { | ||
| 1614 | const sect_name = mem.toSliceConst(u8, &sect.sectname); | ||
| 1615 | if (mem.eql(u8, sect_name, "__debug_line")) { | ||
| 1616 | gop.kv.value.sect_debug_line = sect; | ||
| 1617 | } else if (mem.eql(u8, sect_name, "__debug_info")) { | ||
| 1618 | gop.kv.value.sect_debug_info = sect; | ||
| 1619 | } | ||
| 1620 | } | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | break :blk &gop.kv.value; | ||
| 1624 | }; | ||
| 1625 | |||
| 1626 | const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; | ||
| 1627 | var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; | ||
| 1628 | |||
| 1629 | var is_64: bool = undefined; | ||
| 1630 | const unit_length = try readInitialLengthMem(&ptr, &is_64); | ||
| 1631 | if (unit_length == 0) return error.MissingDebugInfo; | ||
| 1632 | |||
| 1633 | const version = readIntMem(&ptr, u16, builtin.Endian.Little); | ||
| 1634 | // TODO support 3 and 5 | ||
| 1635 | if (version != 2 and version != 4) return error.InvalidDebugInfo; | ||
| 1636 | |||
| 1637 | const prologue_length = if (is_64) | ||
| 1638 | readIntMem(&ptr, u64, builtin.Endian.Little) | ||
| 1639 | else | ||
| 1640 | readIntMem(&ptr, u32, builtin.Endian.Little); | ||
| 1641 | const prog_start = ptr + prologue_length; | ||
| 1642 | |||
| 1643 | const minimum_instruction_length = readByteMem(&ptr); | ||
| 1644 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; | ||
| 1645 | |||
| 1646 | if (version >= 4) { | ||
| 1647 | // maximum_operations_per_instruction | ||
| 1648 | ptr += 1; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | const default_is_stmt = readByteMem(&ptr) != 0; | ||
| 1652 | const line_base = readByteSignedMem(&ptr); | ||
| 1653 | |||
| 1654 | const line_range = readByteMem(&ptr); | ||
| 1655 | if (line_range == 0) return error.InvalidDebugInfo; | ||
| 1656 | |||
| 1657 | const opcode_base = readByteMem(&ptr); | ||
| 1658 | |||
| 1659 | const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; | ||
| 1660 | ptr += opcode_base - 1; | ||
| 1661 | |||
| 1662 | var include_directories = ArrayList([]const u8).init(di.allocator()); | ||
| 1663 | try include_directories.append(""); | ||
| 1664 | while (true) { | ||
| 1665 | const dir = readStringMem(&ptr); | ||
| 1666 | if (dir.len == 0) break; | ||
| 1667 | try include_directories.append(dir); | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | var file_entries = ArrayList(FileEntry).init(di.allocator()); | ||
| 1671 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); | ||
| 1672 | |||
| 1673 | while (true) { | ||
| 1674 | const file_name = readStringMem(&ptr); | ||
| 1675 | if (file_name.len == 0) break; | ||
| 1676 | const dir_index = try readULeb128Mem(&ptr); | ||
| 1677 | const mtime = try readULeb128Mem(&ptr); | ||
| 1678 | const len_bytes = try readULeb128Mem(&ptr); | ||
| 1679 | try file_entries.append(FileEntry{ | ||
| 1680 | .file_name = file_name, | ||
| 1681 | .dir_index = dir_index, | ||
| 1682 | .mtime = mtime, | ||
| 1683 | .len_bytes = len_bytes, | ||
| 1684 | }); | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | ptr = prog_start; | ||
| 1688 | while (true) { | ||
| 1689 | const opcode = readByteMem(&ptr); | ||
| 1690 | |||
| 1691 | if (opcode == DW.LNS_extended_op) { | ||
| 1692 | const op_size = try readULeb128Mem(&ptr); | ||
| 1693 | if (op_size < 1) return error.InvalidDebugInfo; | ||
| 1694 | var sub_op = readByteMem(&ptr); | ||
| 1695 | switch (sub_op) { | ||
| 1696 | DW.LNE_end_sequence => { | ||
| 1697 | prog.end_sequence = true; | ||
| 1698 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1699 | return error.MissingDebugInfo; | ||
| 1700 | }, | ||
| 1701 | DW.LNE_set_address => { | ||
| 1702 | const addr = readIntMem(&ptr, usize, builtin.Endian.Little); | ||
| 1703 | prog.address = symbol.reloc + addr; | ||
| 1704 | }, | ||
| 1705 | DW.LNE_define_file => { | ||
| 1706 | const file_name = readStringMem(&ptr); | ||
| 1707 | const dir_index = try readULeb128Mem(&ptr); | ||
| 1708 | const mtime = try readULeb128Mem(&ptr); | ||
| 1709 | const len_bytes = try readULeb128Mem(&ptr); | ||
| 1710 | try file_entries.append(FileEntry{ | ||
| 1711 | .file_name = file_name, | ||
| 1712 | .dir_index = dir_index, | ||
| 1713 | .mtime = mtime, | ||
| 1714 | .len_bytes = len_bytes, | ||
| 1715 | }); | ||
| 1716 | }, | ||
| 1717 | else => { | ||
| 1718 | ptr += op_size - 1; | ||
| 1719 | }, | ||
| 1720 | } | ||
| 1721 | } else if (opcode >= opcode_base) { | ||
| 1722 | // special opcodes | ||
| 1723 | const adjusted_opcode = opcode - opcode_base; | ||
| 1724 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); | ||
| 1725 | const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); | ||
| 1726 | prog.line += inc_line; | ||
| 1727 | prog.address += inc_addr; | ||
| 1728 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1729 | prog.basic_block = false; | ||
| 1730 | } else { | ||
| 1731 | switch (opcode) { | ||
| 1732 | DW.LNS_copy => { | ||
| 1733 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1734 | prog.basic_block = false; | ||
| 1735 | }, | ||
| 1736 | DW.LNS_advance_pc => { | ||
| 1737 | const arg = try readULeb128Mem(&ptr); | ||
| 1738 | prog.address += arg * minimum_instruction_length; | ||
| 1739 | }, | ||
| 1740 | DW.LNS_advance_line => { | ||
| 1741 | const arg = try readILeb128Mem(&ptr); | ||
| 1742 | prog.line += arg; | ||
| 1743 | }, | ||
| 1744 | DW.LNS_set_file => { | ||
| 1745 | const arg = try readULeb128Mem(&ptr); | ||
| 1746 | prog.file = arg; | ||
| 1747 | }, | ||
| 1748 | DW.LNS_set_column => { | ||
| 1749 | const arg = try readULeb128Mem(&ptr); | ||
| 1750 | prog.column = arg; | ||
| 1751 | }, | ||
| 1752 | DW.LNS_negate_stmt => { | ||
| 1753 | prog.is_stmt = !prog.is_stmt; | ||
| 1754 | }, | ||
| 1755 | DW.LNS_set_basic_block => { | ||
| 1756 | prog.basic_block = true; | ||
| 1757 | }, | ||
| 1758 | DW.LNS_const_add_pc => { | ||
| 1759 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); | ||
| 1760 | prog.address += inc_addr; | ||
| 1761 | }, | ||
| 1762 | DW.LNS_fixed_advance_pc => { | ||
| 1763 | const arg = readIntMem(&ptr, u16, builtin.Endian.Little); | ||
| 1764 | prog.address += arg; | ||
| 1765 | }, | ||
| 1766 | DW.LNS_set_prologue_end => {}, | ||
| 1767 | else => { | ||
| 1768 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; | ||
| 1769 | const len_bytes = standard_opcode_lengths[opcode - 1]; | ||
| 1770 | ptr += len_bytes; | ||
| 1771 | }, | ||
| 1772 | } | ||
| 1773 | } | ||
| 1774 | } | ||
| 1775 | |||
| 1776 | return error.MissingDebugInfo; | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { | ||
| 1780 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); | ||
| 1781 | |||
| 1782 | const debug_line_end = di.debug_line.offset + di.debug_line.size; | ||
| 1783 | var this_offset = di.debug_line.offset; | ||
| 1784 | var this_index: usize = 0; | ||
| 1785 | |||
| 1786 | while (this_offset < debug_line_end) : (this_index += 1) { | ||
| 1787 | try di.dwarf_seekable_stream.seekTo(this_offset); | ||
| 1788 | |||
| 1789 | var is_64: bool = undefined; | ||
| 1790 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | ||
| 1791 | if (unit_length == 0) return error.MissingDebugInfo; | ||
| 1792 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); | ||
| 1793 | |||
| 1794 | if (compile_unit.index != this_index) { | ||
| 1795 | this_offset += next_offset; | ||
| 1796 | continue; | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | ||
| 1800 | // TODO support 3 and 5 | ||
| 1801 | if (version != 2 and version != 4) return error.InvalidDebugInfo; | ||
| 1802 | |||
| 1803 | 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); | ||
| 1804 | const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; | ||
| 1805 | |||
| 1806 | const minimum_instruction_length = try di.dwarf_in_stream.readByte(); | ||
| 1807 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; | ||
| 1808 | |||
| 1809 | if (version >= 4) { | ||
| 1810 | // maximum_operations_per_instruction | ||
| 1811 | _ = try di.dwarf_in_stream.readByte(); | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; | ||
| 1815 | const line_base = try di.dwarf_in_stream.readByteSigned(); | ||
| 1816 | |||
| 1817 | const line_range = try di.dwarf_in_stream.readByte(); | ||
| 1818 | if (line_range == 0) return error.InvalidDebugInfo; | ||
| 1819 | |||
| 1820 | const opcode_base = try di.dwarf_in_stream.readByte(); | ||
| 1821 | |||
| 1822 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); | ||
| 1823 | |||
| 1824 | { | ||
| 1825 | var i: usize = 0; | ||
| 1826 | while (i < opcode_base - 1) : (i += 1) { | ||
| 1827 | standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); | ||
| 1828 | } | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | var include_directories = ArrayList([]u8).init(di.allocator()); | ||
| 1832 | try include_directories.append(compile_unit_cwd); | ||
| 1833 | while (true) { | ||
| 1834 | const dir = try di.readString(); | ||
| 1835 | if (dir.len == 0) break; | ||
| 1836 | try include_directories.append(dir); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | var file_entries = ArrayList(FileEntry).init(di.allocator()); | ||
| 1840 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); | ||
| 1841 | |||
| 1842 | while (true) { | ||
| 1843 | const file_name = try di.readString(); | ||
| 1844 | if (file_name.len == 0) break; | ||
| 1845 | const dir_index = try readULeb128(di.dwarf_in_stream); | ||
| 1846 | const mtime = try readULeb128(di.dwarf_in_stream); | ||
| 1847 | const len_bytes = try readULeb128(di.dwarf_in_stream); | ||
| 1848 | try file_entries.append(FileEntry{ | ||
| 1849 | .file_name = file_name, | ||
| 1850 | .dir_index = dir_index, | ||
| 1851 | .mtime = mtime, | ||
| 1852 | .len_bytes = len_bytes, | ||
| 1853 | }); | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | try di.dwarf_seekable_stream.seekTo(prog_start_offset); | ||
| 1857 | |||
| 1858 | while (true) { | ||
| 1859 | const opcode = try di.dwarf_in_stream.readByte(); | ||
| 1860 | |||
| 1861 | if (opcode == DW.LNS_extended_op) { | ||
| 1862 | const op_size = try readULeb128(di.dwarf_in_stream); | ||
| 1863 | if (op_size < 1) return error.InvalidDebugInfo; | ||
| 1864 | var sub_op = try di.dwarf_in_stream.readByte(); | ||
| 1865 | switch (sub_op) { | ||
| 1866 | DW.LNE_end_sequence => { | ||
| 1867 | prog.end_sequence = true; | ||
| 1868 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1869 | return error.MissingDebugInfo; | ||
| 1870 | }, | ||
| 1871 | DW.LNE_set_address => { | ||
| 1872 | const addr = try di.dwarf_in_stream.readInt(usize, di.endian); | ||
| 1873 | prog.address = addr; | ||
| 1874 | }, | ||
| 1875 | DW.LNE_define_file => { | ||
| 1876 | const file_name = try di.readString(); | ||
| 1877 | const dir_index = try readULeb128(di.dwarf_in_stream); | ||
| 1878 | const mtime = try readULeb128(di.dwarf_in_stream); | ||
| 1879 | const len_bytes = try readULeb128(di.dwarf_in_stream); | ||
| 1880 | try file_entries.append(FileEntry{ | ||
| 1881 | .file_name = file_name, | ||
| 1882 | .dir_index = dir_index, | ||
| 1883 | .mtime = mtime, | ||
| 1884 | .len_bytes = len_bytes, | ||
| 1885 | }); | ||
| 1886 | }, | ||
| 1887 | else => { | ||
| 1888 | const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; | ||
| 1889 | try di.dwarf_seekable_stream.seekForward(fwd_amt); | ||
| 1890 | }, | ||
| 1891 | } | ||
| 1892 | } else if (opcode >= opcode_base) { | ||
| 1893 | // special opcodes | ||
| 1894 | const adjusted_opcode = opcode - opcode_base; | ||
| 1895 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); | ||
| 1896 | const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); | ||
| 1897 | prog.line += inc_line; | ||
| 1898 | prog.address += inc_addr; | ||
| 1899 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1900 | prog.basic_block = false; | ||
| 1901 | } else { | ||
| 1902 | switch (opcode) { | ||
| 1903 | DW.LNS_copy => { | ||
| 1904 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1905 | prog.basic_block = false; | ||
| 1906 | }, | ||
| 1907 | DW.LNS_advance_pc => { | ||
| 1908 | const arg = try readULeb128(di.dwarf_in_stream); | ||
| 1909 | prog.address += arg * minimum_instruction_length; | ||
| 1910 | }, | ||
| 1911 | DW.LNS_advance_line => { | ||
| 1912 | const arg = try readILeb128(di.dwarf_in_stream); | ||
| 1913 | prog.line += arg; | ||
| 1914 | }, | ||
| 1915 | DW.LNS_set_file => { | ||
| 1916 | const arg = try readULeb128(di.dwarf_in_stream); | ||
| 1917 | prog.file = arg; | ||
| 1918 | }, | ||
| 1919 | DW.LNS_set_column => { | ||
| 1920 | const arg = try readULeb128(di.dwarf_in_stream); | ||
| 1921 | prog.column = arg; | ||
| 1922 | }, | ||
| 1923 | DW.LNS_negate_stmt => { | ||
| 1924 | prog.is_stmt = !prog.is_stmt; | ||
| 1925 | }, | ||
| 1926 | DW.LNS_set_basic_block => { | ||
| 1927 | prog.basic_block = true; | ||
| 1928 | }, | ||
| 1929 | DW.LNS_const_add_pc => { | ||
| 1930 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); | ||
| 1931 | prog.address += inc_addr; | ||
| 1932 | }, | ||
| 1933 | DW.LNS_fixed_advance_pc => { | ||
| 1934 | const arg = try di.dwarf_in_stream.readInt(u16, di.endian); | ||
| 1935 | prog.address += arg; | ||
| 1936 | }, | ||
| 1937 | DW.LNS_set_prologue_end => {}, | ||
| 1938 | else => { | ||
| 1939 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; | ||
| 1940 | const len_bytes = standard_opcode_lengths[opcode - 1]; | ||
| 1941 | try di.dwarf_seekable_stream.seekForward(len_bytes); | ||
| 1942 | }, | ||
| 1943 | } | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | this_offset += next_offset; | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | return error.MissingDebugInfo; | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | ||
| 1954 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | ||
| 1955 | var this_unit_offset = di.debug_info.offset; | ||
| 1956 | var cu_index: usize = 0; | ||
| 1957 | |||
| 1958 | while (this_unit_offset < debug_info_end) { | ||
| 1959 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); | ||
| 1960 | |||
| 1961 | var is_64: bool = undefined; | ||
| 1962 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | ||
| 1963 | if (unit_length == 0) return; | ||
| 1964 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); | ||
| 1965 | |||
| 1966 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | ||
| 1967 | if (version < 2 or version > 5) return error.InvalidDebugInfo; | ||
| 1968 | |||
| 1969 | 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); | ||
| 1970 | |||
| 1971 | const address_size = try di.dwarf_in_stream.readByte(); | ||
| 1972 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | ||
| 1973 | |||
| 1974 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); | ||
| 1975 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); | ||
| 1976 | |||
| 1977 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); | ||
| 1978 | |||
| 1979 | const compile_unit_die = try di.allocator().create(Die); | ||
| 1980 | compile_unit_die.* = try parseDie(di, abbrev_table, is_64); | ||
| 1981 | |||
| 1982 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; | ||
| 1983 | |||
| 1984 | const pc_range = x: { | ||
| 1985 | if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { | ||
| 1986 | if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { | ||
| 1987 | const pc_end = switch (high_pc_value.*) { | ||
| 1988 | FormValue.Address => |value| value, | ||
| 1989 | FormValue.Const => |value| b: { | ||
| 1990 | const offset = try value.asUnsignedLe(); | ||
| 1991 | break :b (low_pc + offset); | ||
| 1992 | }, | ||
| 1993 | else => return error.InvalidDebugInfo, | ||
| 1994 | }; | ||
| 1995 | break :x PcRange{ | ||
| 1996 | .start = low_pc, | ||
| 1997 | .end = pc_end, | ||
| 1998 | }; | ||
| 1999 | } else { | ||
| 2000 | break :x null; | ||
| 2001 | } | ||
| 2002 | } else |err| { | ||
| 2003 | if (err != error.MissingDebugInfo) return err; | ||
| 2004 | break :x null; | ||
| 2005 | } | ||
| 2006 | }; | ||
| 2007 | |||
| 2008 | try di.compile_unit_list.append(CompileUnit{ | ||
| 2009 | .version = version, | ||
| 2010 | .is_64 = is_64, | ||
| 2011 | .pc_range = pc_range, | ||
| 2012 | .die = compile_unit_die, | ||
| 2013 | .index = cu_index, | ||
| 2014 | }); | ||
| 2015 | |||
| 2016 | this_unit_offset += next_offset; | ||
| 2017 | cu_index += 1; | ||
| 2018 | } | ||
| 2019 | } | ||
| 2020 | |||
| 2021 | fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { | ||
| 2022 | for (di.compile_unit_list.toSlice()) |*compile_unit| { | ||
| 2023 | if (compile_unit.pc_range) |range| { | ||
| 2024 | if (target_address >= range.start and target_address < range.end) return compile_unit; | ||
| 2025 | } | ||
| 2026 | if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { | ||
| 2027 | var base_address: usize = 0; | ||
| 2028 | if (di.debug_ranges) |debug_ranges| { | ||
| 2029 | try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); | ||
| 2030 | while (true) { | ||
| 2031 | const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); | ||
| 2032 | const end_addr = try di.dwarf_in_stream.readIntLittle(usize); | ||
| 2033 | if (begin_addr == 0 and end_addr == 0) { | ||
| 2034 | break; | ||
| 2035 | } | ||
| 2036 | if (begin_addr == maxInt(usize)) { | ||
| 2037 | base_address = begin_addr; | ||
| 2038 | continue; | ||
| 2039 | } | ||
| 2040 | if (target_address >= begin_addr and target_address < end_addr) { | ||
| 2041 | return compile_unit; | ||
| 2042 | } | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | } else |err| { | ||
| 2046 | if (err != error.MissingDebugInfo) return err; | ||
| 2047 | continue; | ||
| 2048 | } | ||
| 2049 | } | ||
| 2050 | return error.MissingDebugInfo; | ||
| 2051 | } | ||
| 2052 | |||
| 2053 | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { | ||
| 2054 | // TODO https://github.com/ziglang/zig/issues/863 | ||
| 2055 | const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian); | ||
| 2056 | ptr.* += @sizeOf(T); | ||
| 2057 | return result; | ||
| 2058 | } | ||
| 2059 | |||
| 2060 | fn readByteMem(ptr: *[*]const u8) u8 { | ||
| 2061 | const result = ptr.*[0]; | ||
| 2062 | ptr.* += 1; | ||
| 2063 | return result; | ||
| 2064 | } | ||
| 2065 | |||
| 2066 | fn readByteSignedMem(ptr: *[*]const u8) i8 { | ||
| 2067 | return @bitCast(i8, readByteMem(ptr)); | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { | ||
| 2071 | // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 | ||
| 2072 | const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); | ||
| 2073 | is_64.* = (first_32_bits == 0xffffffff); | ||
| 2074 | if (is_64.*) { | ||
| 2075 | ptr.* += 4; | ||
| 2076 | const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); | ||
| 2077 | ptr.* += 8; | ||
| 2078 | return result; | ||
| 2079 | } else { | ||
| 2080 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; | ||
| 2081 | ptr.* += 4; | ||
| 2082 | return u64(first_32_bits); | ||
| 2083 | } | ||
| 2084 | } | ||
| 2085 | |||
| 2086 | fn readStringMem(ptr: *[*]const u8) []const u8 { | ||
| 2087 | const result = mem.toSliceConst(u8, ptr.*); | ||
| 2088 | ptr.* += result.len + 1; | ||
| 2089 | return result; | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | fn readULeb128Mem(ptr: *[*]const u8) !u64 { | ||
| 2093 | var result: u64 = 0; | ||
| 2094 | var shift: usize = 0; | ||
| 2095 | var i: usize = 0; | ||
| 2096 | |||
| 2097 | while (true) { | ||
| 2098 | const byte = ptr.*[i]; | ||
| 2099 | i += 1; | ||
| 2100 | |||
| 2101 | var operand: u64 = undefined; | ||
| 2102 | |||
| 2103 | if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2104 | |||
| 2105 | result |= operand; | ||
| 2106 | |||
| 2107 | if ((byte & 0b10000000) == 0) { | ||
| 2108 | ptr.* += i; | ||
| 2109 | return result; | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | shift += 7; | ||
| 2113 | } | ||
| 2114 | } | ||
| 2115 | fn readILeb128Mem(ptr: *[*]const u8) !i64 { | ||
| 2116 | var result: i64 = 0; | ||
| 2117 | var shift: usize = 0; | ||
| 2118 | var i: usize = 0; | ||
| 2119 | |||
| 2120 | while (true) { | ||
| 2121 | const byte = ptr.*[i]; | ||
| 2122 | i += 1; | ||
| 2123 | |||
| 2124 | var operand: i64 = undefined; | ||
| 2125 | if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2126 | |||
| 2127 | result |= operand; | ||
| 2128 | shift += 7; | ||
| 2129 | |||
| 2130 | if ((byte & 0b10000000) == 0) { | ||
| 2131 | if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); | ||
| 2132 | ptr.* += i; | ||
| 2133 | return result; | ||
| 2134 | } | ||
| 2135 | } | ||
| 2136 | } | ||
| 2137 | |||
| 2138 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { | ||
| 2139 | const first_32_bits = try in_stream.readIntLittle(u32); | ||
| 2140 | is_64.* = (first_32_bits == 0xffffffff); | ||
| 2141 | if (is_64.*) { | ||
| 2142 | return in_stream.readIntLittle(u64); | ||
| 2143 | } else { | ||
| 2144 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; | ||
| 2145 | return u64(first_32_bits); | ||
| 2146 | } | ||
| 2147 | } | ||
| 2148 | |||
| 2149 | fn readULeb128(in_stream: var) !u64 { | ||
| 2150 | var result: u64 = 0; | ||
| 2151 | var shift: usize = 0; | ||
| 2152 | |||
| 2153 | while (true) { | ||
| 2154 | const byte = try in_stream.readByte(); | ||
| 2155 | |||
| 2156 | var operand: u64 = undefined; | ||
| 2157 | |||
| 2158 | if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2159 | |||
| 2160 | result |= operand; | ||
| 2161 | |||
| 2162 | if ((byte & 0b10000000) == 0) return result; | ||
| 2163 | |||
| 2164 | shift += 7; | ||
| 2165 | } | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | fn readILeb128(in_stream: var) !i64 { | ||
| 2169 | var result: i64 = 0; | ||
| 2170 | var shift: usize = 0; | ||
| 2171 | |||
| 2172 | while (true) { | ||
| 2173 | const byte = try in_stream.readByte(); | ||
| 2174 | |||
| 2175 | var operand: i64 = undefined; | ||
| 2176 | |||
| 2177 | if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2178 | |||
| 2179 | result |= operand; | ||
| 2180 | shift += 7; | ||
| 2181 | |||
| 2182 | if ((byte & 0b10000000) == 0) { | ||
| 2183 | if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); | ||
| 2184 | return result; | ||
| 2185 | } | ||
| 2186 | } | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | /// This should only be used in temporary test programs. | ||
| 2190 | pub const global_allocator = &global_fixed_allocator.allocator; | ||
| 2191 | var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]); | ||
| 2192 | var global_allocator_mem: [100 * 1024]u8 = undefined; | ||
| 2193 | |||
| 2194 | /// TODO multithreaded awareness | ||
| 2195 | var debug_info_allocator: ?*mem.Allocator = null; | ||
| 2196 | var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; | ||
| 2197 | var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; | ||
| 2198 | fn getDebugInfoAllocator() *mem.Allocator { | ||
| 2199 | if (debug_info_allocator) |a| return a; | ||
| 2200 | |||
| 2201 | debug_info_direct_allocator = std.heap.DirectAllocator.init(); | ||
| 2202 | debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator); | ||
| 2203 | debug_info_allocator = &debug_info_arena_allocator.allocator; | ||
| 2204 | return &debug_info_arena_allocator.allocator; | ||
| 2205 | } | ||
std/debug/failing_allocator.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | 3 | ||
| 4 | /// Allocator that fails after N allocations, useful for making sure out of | 4 | /// Allocator that fails after N allocations, useful for making sure out of |
std/debug/index.zig deleted-2205| ... | @@ -1,2205 +0,0 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const mem = std.mem; | ||
| 4 | const io = std.io; | ||
| 5 | const os = std.os; | ||
| 6 | const elf = std.elf; | ||
| 7 | const DW = std.dwarf; | ||
| 8 | const macho = std.macho; | ||
| 9 | const coff = std.coff; | ||
| 10 | const pdb = std.pdb; | ||
| 11 | const windows = os.windows; | ||
| 12 | const ArrayList = std.ArrayList; | ||
| 13 | const builtin = @import("builtin"); | ||
| 14 | const maxInt = std.math.maxInt; | ||
| 15 | |||
| 16 | pub const FailingAllocator = @import("failing_allocator.zig").FailingAllocator; | ||
| 17 | pub const failing_allocator = &FailingAllocator.init(global_allocator, 0).allocator; | ||
| 18 | |||
| 19 | pub const runtime_safety = switch (builtin.mode) { | ||
| 20 | builtin.Mode.Debug, builtin.Mode.ReleaseSafe => true, | ||
| 21 | builtin.Mode.ReleaseFast, builtin.Mode.ReleaseSmall => false, | ||
| 22 | }; | ||
| 23 | |||
| 24 | const Module = struct { | ||
| 25 | mod_info: pdb.ModInfo, | ||
| 26 | module_name: []u8, | ||
| 27 | obj_file_name: []u8, | ||
| 28 | |||
| 29 | populated: bool, | ||
| 30 | symbols: []u8, | ||
| 31 | subsect_info: []u8, | ||
| 32 | checksum_offset: ?usize, | ||
| 33 | }; | ||
| 34 | |||
| 35 | /// Tries to write to stderr, unbuffered, and ignores any error returned. | ||
| 36 | /// Does not append a newline. | ||
| 37 | var stderr_file: os.File = undefined; | ||
| 38 | var stderr_file_out_stream: os.File.OutStream = undefined; | ||
| 39 | |||
| 40 | var stderr_stream: ?*io.OutStream(os.File.WriteError) = null; | ||
| 41 | var stderr_mutex = std.Mutex.init(); | ||
| 42 | pub fn warn(comptime fmt: []const u8, args: ...) void { | ||
| 43 | const held = stderr_mutex.acquire(); | ||
| 44 | defer held.release(); | ||
| 45 | const stderr = getStderrStream() catch return; | ||
| 46 | stderr.print(fmt, args) catch return; | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn getStderrStream() !*io.OutStream(os.File.WriteError) { | ||
| 50 | if (stderr_stream) |st| { | ||
| 51 | return st; | ||
| 52 | } else { | ||
| 53 | stderr_file = try io.getStdErr(); | ||
| 54 | stderr_file_out_stream = stderr_file.outStream(); | ||
| 55 | const st = &stderr_file_out_stream.stream; | ||
| 56 | stderr_stream = st; | ||
| 57 | return st; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | /// TODO multithreaded awareness | ||
| 62 | var self_debug_info: ?DebugInfo = null; | ||
| 63 | |||
| 64 | pub fn getSelfDebugInfo() !*DebugInfo { | ||
| 65 | if (self_debug_info) |*info| { | ||
| 66 | return info; | ||
| 67 | } else { | ||
| 68 | self_debug_info = try openSelfDebugInfo(getDebugInfoAllocator()); | ||
| 69 | return &self_debug_info.?; | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | fn wantTtyColor() bool { | ||
| 74 | var bytes: [128]u8 = undefined; | ||
| 75 | const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; | ||
| 76 | return if (std.os.getEnvVarOwned(allocator, "ZIG_DEBUG_COLOR")) |_| true else |_| stderr_file.isTty(); | ||
| 77 | } | ||
| 78 | |||
| 79 | /// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned. | ||
| 80 | /// TODO multithreaded awareness | ||
| 81 | pub fn dumpCurrentStackTrace(start_addr: ?usize) void { | ||
| 82 | const stderr = getStderrStream() catch return; | ||
| 83 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 84 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | ||
| 85 | return; | ||
| 86 | }; | ||
| 87 | writeCurrentStackTrace(stderr, debug_info, wantTtyColor(), start_addr) catch |err| { | ||
| 88 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | ||
| 89 | return; | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | /// Returns a slice with the same pointer as addresses, with a potentially smaller len. | ||
| 94 | /// On Windows, when first_address is not null, we ask for at least 32 stack frames, | ||
| 95 | /// and then try to find the first address. If addresses.len is more than 32, we | ||
| 96 | /// capture that many stack frames exactly, and then look for the first address, | ||
| 97 | /// chopping off the irrelevant frames and shifting so that the returned addresses pointer | ||
| 98 | /// equals the passed in addresses pointer. | ||
| 99 | pub fn captureStackTrace(first_address: ?usize, stack_trace: *builtin.StackTrace) void { | ||
| 100 | switch (builtin.os) { | ||
| 101 | builtin.Os.windows => { | ||
| 102 | const addrs = stack_trace.instruction_addresses; | ||
| 103 | const u32_addrs_len = @intCast(u32, addrs.len); | ||
| 104 | const first_addr = first_address orelse { | ||
| 105 | stack_trace.index = windows.RtlCaptureStackBackTrace( | ||
| 106 | 0, | ||
| 107 | u32_addrs_len, | ||
| 108 | @ptrCast(**c_void, addrs.ptr), | ||
| 109 | null, | ||
| 110 | ); | ||
| 111 | return; | ||
| 112 | }; | ||
| 113 | var addr_buf_stack: [32]usize = undefined; | ||
| 114 | const addr_buf = if (addr_buf_stack.len > addrs.len) addr_buf_stack[0..] else addrs; | ||
| 115 | const n = windows.RtlCaptureStackBackTrace(0, u32_addrs_len, @ptrCast(**c_void, addr_buf.ptr), null); | ||
| 116 | const first_index = for (addr_buf[0..n]) |addr, i| { | ||
| 117 | if (addr == first_addr) { | ||
| 118 | break i; | ||
| 119 | } | ||
| 120 | } else { | ||
| 121 | stack_trace.index = 0; | ||
| 122 | return; | ||
| 123 | }; | ||
| 124 | const slice = addr_buf[first_index..n]; | ||
| 125 | // We use a for loop here because slice and addrs may alias. | ||
| 126 | for (slice) |addr, i| { | ||
| 127 | addrs[i] = addr; | ||
| 128 | } | ||
| 129 | stack_trace.index = slice.len; | ||
| 130 | }, | ||
| 131 | else => { | ||
| 132 | var it = StackIterator.init(first_address); | ||
| 133 | for (stack_trace.instruction_addresses) |*addr, i| { | ||
| 134 | addr.* = it.next() orelse { | ||
| 135 | stack_trace.index = i; | ||
| 136 | return; | ||
| 137 | }; | ||
| 138 | } | ||
| 139 | stack_trace.index = stack_trace.instruction_addresses.len; | ||
| 140 | }, | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | /// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned. | ||
| 145 | /// TODO multithreaded awareness | ||
| 146 | pub fn dumpStackTrace(stack_trace: builtin.StackTrace) void { | ||
| 147 | const stderr = getStderrStream() catch return; | ||
| 148 | const debug_info = getSelfDebugInfo() catch |err| { | ||
| 149 | stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return; | ||
| 150 | return; | ||
| 151 | }; | ||
| 152 | writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, wantTtyColor()) catch |err| { | ||
| 153 | stderr.print("Unable to dump stack trace: {}\n", @errorName(err)) catch return; | ||
| 154 | return; | ||
| 155 | }; | ||
| 156 | } | ||
| 157 | |||
| 158 | /// This function invokes undefined behavior when `ok` is `false`. | ||
| 159 | /// In Debug and ReleaseSafe modes, calls to this function are always | ||
| 160 | /// generated, and the `unreachable` statement triggers a panic. | ||
| 161 | /// In ReleaseFast and ReleaseSmall modes, calls to this function are | ||
| 162 | /// optimized away, and in fact the optimizer is able to use the assertion | ||
| 163 | /// in its heuristics. | ||
| 164 | /// Inside a test block, it is best to use the `std.testing` module rather | ||
| 165 | /// than this function, because this function may not detect a test failure | ||
| 166 | /// in ReleaseFast and ReleaseSafe mode. Outside of a test block, this assert | ||
| 167 | /// function is the correct function to use. | ||
| 168 | pub fn assert(ok: bool) void { | ||
| 169 | if (!ok) unreachable; // assertion failure | ||
| 170 | } | ||
| 171 | |||
| 172 | pub fn panic(comptime format: []const u8, args: ...) noreturn { | ||
| 173 | @setCold(true); | ||
| 174 | const first_trace_addr = @returnAddress(); | ||
| 175 | panicExtra(null, first_trace_addr, format, args); | ||
| 176 | } | ||
| 177 | |||
| 178 | /// TODO multithreaded awareness | ||
| 179 | var panicking: u8 = 0; // TODO make this a bool | ||
| 180 | |||
| 181 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: ...) noreturn { | ||
| 182 | @setCold(true); | ||
| 183 | |||
| 184 | if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { | ||
| 185 | // Panicked during a panic. | ||
| 186 | |||
| 187 | // TODO detect if a different thread caused the panic, because in that case | ||
| 188 | // we would want to return here instead of calling abort, so that the thread | ||
| 189 | // which first called panic can finish printing a stack trace. | ||
| 190 | os.abort(); | ||
| 191 | } | ||
| 192 | const stderr = getStderrStream() catch os.abort(); | ||
| 193 | stderr.print(format ++ "\n", args) catch os.abort(); | ||
| 194 | if (trace) |t| { | ||
| 195 | dumpStackTrace(t.*); | ||
| 196 | } | ||
| 197 | dumpCurrentStackTrace(first_trace_addr); | ||
| 198 | |||
| 199 | os.abort(); | ||
| 200 | } | ||
| 201 | |||
| 202 | const RED = "\x1b[31;1m"; | ||
| 203 | const GREEN = "\x1b[32;1m"; | ||
| 204 | const CYAN = "\x1b[36;1m"; | ||
| 205 | const WHITE = "\x1b[37;1m"; | ||
| 206 | const DIM = "\x1b[2m"; | ||
| 207 | const RESET = "\x1b[0m"; | ||
| 208 | |||
| 209 | pub fn writeStackTrace( | ||
| 210 | stack_trace: builtin.StackTrace, | ||
| 211 | out_stream: var, | ||
| 212 | allocator: *mem.Allocator, | ||
| 213 | debug_info: *DebugInfo, | ||
| 214 | tty_color: bool, | ||
| 215 | ) !void { | ||
| 216 | var frame_index: usize = 0; | ||
| 217 | var frames_left: usize = stack_trace.index; | ||
| 218 | |||
| 219 | while (frames_left != 0) : ({ | ||
| 220 | frames_left -= 1; | ||
| 221 | frame_index = (frame_index + 1) % stack_trace.instruction_addresses.len; | ||
| 222 | }) { | ||
| 223 | const return_address = stack_trace.instruction_addresses[frame_index]; | ||
| 224 | try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | pub const StackIterator = struct { | ||
| 229 | first_addr: ?usize, | ||
| 230 | fp: usize, | ||
| 231 | |||
| 232 | pub fn init(first_addr: ?usize) StackIterator { | ||
| 233 | return StackIterator{ | ||
| 234 | .first_addr = first_addr, | ||
| 235 | .fp = @frameAddress(), | ||
| 236 | }; | ||
| 237 | } | ||
| 238 | |||
| 239 | fn next(self: *StackIterator) ?usize { | ||
| 240 | if (self.fp == 0) return null; | ||
| 241 | self.fp = @intToPtr(*const usize, self.fp).*; | ||
| 242 | if (self.fp == 0) return null; | ||
| 243 | |||
| 244 | if (self.first_addr) |addr| { | ||
| 245 | while (self.fp != 0) : (self.fp = @intToPtr(*const usize, self.fp).*) { | ||
| 246 | const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; | ||
| 247 | if (addr == return_address) { | ||
| 248 | self.first_addr = null; | ||
| 249 | return return_address; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | const return_address = @intToPtr(*const usize, self.fp + @sizeOf(usize)).*; | ||
| 255 | return return_address; | ||
| 256 | } | ||
| 257 | }; | ||
| 258 | |||
| 259 | pub fn writeCurrentStackTrace(out_stream: var, debug_info: *DebugInfo, tty_color: bool, start_addr: ?usize) !void { | ||
| 260 | switch (builtin.os) { | ||
| 261 | builtin.Os.windows => return writeCurrentStackTraceWindows(out_stream, debug_info, tty_color, start_addr), | ||
| 262 | else => {}, | ||
| 263 | } | ||
| 264 | var it = StackIterator.init(start_addr); | ||
| 265 | while (it.next()) |return_address| { | ||
| 266 | try printSourceAtAddress(debug_info, out_stream, return_address, tty_color); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | pub fn writeCurrentStackTraceWindows( | ||
| 271 | out_stream: var, | ||
| 272 | debug_info: *DebugInfo, | ||
| 273 | tty_color: bool, | ||
| 274 | start_addr: ?usize, | ||
| 275 | ) !void { | ||
| 276 | var addr_buf: [1024]usize = undefined; | ||
| 277 | const n = windows.RtlCaptureStackBackTrace(0, addr_buf.len, @ptrCast(**c_void, &addr_buf), null); | ||
| 278 | const addrs = addr_buf[0..n]; | ||
| 279 | var start_i: usize = if (start_addr) |saddr| blk: { | ||
| 280 | for (addrs) |addr, i| { | ||
| 281 | if (addr == saddr) break :blk i; | ||
| 282 | } | ||
| 283 | return; | ||
| 284 | } else 0; | ||
| 285 | for (addrs[start_i..]) |addr| { | ||
| 286 | try printSourceAtAddress(debug_info, out_stream, addr, tty_color); | ||
| 287 | } | ||
| 288 | } | ||
| 289 | |||
| 290 | pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | ||
| 291 | switch (builtin.os) { | ||
| 292 | builtin.Os.macosx => return printSourceAtAddressMacOs(debug_info, out_stream, address, tty_color), | ||
| 293 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return printSourceAtAddressLinux(debug_info, out_stream, address, tty_color), | ||
| 294 | builtin.Os.windows => return printSourceAtAddressWindows(debug_info, out_stream, address, tty_color), | ||
| 295 | else => return error.UnsupportedOperatingSystem, | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | fn printSourceAtAddressWindows(di: *DebugInfo, out_stream: var, relocated_address: usize, tty_color: bool) !void { | ||
| 300 | const allocator = getDebugInfoAllocator(); | ||
| 301 | const base_address = os.getBaseAddress(); | ||
| 302 | const relative_address = relocated_address - base_address; | ||
| 303 | |||
| 304 | var coff_section: *coff.Section = undefined; | ||
| 305 | const mod_index = for (di.sect_contribs) |sect_contrib| { | ||
| 306 | if (sect_contrib.Section > di.coff.sections.len) continue; | ||
| 307 | // Remember that SectionContribEntry.Section is 1-based. | ||
| 308 | coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1]; | ||
| 309 | |||
| 310 | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; | ||
| 311 | const vaddr_end = vaddr_start + sect_contrib.Size; | ||
| 312 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 313 | break sect_contrib.ModuleIndex; | ||
| 314 | } | ||
| 315 | } else { | ||
| 316 | // we have no information to add to the address | ||
| 317 | if (tty_color) { | ||
| 318 | try out_stream.print("???:?:?: "); | ||
| 319 | setTtyColor(TtyColor.Dim); | ||
| 320 | try out_stream.print("0x{x} in ??? (???)", relocated_address); | ||
| 321 | setTtyColor(TtyColor.Reset); | ||
| 322 | try out_stream.print("\n\n\n"); | ||
| 323 | } else { | ||
| 324 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", relocated_address); | ||
| 325 | } | ||
| 326 | return; | ||
| 327 | }; | ||
| 328 | |||
| 329 | const mod = &di.modules[mod_index]; | ||
| 330 | try populateModule(di, mod); | ||
| 331 | const obj_basename = os.path.basename(mod.obj_file_name); | ||
| 332 | |||
| 333 | var symbol_i: usize = 0; | ||
| 334 | const symbol_name = while (symbol_i != mod.symbols.len) { | ||
| 335 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); | ||
| 336 | if (prefix.RecordLen < 2) | ||
| 337 | return error.InvalidDebugInfo; | ||
| 338 | switch (prefix.RecordKind) { | ||
| 339 | pdb.SymbolKind.S_LPROC32 => { | ||
| 340 | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); | ||
| 341 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; | ||
| 342 | const vaddr_end = vaddr_start + proc_sym.CodeSize; | ||
| 343 | if (relative_address >= vaddr_start and relative_address < vaddr_end) { | ||
| 344 | break mem.toSliceConst(u8, @ptrCast([*]u8, proc_sym) + @sizeOf(pdb.ProcSym)); | ||
| 345 | } | ||
| 346 | }, | ||
| 347 | else => {}, | ||
| 348 | } | ||
| 349 | symbol_i += prefix.RecordLen + @sizeOf(u16); | ||
| 350 | if (symbol_i > mod.symbols.len) | ||
| 351 | return error.InvalidDebugInfo; | ||
| 352 | } else "???"; | ||
| 353 | |||
| 354 | const subsect_info = mod.subsect_info; | ||
| 355 | |||
| 356 | var sect_offset: usize = 0; | ||
| 357 | var skip_len: usize = undefined; | ||
| 358 | const opt_line_info = subsections: { | ||
| 359 | const checksum_offset = mod.checksum_offset orelse break :subsections null; | ||
| 360 | while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { | ||
| 361 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); | ||
| 362 | skip_len = subsect_hdr.Length; | ||
| 363 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 364 | |||
| 365 | switch (subsect_hdr.Kind) { | ||
| 366 | pdb.DebugSubsectionKind.Lines => { | ||
| 367 | var line_index = sect_offset; | ||
| 368 | |||
| 369 | const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); | ||
| 370 | if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; | ||
| 371 | line_index += @sizeOf(pdb.LineFragmentHeader); | ||
| 372 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; | ||
| 373 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; | ||
| 374 | |||
| 375 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) | ||
| 376 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, | ||
| 377 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. | ||
| 378 | const subsection_end_index = sect_offset + subsect_hdr.Length; | ||
| 379 | while (line_index < subsection_end_index) { | ||
| 380 | const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); | ||
| 381 | line_index += @sizeOf(pdb.LineBlockFragmentHeader); | ||
| 382 | const start_line_index = line_index; | ||
| 383 | |||
| 384 | const has_column = line_hdr.Flags.LF_HaveColumns; | ||
| 385 | |||
| 386 | if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) { | ||
| 387 | // All line entries are stored inside their line block by ascending start address. | ||
| 388 | // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address. | ||
| 389 | // This is done with a simple linear search. | ||
| 390 | var line_i: u32 = 0; | ||
| 391 | while (line_i < block_hdr.NumLines) : (line_i += 1) { | ||
| 392 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); | ||
| 393 | line_index += @sizeOf(pdb.LineNumberEntry); | ||
| 394 | |||
| 395 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; | ||
| 396 | if (relative_address <= vaddr_start) { | ||
| 397 | break; | ||
| 398 | } | ||
| 399 | } | ||
| 400 | |||
| 401 | // line_i == 0 would mean that no matching LineNumberEntry was found. | ||
| 402 | if (line_i > 0) { | ||
| 403 | const subsect_index = checksum_offset + block_hdr.NameIndex; | ||
| 404 | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); | ||
| 405 | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; | ||
| 406 | try di.pdb.string_table.seekTo(strtab_offset); | ||
| 407 | const source_file_name = try di.pdb.string_table.readNullTermString(allocator); | ||
| 408 | |||
| 409 | const line_entry_idx = line_i - 1; | ||
| 410 | |||
| 411 | const column = if (has_column) blk: { | ||
| 412 | const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; | ||
| 413 | const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; | ||
| 414 | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); | ||
| 415 | break :blk col_num_entry.StartColumn; | ||
| 416 | } else 0; | ||
| 417 | |||
| 418 | const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); | ||
| 419 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); | ||
| 420 | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); | ||
| 421 | |||
| 422 | break :subsections LineInfo{ | ||
| 423 | .allocator = allocator, | ||
| 424 | .file_name = source_file_name, | ||
| 425 | .line = flags.Start, | ||
| 426 | .column = column, | ||
| 427 | }; | ||
| 428 | } | ||
| 429 | } | ||
| 430 | } | ||
| 431 | |||
| 432 | // Checking that we are not reading garbage after the (possibly) multiple block fragments. | ||
| 433 | if (line_index != subsection_end_index) { | ||
| 434 | return error.InvalidDebugInfo; | ||
| 435 | } | ||
| 436 | }, | ||
| 437 | else => {}, | ||
| 438 | } | ||
| 439 | |||
| 440 | if (sect_offset > subsect_info.len) | ||
| 441 | return error.InvalidDebugInfo; | ||
| 442 | } else { | ||
| 443 | break :subsections null; | ||
| 444 | } | ||
| 445 | }; | ||
| 446 | |||
| 447 | if (tty_color) { | ||
| 448 | setTtyColor(TtyColor.White); | ||
| 449 | if (opt_line_info) |li| { | ||
| 450 | try out_stream.print("{}:{}:{}", li.file_name, li.line, li.column); | ||
| 451 | } else { | ||
| 452 | try out_stream.print("???:?:?"); | ||
| 453 | } | ||
| 454 | setTtyColor(TtyColor.Reset); | ||
| 455 | try out_stream.print(": "); | ||
| 456 | setTtyColor(TtyColor.Dim); | ||
| 457 | try out_stream.print("0x{x} in {} ({})", relocated_address, symbol_name, obj_basename); | ||
| 458 | setTtyColor(TtyColor.Reset); | ||
| 459 | |||
| 460 | if (opt_line_info) |line_info| { | ||
| 461 | try out_stream.print("\n"); | ||
| 462 | if (printLineFromFileAnyOs(out_stream, line_info)) { | ||
| 463 | if (line_info.column == 0) { | ||
| 464 | try out_stream.write("\n"); | ||
| 465 | } else { | ||
| 466 | { | ||
| 467 | var col_i: usize = 1; | ||
| 468 | while (col_i < line_info.column) : (col_i += 1) { | ||
| 469 | try out_stream.writeByte(' '); | ||
| 470 | } | ||
| 471 | } | ||
| 472 | setTtyColor(TtyColor.Green); | ||
| 473 | try out_stream.write("^"); | ||
| 474 | setTtyColor(TtyColor.Reset); | ||
| 475 | try out_stream.write("\n"); | ||
| 476 | } | ||
| 477 | } else |err| switch (err) { | ||
| 478 | error.EndOfFile => {}, | ||
| 479 | else => return err, | ||
| 480 | } | ||
| 481 | } else { | ||
| 482 | try out_stream.print("\n\n\n"); | ||
| 483 | } | ||
| 484 | } else { | ||
| 485 | if (opt_line_info) |li| { | ||
| 486 | try out_stream.print("{}:{}:{}: 0x{x} in {} ({})\n\n\n", li.file_name, li.line, li.column, relocated_address, symbol_name, obj_basename); | ||
| 487 | } else { | ||
| 488 | try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", relocated_address, symbol_name, obj_basename); | ||
| 489 | } | ||
| 490 | } | ||
| 491 | } | ||
| 492 | |||
| 493 | const TtyColor = enum { | ||
| 494 | Red, | ||
| 495 | Green, | ||
| 496 | Cyan, | ||
| 497 | White, | ||
| 498 | Dim, | ||
| 499 | Bold, | ||
| 500 | Reset, | ||
| 501 | }; | ||
| 502 | |||
| 503 | /// TODO this is a special case hack right now. clean it up and maybe make it part of std.fmt | ||
| 504 | fn setTtyColor(tty_color: TtyColor) void { | ||
| 505 | if (os.supportsAnsiEscapeCodes(stderr_file.handle)) { | ||
| 506 | switch (tty_color) { | ||
| 507 | TtyColor.Red => { | ||
| 508 | stderr_file.write(RED) catch return; | ||
| 509 | }, | ||
| 510 | TtyColor.Green => { | ||
| 511 | stderr_file.write(GREEN) catch return; | ||
| 512 | }, | ||
| 513 | TtyColor.Cyan => { | ||
| 514 | stderr_file.write(CYAN) catch return; | ||
| 515 | }, | ||
| 516 | TtyColor.White, TtyColor.Bold => { | ||
| 517 | stderr_file.write(WHITE) catch return; | ||
| 518 | }, | ||
| 519 | TtyColor.Dim => { | ||
| 520 | stderr_file.write(DIM) catch return; | ||
| 521 | }, | ||
| 522 | TtyColor.Reset => { | ||
| 523 | stderr_file.write(RESET) catch return; | ||
| 524 | }, | ||
| 525 | } | ||
| 526 | } else { | ||
| 527 | const S = struct { | ||
| 528 | var attrs: windows.WORD = undefined; | ||
| 529 | var init_attrs = false; | ||
| 530 | }; | ||
| 531 | if (!S.init_attrs) { | ||
| 532 | S.init_attrs = true; | ||
| 533 | var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined; | ||
| 534 | // TODO handle error | ||
| 535 | _ = windows.GetConsoleScreenBufferInfo(stderr_file.handle, &info); | ||
| 536 | S.attrs = info.wAttributes; | ||
| 537 | } | ||
| 538 | |||
| 539 | // TODO handle errors | ||
| 540 | switch (tty_color) { | ||
| 541 | TtyColor.Red => { | ||
| 542 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY); | ||
| 543 | }, | ||
| 544 | TtyColor.Green => { | ||
| 545 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY); | ||
| 546 | }, | ||
| 547 | TtyColor.Cyan => { | ||
| 548 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); | ||
| 549 | }, | ||
| 550 | TtyColor.White, TtyColor.Bold => { | ||
| 551 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY); | ||
| 552 | }, | ||
| 553 | TtyColor.Dim => { | ||
| 554 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, windows.FOREGROUND_INTENSITY); | ||
| 555 | }, | ||
| 556 | TtyColor.Reset => { | ||
| 557 | _ = windows.SetConsoleTextAttribute(stderr_file.handle, S.attrs); | ||
| 558 | }, | ||
| 559 | } | ||
| 560 | } | ||
| 561 | } | ||
| 562 | |||
| 563 | fn populateModule(di: *DebugInfo, mod: *Module) !void { | ||
| 564 | if (mod.populated) | ||
| 565 | return; | ||
| 566 | const allocator = getDebugInfoAllocator(); | ||
| 567 | |||
| 568 | if (mod.mod_info.C11ByteSize != 0) | ||
| 569 | return error.InvalidDebugInfo; | ||
| 570 | |||
| 571 | if (mod.mod_info.C13ByteSize == 0) | ||
| 572 | return error.MissingDebugInfo; | ||
| 573 | |||
| 574 | const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo; | ||
| 575 | |||
| 576 | const signature = try modi.stream.readIntLittle(u32); | ||
| 577 | if (signature != 4) | ||
| 578 | return error.InvalidDebugInfo; | ||
| 579 | |||
| 580 | mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4); | ||
| 581 | try modi.stream.readNoEof(mod.symbols); | ||
| 582 | |||
| 583 | mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize); | ||
| 584 | try modi.stream.readNoEof(mod.subsect_info); | ||
| 585 | |||
| 586 | var sect_offset: usize = 0; | ||
| 587 | var skip_len: usize = undefined; | ||
| 588 | while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) { | ||
| 589 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]); | ||
| 590 | skip_len = subsect_hdr.Length; | ||
| 591 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | ||
| 592 | |||
| 593 | switch (subsect_hdr.Kind) { | ||
| 594 | pdb.DebugSubsectionKind.FileChecksums => { | ||
| 595 | mod.checksum_offset = sect_offset; | ||
| 596 | break; | ||
| 597 | }, | ||
| 598 | else => {}, | ||
| 599 | } | ||
| 600 | |||
| 601 | if (sect_offset > mod.subsect_info.len) | ||
| 602 | return error.InvalidDebugInfo; | ||
| 603 | } | ||
| 604 | |||
| 605 | mod.populated = true; | ||
| 606 | } | ||
| 607 | |||
| 608 | fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol { | ||
| 609 | var min: usize = 0; | ||
| 610 | var max: usize = symbols.len - 1; // Exclude sentinel. | ||
| 611 | while (min < max) { | ||
| 612 | const mid = min + (max - min) / 2; | ||
| 613 | const curr = &symbols[mid]; | ||
| 614 | const next = &symbols[mid + 1]; | ||
| 615 | if (address >= next.address()) { | ||
| 616 | min = mid + 1; | ||
| 617 | } else if (address < curr.address()) { | ||
| 618 | max = mid; | ||
| 619 | } else { | ||
| 620 | return curr; | ||
| 621 | } | ||
| 622 | } | ||
| 623 | return null; | ||
| 624 | } | ||
| 625 | |||
| 626 | fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | ||
| 627 | const base_addr = std.os.getBaseAddress(); | ||
| 628 | const adjusted_addr = 0x100000000 + (address - base_addr); | ||
| 629 | |||
| 630 | const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse { | ||
| 631 | if (tty_color) { | ||
| 632 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); | ||
| 633 | } else { | ||
| 634 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); | ||
| 635 | } | ||
| 636 | return; | ||
| 637 | }; | ||
| 638 | |||
| 639 | const symbol_name = mem.toSliceConst(u8, di.strings.ptr + symbol.nlist.n_strx); | ||
| 640 | const compile_unit_name = if (symbol.ofile) |ofile| blk: { | ||
| 641 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); | ||
| 642 | break :blk os.path.basename(ofile_path); | ||
| 643 | } else "???"; | ||
| 644 | if (getLineNumberInfoMacOs(di, symbol.*, adjusted_addr)) |line_info| { | ||
| 645 | defer line_info.deinit(); | ||
| 646 | try printLineInfo( | ||
| 647 | out_stream, | ||
| 648 | line_info, | ||
| 649 | address, | ||
| 650 | symbol_name, | ||
| 651 | compile_unit_name, | ||
| 652 | tty_color, | ||
| 653 | printLineFromFileAnyOs, | ||
| 654 | ); | ||
| 655 | } else |err| switch (err) { | ||
| 656 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 657 | if (tty_color) { | ||
| 658 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n\n\n", address, symbol_name, compile_unit_name); | ||
| 659 | } else { | ||
| 660 | try out_stream.print("???:?:?: 0x{x} in {} ({})\n\n\n", address, symbol_name, compile_unit_name); | ||
| 661 | } | ||
| 662 | }, | ||
| 663 | else => return err, | ||
| 664 | } | ||
| 665 | } | ||
| 666 | |||
| 667 | /// This function works in freestanding mode. | ||
| 668 | /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void | ||
| 669 | pub fn printSourceAtAddressDwarf( | ||
| 670 | debug_info: *DwarfInfo, | ||
| 671 | out_stream: var, | ||
| 672 | address: usize, | ||
| 673 | tty_color: bool, | ||
| 674 | comptime printLineFromFile: var, | ||
| 675 | ) !void { | ||
| 676 | const compile_unit = findCompileUnit(debug_info, address) catch { | ||
| 677 | if (tty_color) { | ||
| 678 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? (???)" ++ RESET ++ "\n\n\n", address); | ||
| 679 | } else { | ||
| 680 | try out_stream.print("???:?:?: 0x{x} in ??? (???)\n\n\n", address); | ||
| 681 | } | ||
| 682 | return; | ||
| 683 | }; | ||
| 684 | const compile_unit_name = try compile_unit.die.getAttrString(debug_info, DW.AT_name); | ||
| 685 | if (getLineNumberInfoDwarf(debug_info, compile_unit.*, address - 1)) |line_info| { | ||
| 686 | defer line_info.deinit(); | ||
| 687 | const symbol_name = "???"; | ||
| 688 | try printLineInfo( | ||
| 689 | out_stream, | ||
| 690 | line_info, | ||
| 691 | address, | ||
| 692 | symbol_name, | ||
| 693 | compile_unit_name, | ||
| 694 | tty_color, | ||
| 695 | printLineFromFile, | ||
| 696 | ); | ||
| 697 | } else |err| switch (err) { | ||
| 698 | error.MissingDebugInfo, error.InvalidDebugInfo => { | ||
| 699 | if (tty_color) { | ||
| 700 | try out_stream.print("???:?:?: " ++ DIM ++ "0x{x} in ??? ({})" ++ RESET ++ "\n\n\n", address, compile_unit_name); | ||
| 701 | } else { | ||
| 702 | try out_stream.print("???:?:?: 0x{x} in ??? ({})\n\n\n", address, compile_unit_name); | ||
| 703 | } | ||
| 704 | }, | ||
| 705 | else => return err, | ||
| 706 | } | ||
| 707 | } | ||
| 708 | |||
| 709 | pub fn printSourceAtAddressLinux(debug_info: *DebugInfo, out_stream: var, address: usize, tty_color: bool) !void { | ||
| 710 | return printSourceAtAddressDwarf(debug_info, out_stream, address, tty_color, printLineFromFileAnyOs); | ||
| 711 | } | ||
| 712 | |||
| 713 | fn printLineInfo( | ||
| 714 | out_stream: var, | ||
| 715 | line_info: LineInfo, | ||
| 716 | address: usize, | ||
| 717 | symbol_name: []const u8, | ||
| 718 | compile_unit_name: []const u8, | ||
| 719 | tty_color: bool, | ||
| 720 | comptime printLineFromFile: var, | ||
| 721 | ) !void { | ||
| 722 | if (tty_color) { | ||
| 723 | try out_stream.print( | ||
| 724 | WHITE ++ "{}:{}:{}" ++ RESET ++ ": " ++ DIM ++ "0x{x} in {} ({})" ++ RESET ++ "\n", | ||
| 725 | line_info.file_name, | ||
| 726 | line_info.line, | ||
| 727 | line_info.column, | ||
| 728 | address, | ||
| 729 | symbol_name, | ||
| 730 | compile_unit_name, | ||
| 731 | ); | ||
| 732 | if (printLineFromFile(out_stream, line_info)) { | ||
| 733 | if (line_info.column == 0) { | ||
| 734 | try out_stream.write("\n"); | ||
| 735 | } else { | ||
| 736 | { | ||
| 737 | var col_i: usize = 1; | ||
| 738 | while (col_i < line_info.column) : (col_i += 1) { | ||
| 739 | try out_stream.writeByte(' '); | ||
| 740 | } | ||
| 741 | } | ||
| 742 | try out_stream.write(GREEN ++ "^" ++ RESET ++ "\n"); | ||
| 743 | } | ||
| 744 | } else |err| switch (err) { | ||
| 745 | error.EndOfFile => {}, | ||
| 746 | else => return err, | ||
| 747 | } | ||
| 748 | } else { | ||
| 749 | try out_stream.print( | ||
| 750 | "{}:{}:{}: 0x{x} in {} ({})\n", | ||
| 751 | line_info.file_name, | ||
| 752 | line_info.line, | ||
| 753 | line_info.column, | ||
| 754 | address, | ||
| 755 | symbol_name, | ||
| 756 | compile_unit_name, | ||
| 757 | ); | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | // TODO use this | ||
| 762 | pub const OpenSelfDebugInfoError = error{ | ||
| 763 | MissingDebugInfo, | ||
| 764 | OutOfMemory, | ||
| 765 | UnsupportedOperatingSystem, | ||
| 766 | }; | ||
| 767 | |||
| 768 | pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo { | ||
| 769 | switch (builtin.os) { | ||
| 770 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => return openSelfDebugInfoLinux(allocator), | ||
| 771 | builtin.Os.macosx, builtin.Os.ios => return openSelfDebugInfoMacOs(allocator), | ||
| 772 | builtin.Os.windows => return openSelfDebugInfoWindows(allocator), | ||
| 773 | else => return error.UnsupportedOperatingSystem, | ||
| 774 | } | ||
| 775 | } | ||
| 776 | |||
| 777 | fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo { | ||
| 778 | const self_file = try os.openSelfExe(); | ||
| 779 | defer self_file.close(); | ||
| 780 | |||
| 781 | const coff_obj = try allocator.create(coff.Coff); | ||
| 782 | coff_obj.* = coff.Coff{ | ||
| 783 | .in_file = self_file, | ||
| 784 | .allocator = allocator, | ||
| 785 | .coff_header = undefined, | ||
| 786 | .pe_header = undefined, | ||
| 787 | .sections = undefined, | ||
| 788 | .guid = undefined, | ||
| 789 | .age = undefined, | ||
| 790 | }; | ||
| 791 | |||
| 792 | var di = DebugInfo{ | ||
| 793 | .coff = coff_obj, | ||
| 794 | .pdb = undefined, | ||
| 795 | .sect_contribs = undefined, | ||
| 796 | .modules = undefined, | ||
| 797 | }; | ||
| 798 | |||
| 799 | try di.coff.loadHeader(); | ||
| 800 | |||
| 801 | var path_buf: [windows.MAX_PATH]u8 = undefined; | ||
| 802 | const len = try di.coff.getPdbPath(path_buf[0..]); | ||
| 803 | const raw_path = path_buf[0..len]; | ||
| 804 | |||
| 805 | const path = try os.path.resolve(allocator, [][]const u8{raw_path}); | ||
| 806 | |||
| 807 | try di.pdb.openFile(di.coff, path); | ||
| 808 | |||
| 809 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | ||
| 810 | const version = try pdb_stream.stream.readIntLittle(u32); | ||
| 811 | const signature = try pdb_stream.stream.readIntLittle(u32); | ||
| 812 | const age = try pdb_stream.stream.readIntLittle(u32); | ||
| 813 | var guid: [16]u8 = undefined; | ||
| 814 | try pdb_stream.stream.readNoEof(guid[0..]); | ||
| 815 | if (!mem.eql(u8, di.coff.guid, guid) or di.coff.age != age) | ||
| 816 | return error.InvalidDebugInfo; | ||
| 817 | // We validated the executable and pdb match. | ||
| 818 | |||
| 819 | const string_table_index = str_tab_index: { | ||
| 820 | const name_bytes_len = try pdb_stream.stream.readIntLittle(u32); | ||
| 821 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | ||
| 822 | try pdb_stream.stream.readNoEof(name_bytes); | ||
| 823 | |||
| 824 | const HashTableHeader = packed struct { | ||
| 825 | Size: u32, | ||
| 826 | Capacity: u32, | ||
| 827 | |||
| 828 | fn maxLoad(cap: u32) u32 { | ||
| 829 | return cap * 2 / 3 + 1; | ||
| 830 | } | ||
| 831 | }; | ||
| 832 | const hash_tbl_hdr = try pdb_stream.stream.readStruct(HashTableHeader); | ||
| 833 | if (hash_tbl_hdr.Capacity == 0) | ||
| 834 | return error.InvalidDebugInfo; | ||
| 835 | |||
| 836 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | ||
| 837 | return error.InvalidDebugInfo; | ||
| 838 | |||
| 839 | const present = try readSparseBitVector(&pdb_stream.stream, allocator); | ||
| 840 | if (present.len != hash_tbl_hdr.Size) | ||
| 841 | return error.InvalidDebugInfo; | ||
| 842 | const deleted = try readSparseBitVector(&pdb_stream.stream, allocator); | ||
| 843 | |||
| 844 | const Bucket = struct { | ||
| 845 | first: u32, | ||
| 846 | second: u32, | ||
| 847 | }; | ||
| 848 | const bucket_list = try allocator.alloc(Bucket, present.len); | ||
| 849 | for (present) |_| { | ||
| 850 | const name_offset = try pdb_stream.stream.readIntLittle(u32); | ||
| 851 | const name_index = try pdb_stream.stream.readIntLittle(u32); | ||
| 852 | const name = mem.toSlice(u8, name_bytes.ptr + name_offset); | ||
| 853 | if (mem.eql(u8, name, "/names")) { | ||
| 854 | break :str_tab_index name_index; | ||
| 855 | } | ||
| 856 | } | ||
| 857 | return error.MissingDebugInfo; | ||
| 858 | }; | ||
| 859 | |||
| 860 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.InvalidDebugInfo; | ||
| 861 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | ||
| 862 | |||
| 863 | const dbi = di.pdb.dbi; | ||
| 864 | |||
| 865 | // Dbi Header | ||
| 866 | const dbi_stream_header = try dbi.stream.readStruct(pdb.DbiStreamHeader); | ||
| 867 | const mod_info_size = dbi_stream_header.ModInfoSize; | ||
| 868 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | ||
| 869 | |||
| 870 | var modules = ArrayList(Module).init(allocator); | ||
| 871 | |||
| 872 | // Module Info Substream | ||
| 873 | var mod_info_offset: usize = 0; | ||
| 874 | while (mod_info_offset != mod_info_size) { | ||
| 875 | const mod_info = try dbi.stream.readStruct(pdb.ModInfo); | ||
| 876 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | ||
| 877 | |||
| 878 | const module_name = try dbi.readNullTermString(allocator); | ||
| 879 | this_record_len += module_name.len + 1; | ||
| 880 | |||
| 881 | const obj_file_name = try dbi.readNullTermString(allocator); | ||
| 882 | this_record_len += obj_file_name.len + 1; | ||
| 883 | |||
| 884 | const march_forward_bytes = this_record_len % 4; | ||
| 885 | if (march_forward_bytes != 0) { | ||
| 886 | try dbi.seekForward(march_forward_bytes); | ||
| 887 | this_record_len += march_forward_bytes; | ||
| 888 | } | ||
| 889 | |||
| 890 | try modules.append(Module{ | ||
| 891 | .mod_info = mod_info, | ||
| 892 | .module_name = module_name, | ||
| 893 | .obj_file_name = obj_file_name, | ||
| 894 | |||
| 895 | .populated = false, | ||
| 896 | .symbols = undefined, | ||
| 897 | .subsect_info = undefined, | ||
| 898 | .checksum_offset = null, | ||
| 899 | }); | ||
| 900 | |||
| 901 | mod_info_offset += this_record_len; | ||
| 902 | if (mod_info_offset > mod_info_size) | ||
| 903 | return error.InvalidDebugInfo; | ||
| 904 | } | ||
| 905 | |||
| 906 | di.modules = modules.toOwnedSlice(); | ||
| 907 | |||
| 908 | // Section Contribution Substream | ||
| 909 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | ||
| 910 | var sect_cont_offset: usize = 0; | ||
| 911 | if (section_contrib_size != 0) { | ||
| 912 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.stream.readIntLittle(u32)); | ||
| 913 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | ||
| 914 | return error.InvalidDebugInfo; | ||
| 915 | sect_cont_offset += @sizeOf(u32); | ||
| 916 | } | ||
| 917 | while (sect_cont_offset != section_contrib_size) { | ||
| 918 | const entry = try sect_contribs.addOne(); | ||
| 919 | entry.* = try dbi.stream.readStruct(pdb.SectionContribEntry); | ||
| 920 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | ||
| 921 | |||
| 922 | if (sect_cont_offset > section_contrib_size) | ||
| 923 | return error.InvalidDebugInfo; | ||
| 924 | } | ||
| 925 | |||
| 926 | di.sect_contribs = sect_contribs.toOwnedSlice(); | ||
| 927 | |||
| 928 | return di; | ||
| 929 | } | ||
| 930 | |||
| 931 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { | ||
| 932 | const num_words = try stream.readIntLittle(u32); | ||
| 933 | var word_i: usize = 0; | ||
| 934 | var list = ArrayList(usize).init(allocator); | ||
| 935 | while (word_i != num_words) : (word_i += 1) { | ||
| 936 | const word = try stream.readIntLittle(u32); | ||
| 937 | var bit_i: u5 = 0; | ||
| 938 | while (true) : (bit_i += 1) { | ||
| 939 | if (word & (u32(1) << bit_i) != 0) { | ||
| 940 | try list.append(word_i * 32 + bit_i); | ||
| 941 | } | ||
| 942 | if (bit_i == maxInt(u5)) break; | ||
| 943 | } | ||
| 944 | } | ||
| 945 | return list.toOwnedSlice(); | ||
| 946 | } | ||
| 947 | |||
| 948 | fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section { | ||
| 949 | const elf_header = (try elf_file.findSection(name)) orelse return null; | ||
| 950 | return DwarfInfo.Section{ | ||
| 951 | .offset = elf_header.offset, | ||
| 952 | .size = elf_header.size, | ||
| 953 | }; | ||
| 954 | } | ||
| 955 | |||
| 956 | /// Initialize DWARF info. The caller has the responsibility to initialize most | ||
| 957 | /// the DwarfInfo fields before calling. These fields can be left undefined: | ||
| 958 | /// * abbrev_table_list | ||
| 959 | /// * compile_unit_list | ||
| 960 | pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void { | ||
| 961 | di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator); | ||
| 962 | di.compile_unit_list = ArrayList(CompileUnit).init(allocator); | ||
| 963 | try scanAllCompileUnits(di); | ||
| 964 | } | ||
| 965 | |||
| 966 | pub fn openElfDebugInfo( | ||
| 967 | allocator: *mem.Allocator, | ||
| 968 | elf_seekable_stream: *DwarfSeekableStream, | ||
| 969 | elf_in_stream: *DwarfInStream, | ||
| 970 | ) !DwarfInfo { | ||
| 971 | var efile: elf.Elf = undefined; | ||
| 972 | try efile.openStream(allocator, elf_seekable_stream, elf_in_stream); | ||
| 973 | errdefer efile.close(); | ||
| 974 | |||
| 975 | var di = DwarfInfo{ | ||
| 976 | .dwarf_seekable_stream = elf_seekable_stream, | ||
| 977 | .dwarf_in_stream = elf_in_stream, | ||
| 978 | .endian = efile.endian, | ||
| 979 | .debug_info = (try findDwarfSectionFromElf(&efile, ".debug_info")) orelse return error.MissingDebugInfo, | ||
| 980 | .debug_abbrev = (try findDwarfSectionFromElf(&efile, ".debug_abbrev")) orelse return error.MissingDebugInfo, | ||
| 981 | .debug_str = (try findDwarfSectionFromElf(&efile, ".debug_str")) orelse return error.MissingDebugInfo, | ||
| 982 | .debug_line = (try findDwarfSectionFromElf(&efile, ".debug_line")) orelse return error.MissingDebugInfo, | ||
| 983 | .debug_ranges = (try findDwarfSectionFromElf(&efile, ".debug_ranges")), | ||
| 984 | .abbrev_table_list = undefined, | ||
| 985 | .compile_unit_list = undefined, | ||
| 986 | }; | ||
| 987 | try openDwarfDebugInfo(&di, allocator); | ||
| 988 | return di; | ||
| 989 | } | ||
| 990 | |||
| 991 | fn openSelfDebugInfoLinux(allocator: *mem.Allocator) !DwarfInfo { | ||
| 992 | const S = struct { | ||
| 993 | var self_exe_file: os.File = undefined; | ||
| 994 | var self_exe_seekable_stream: os.File.SeekableStream = undefined; | ||
| 995 | var self_exe_in_stream: os.File.InStream = undefined; | ||
| 996 | }; | ||
| 997 | S.self_exe_file = try os.openSelfExe(); | ||
| 998 | errdefer S.self_exe_file.close(); | ||
| 999 | |||
| 1000 | S.self_exe_seekable_stream = S.self_exe_file.seekableStream(); | ||
| 1001 | S.self_exe_in_stream = S.self_exe_file.inStream(); | ||
| 1002 | |||
| 1003 | return openElfDebugInfo( | ||
| 1004 | allocator, | ||
| 1005 | // TODO https://github.com/ziglang/zig/issues/764 | ||
| 1006 | @ptrCast(*DwarfSeekableStream, &S.self_exe_seekable_stream.stream), | ||
| 1007 | // TODO https://github.com/ziglang/zig/issues/764 | ||
| 1008 | @ptrCast(*DwarfInStream, &S.self_exe_in_stream.stream), | ||
| 1009 | ); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo { | ||
| 1013 | const hdr = &std.c._mh_execute_header; | ||
| 1014 | assert(hdr.magic == std.macho.MH_MAGIC_64); | ||
| 1015 | |||
| 1016 | const hdr_base = @ptrCast([*]u8, hdr); | ||
| 1017 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); | ||
| 1018 | var ncmd: u32 = hdr.ncmds; | ||
| 1019 | const symtab = while (ncmd != 0) : (ncmd -= 1) { | ||
| 1020 | const lc = @ptrCast(*std.macho.load_command, ptr); | ||
| 1021 | switch (lc.cmd) { | ||
| 1022 | std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr), | ||
| 1023 | else => {}, | ||
| 1024 | } | ||
| 1025 | ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 | ||
| 1026 | } else { | ||
| 1027 | return error.MissingDebugInfo; | ||
| 1028 | }; | ||
| 1029 | const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms]; | ||
| 1030 | const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize]; | ||
| 1031 | |||
| 1032 | const symbols_buf = try allocator.alloc(MachoSymbol, syms.len); | ||
| 1033 | |||
| 1034 | var ofile: ?*macho.nlist_64 = null; | ||
| 1035 | var reloc: u64 = 0; | ||
| 1036 | var symbol_index: usize = 0; | ||
| 1037 | var last_len: u64 = 0; | ||
| 1038 | for (syms) |*sym| { | ||
| 1039 | if (sym.n_type & std.macho.N_STAB != 0) { | ||
| 1040 | switch (sym.n_type) { | ||
| 1041 | std.macho.N_OSO => { | ||
| 1042 | ofile = sym; | ||
| 1043 | reloc = 0; | ||
| 1044 | }, | ||
| 1045 | std.macho.N_FUN => { | ||
| 1046 | if (sym.n_sect == 0) { | ||
| 1047 | last_len = sym.n_value; | ||
| 1048 | } else { | ||
| 1049 | symbols_buf[symbol_index] = MachoSymbol{ | ||
| 1050 | .nlist = sym, | ||
| 1051 | .ofile = ofile, | ||
| 1052 | .reloc = reloc, | ||
| 1053 | }; | ||
| 1054 | symbol_index += 1; | ||
| 1055 | } | ||
| 1056 | }, | ||
| 1057 | std.macho.N_BNSYM => { | ||
| 1058 | if (reloc == 0) { | ||
| 1059 | reloc = sym.n_value; | ||
| 1060 | } | ||
| 1061 | }, | ||
| 1062 | else => continue, | ||
| 1063 | } | ||
| 1064 | } | ||
| 1065 | } | ||
| 1066 | const sentinel = try allocator.create(macho.nlist_64); | ||
| 1067 | sentinel.* = macho.nlist_64{ | ||
| 1068 | .n_strx = 0, | ||
| 1069 | .n_type = 36, | ||
| 1070 | .n_sect = 0, | ||
| 1071 | .n_desc = 0, | ||
| 1072 | .n_value = symbols_buf[symbol_index - 1].nlist.n_value + last_len, | ||
| 1073 | }; | ||
| 1074 | |||
| 1075 | const symbols = allocator.shrink(MachoSymbol, symbols_buf, symbol_index); | ||
| 1076 | |||
| 1077 | // Even though lld emits symbols in ascending order, this debug code | ||
| 1078 | // should work for programs linked in any valid way. | ||
| 1079 | // This sort is so that we can binary search later. | ||
| 1080 | std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan); | ||
| 1081 | |||
| 1082 | return DebugInfo{ | ||
| 1083 | .ofiles = DebugInfo.OFileTable.init(allocator), | ||
| 1084 | .symbols = symbols, | ||
| 1085 | .strings = strings, | ||
| 1086 | }; | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void { | ||
| 1090 | var f = try os.File.openRead(line_info.file_name); | ||
| 1091 | defer f.close(); | ||
| 1092 | // TODO fstat and make sure that the file has the correct size | ||
| 1093 | |||
| 1094 | var buf: [os.page_size]u8 = undefined; | ||
| 1095 | var line: usize = 1; | ||
| 1096 | var column: usize = 1; | ||
| 1097 | var abs_index: usize = 0; | ||
| 1098 | while (true) { | ||
| 1099 | const amt_read = try f.read(buf[0..]); | ||
| 1100 | const slice = buf[0..amt_read]; | ||
| 1101 | |||
| 1102 | for (slice) |byte| { | ||
| 1103 | if (line == line_info.line) { | ||
| 1104 | try out_stream.writeByte(byte); | ||
| 1105 | if (byte == '\n') { | ||
| 1106 | return; | ||
| 1107 | } | ||
| 1108 | } | ||
| 1109 | if (byte == '\n') { | ||
| 1110 | line += 1; | ||
| 1111 | column = 1; | ||
| 1112 | } else { | ||
| 1113 | column += 1; | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | if (amt_read < buf.len) return error.EndOfFile; | ||
| 1118 | } | ||
| 1119 | } | ||
| 1120 | |||
| 1121 | const MachoSymbol = struct { | ||
| 1122 | nlist: *macho.nlist_64, | ||
| 1123 | ofile: ?*macho.nlist_64, | ||
| 1124 | reloc: u64, | ||
| 1125 | |||
| 1126 | /// Returns the address from the macho file | ||
| 1127 | fn address(self: MachoSymbol) u64 { | ||
| 1128 | return self.nlist.n_value; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | fn addressLessThan(lhs: MachoSymbol, rhs: MachoSymbol) bool { | ||
| 1132 | return lhs.address() < rhs.address(); | ||
| 1133 | } | ||
| 1134 | }; | ||
| 1135 | |||
| 1136 | const MachOFile = struct { | ||
| 1137 | bytes: []align(@alignOf(macho.mach_header_64)) const u8, | ||
| 1138 | sect_debug_info: ?*const macho.section_64, | ||
| 1139 | sect_debug_line: ?*const macho.section_64, | ||
| 1140 | }; | ||
| 1141 | |||
| 1142 | pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror); | ||
| 1143 | pub const DwarfInStream = io.InStream(anyerror); | ||
| 1144 | |||
| 1145 | pub const DwarfInfo = struct { | ||
| 1146 | dwarf_seekable_stream: *DwarfSeekableStream, | ||
| 1147 | dwarf_in_stream: *DwarfInStream, | ||
| 1148 | endian: builtin.Endian, | ||
| 1149 | debug_info: Section, | ||
| 1150 | debug_abbrev: Section, | ||
| 1151 | debug_str: Section, | ||
| 1152 | debug_line: Section, | ||
| 1153 | debug_ranges: ?Section, | ||
| 1154 | abbrev_table_list: ArrayList(AbbrevTableHeader), | ||
| 1155 | compile_unit_list: ArrayList(CompileUnit), | ||
| 1156 | |||
| 1157 | pub const Section = struct { | ||
| 1158 | offset: usize, | ||
| 1159 | size: usize, | ||
| 1160 | }; | ||
| 1161 | |||
| 1162 | pub fn allocator(self: DwarfInfo) *mem.Allocator { | ||
| 1163 | return self.abbrev_table_list.allocator; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | pub fn readString(self: *DwarfInfo) ![]u8 { | ||
| 1167 | return readStringRaw(self.allocator(), self.dwarf_in_stream); | ||
| 1168 | } | ||
| 1169 | }; | ||
| 1170 | |||
| 1171 | pub const DebugInfo = switch (builtin.os) { | ||
| 1172 | builtin.Os.macosx => struct { | ||
| 1173 | symbols: []const MachoSymbol, | ||
| 1174 | strings: []const u8, | ||
| 1175 | ofiles: OFileTable, | ||
| 1176 | |||
| 1177 | const OFileTable = std.HashMap( | ||
| 1178 | *macho.nlist_64, | ||
| 1179 | MachOFile, | ||
| 1180 | std.hash_map.getHashPtrAddrFn(*macho.nlist_64), | ||
| 1181 | std.hash_map.getTrivialEqlFn(*macho.nlist_64), | ||
| 1182 | ); | ||
| 1183 | |||
| 1184 | pub fn allocator(self: DebugInfo) *mem.Allocator { | ||
| 1185 | return self.ofiles.allocator; | ||
| 1186 | } | ||
| 1187 | }, | ||
| 1188 | builtin.Os.uefi, builtin.Os.windows => struct { | ||
| 1189 | pdb: pdb.Pdb, | ||
| 1190 | coff: *coff.Coff, | ||
| 1191 | sect_contribs: []pdb.SectionContribEntry, | ||
| 1192 | modules: []Module, | ||
| 1193 | }, | ||
| 1194 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => DwarfInfo, | ||
| 1195 | else => @compileError("Unsupported OS"), | ||
| 1196 | }; | ||
| 1197 | |||
| 1198 | const PcRange = struct { | ||
| 1199 | start: u64, | ||
| 1200 | end: u64, | ||
| 1201 | }; | ||
| 1202 | |||
| 1203 | const CompileUnit = struct { | ||
| 1204 | version: u16, | ||
| 1205 | is_64: bool, | ||
| 1206 | die: *Die, | ||
| 1207 | index: usize, | ||
| 1208 | pc_range: ?PcRange, | ||
| 1209 | }; | ||
| 1210 | |||
| 1211 | const AbbrevTable = ArrayList(AbbrevTableEntry); | ||
| 1212 | |||
| 1213 | const AbbrevTableHeader = struct { | ||
| 1214 | // offset from .debug_abbrev | ||
| 1215 | offset: u64, | ||
| 1216 | table: AbbrevTable, | ||
| 1217 | }; | ||
| 1218 | |||
| 1219 | const AbbrevTableEntry = struct { | ||
| 1220 | has_children: bool, | ||
| 1221 | abbrev_code: u64, | ||
| 1222 | tag_id: u64, | ||
| 1223 | attrs: ArrayList(AbbrevAttr), | ||
| 1224 | }; | ||
| 1225 | |||
| 1226 | const AbbrevAttr = struct { | ||
| 1227 | attr_id: u64, | ||
| 1228 | form_id: u64, | ||
| 1229 | }; | ||
| 1230 | |||
| 1231 | const FormValue = union(enum) { | ||
| 1232 | Address: u64, | ||
| 1233 | Block: []u8, | ||
| 1234 | Const: Constant, | ||
| 1235 | ExprLoc: []u8, | ||
| 1236 | Flag: bool, | ||
| 1237 | SecOffset: u64, | ||
| 1238 | Ref: []u8, | ||
| 1239 | RefAddr: u64, | ||
| 1240 | RefSig8: u64, | ||
| 1241 | String: []u8, | ||
| 1242 | StrPtr: u64, | ||
| 1243 | }; | ||
| 1244 | |||
| 1245 | const Constant = struct { | ||
| 1246 | payload: []u8, | ||
| 1247 | signed: bool, | ||
| 1248 | |||
| 1249 | fn asUnsignedLe(self: *const Constant) !u64 { | ||
| 1250 | if (self.payload.len > @sizeOf(u64)) return error.InvalidDebugInfo; | ||
| 1251 | if (self.signed) return error.InvalidDebugInfo; | ||
| 1252 | return mem.readVarInt(u64, self.payload, builtin.Endian.Little); | ||
| 1253 | } | ||
| 1254 | }; | ||
| 1255 | |||
| 1256 | const Die = struct { | ||
| 1257 | tag_id: u64, | ||
| 1258 | has_children: bool, | ||
| 1259 | attrs: ArrayList(Attr), | ||
| 1260 | |||
| 1261 | const Attr = struct { | ||
| 1262 | id: u64, | ||
| 1263 | value: FormValue, | ||
| 1264 | }; | ||
| 1265 | |||
| 1266 | fn getAttr(self: *const Die, id: u64) ?*const FormValue { | ||
| 1267 | for (self.attrs.toSliceConst()) |*attr| { | ||
| 1268 | if (attr.id == id) return &attr.value; | ||
| 1269 | } | ||
| 1270 | return null; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | fn getAttrAddr(self: *const Die, id: u64) !u64 { | ||
| 1274 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1275 | return switch (form_value.*) { | ||
| 1276 | FormValue.Address => |value| value, | ||
| 1277 | else => error.InvalidDebugInfo, | ||
| 1278 | }; | ||
| 1279 | } | ||
| 1280 | |||
| 1281 | fn getAttrSecOffset(self: *const Die, id: u64) !u64 { | ||
| 1282 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1283 | return switch (form_value.*) { | ||
| 1284 | FormValue.Const => |value| value.asUnsignedLe(), | ||
| 1285 | FormValue.SecOffset => |value| value, | ||
| 1286 | else => error.InvalidDebugInfo, | ||
| 1287 | }; | ||
| 1288 | } | ||
| 1289 | |||
| 1290 | fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { | ||
| 1291 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1292 | return switch (form_value.*) { | ||
| 1293 | FormValue.Const => |value| value.asUnsignedLe(), | ||
| 1294 | else => error.InvalidDebugInfo, | ||
| 1295 | }; | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 { | ||
| 1299 | const form_value = self.getAttr(id) orelse return error.MissingDebugInfo; | ||
| 1300 | return switch (form_value.*) { | ||
| 1301 | FormValue.String => |value| value, | ||
| 1302 | FormValue.StrPtr => |offset| getString(di, offset), | ||
| 1303 | else => error.InvalidDebugInfo, | ||
| 1304 | }; | ||
| 1305 | } | ||
| 1306 | }; | ||
| 1307 | |||
| 1308 | const FileEntry = struct { | ||
| 1309 | file_name: []const u8, | ||
| 1310 | dir_index: usize, | ||
| 1311 | mtime: usize, | ||
| 1312 | len_bytes: usize, | ||
| 1313 | }; | ||
| 1314 | |||
| 1315 | pub const LineInfo = struct { | ||
| 1316 | line: usize, | ||
| 1317 | column: usize, | ||
| 1318 | file_name: []const u8, | ||
| 1319 | allocator: ?*mem.Allocator, | ||
| 1320 | |||
| 1321 | fn deinit(self: LineInfo) void { | ||
| 1322 | const allocator = self.allocator orelse return; | ||
| 1323 | allocator.free(self.file_name); | ||
| 1324 | } | ||
| 1325 | }; | ||
| 1326 | |||
| 1327 | const LineNumberProgram = struct { | ||
| 1328 | address: usize, | ||
| 1329 | file: usize, | ||
| 1330 | line: isize, | ||
| 1331 | column: usize, | ||
| 1332 | is_stmt: bool, | ||
| 1333 | basic_block: bool, | ||
| 1334 | end_sequence: bool, | ||
| 1335 | |||
| 1336 | target_address: usize, | ||
| 1337 | include_dirs: []const []const u8, | ||
| 1338 | file_entries: *ArrayList(FileEntry), | ||
| 1339 | |||
| 1340 | prev_address: usize, | ||
| 1341 | prev_file: usize, | ||
| 1342 | prev_line: isize, | ||
| 1343 | prev_column: usize, | ||
| 1344 | prev_is_stmt: bool, | ||
| 1345 | prev_basic_block: bool, | ||
| 1346 | prev_end_sequence: bool, | ||
| 1347 | |||
| 1348 | pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram { | ||
| 1349 | return LineNumberProgram{ | ||
| 1350 | .address = 0, | ||
| 1351 | .file = 1, | ||
| 1352 | .line = 1, | ||
| 1353 | .column = 0, | ||
| 1354 | .is_stmt = is_stmt, | ||
| 1355 | .basic_block = false, | ||
| 1356 | .end_sequence = false, | ||
| 1357 | .include_dirs = include_dirs, | ||
| 1358 | .file_entries = file_entries, | ||
| 1359 | .target_address = target_address, | ||
| 1360 | .prev_address = 0, | ||
| 1361 | .prev_file = undefined, | ||
| 1362 | .prev_line = undefined, | ||
| 1363 | .prev_column = undefined, | ||
| 1364 | .prev_is_stmt = undefined, | ||
| 1365 | .prev_basic_block = undefined, | ||
| 1366 | .prev_end_sequence = undefined, | ||
| 1367 | }; | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo { | ||
| 1371 | if (self.target_address >= self.prev_address and self.target_address < self.address) { | ||
| 1372 | const file_entry = if (self.prev_file == 0) { | ||
| 1373 | return error.MissingDebugInfo; | ||
| 1374 | } else if (self.prev_file - 1 >= self.file_entries.len) { | ||
| 1375 | return error.InvalidDebugInfo; | ||
| 1376 | } else | ||
| 1377 | &self.file_entries.items[self.prev_file - 1]; | ||
| 1378 | |||
| 1379 | const dir_name = if (file_entry.dir_index >= self.include_dirs.len) { | ||
| 1380 | return error.InvalidDebugInfo; | ||
| 1381 | } else | ||
| 1382 | self.include_dirs[file_entry.dir_index]; | ||
| 1383 | const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name }); | ||
| 1384 | errdefer self.file_entries.allocator.free(file_name); | ||
| 1385 | return LineInfo{ | ||
| 1386 | .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0, | ||
| 1387 | .column = self.prev_column, | ||
| 1388 | .file_name = file_name, | ||
| 1389 | .allocator = self.file_entries.allocator, | ||
| 1390 | }; | ||
| 1391 | } | ||
| 1392 | |||
| 1393 | self.prev_address = self.address; | ||
| 1394 | self.prev_file = self.file; | ||
| 1395 | self.prev_line = self.line; | ||
| 1396 | self.prev_column = self.column; | ||
| 1397 | self.prev_is_stmt = self.is_stmt; | ||
| 1398 | self.prev_basic_block = self.basic_block; | ||
| 1399 | self.prev_end_sequence = self.end_sequence; | ||
| 1400 | return null; | ||
| 1401 | } | ||
| 1402 | }; | ||
| 1403 | |||
| 1404 | fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 { | ||
| 1405 | var buf = ArrayList(u8).init(allocator); | ||
| 1406 | while (true) { | ||
| 1407 | const byte = try in_stream.readByte(); | ||
| 1408 | if (byte == 0) break; | ||
| 1409 | try buf.append(byte); | ||
| 1410 | } | ||
| 1411 | return buf.toSlice(); | ||
| 1412 | } | ||
| 1413 | |||
| 1414 | fn getString(di: *DwarfInfo, offset: u64) ![]u8 { | ||
| 1415 | const pos = di.debug_str.offset + offset; | ||
| 1416 | try di.dwarf_seekable_stream.seekTo(pos); | ||
| 1417 | return di.readString(); | ||
| 1418 | } | ||
| 1419 | |||
| 1420 | fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 { | ||
| 1421 | const buf = try allocator.alloc(u8, size); | ||
| 1422 | errdefer allocator.free(buf); | ||
| 1423 | if ((try in_stream.read(buf)) < size) return error.EndOfFile; | ||
| 1424 | return buf; | ||
| 1425 | } | ||
| 1426 | |||
| 1427 | fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | ||
| 1428 | const buf = try readAllocBytes(allocator, in_stream, size); | ||
| 1429 | return FormValue{ .Block = buf }; | ||
| 1430 | } | ||
| 1431 | |||
| 1432 | fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | ||
| 1433 | const block_len = try in_stream.readVarInt(usize, builtin.Endian.Little, size); | ||
| 1434 | return parseFormValueBlockLen(allocator, in_stream, block_len); | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, size: usize) !FormValue { | ||
| 1438 | return FormValue{ | ||
| 1439 | .Const = Constant{ | ||
| 1440 | .signed = signed, | ||
| 1441 | .payload = try readAllocBytes(allocator, in_stream, size), | ||
| 1442 | }, | ||
| 1443 | }; | ||
| 1444 | } | ||
| 1445 | |||
| 1446 | fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 { | ||
| 1447 | return if (is_64) try in_stream.readIntLittle(u64) else u64(try in_stream.readIntLittle(u32)); | ||
| 1448 | } | ||
| 1449 | |||
| 1450 | fn parseFormValueTargetAddrSize(in_stream: var) !u64 { | ||
| 1451 | return if (@sizeOf(usize) == 4) u64(try in_stream.readIntLittle(u32)) else if (@sizeOf(usize) == 8) try in_stream.readIntLittle(u64) else unreachable; | ||
| 1452 | } | ||
| 1453 | |||
| 1454 | fn parseFormValueRefLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue { | ||
| 1455 | const buf = try readAllocBytes(allocator, in_stream, size); | ||
| 1456 | return FormValue{ .Ref = buf }; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, comptime T: type) !FormValue { | ||
| 1460 | const block_len = try in_stream.readIntLittle(T); | ||
| 1461 | return parseFormValueRefLen(allocator, in_stream, block_len); | ||
| 1462 | } | ||
| 1463 | |||
| 1464 | fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue { | ||
| 1465 | return switch (form_id) { | ||
| 1466 | DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) }, | ||
| 1467 | DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1), | ||
| 1468 | DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2), | ||
| 1469 | DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4), | ||
| 1470 | DW.FORM_block => x: { | ||
| 1471 | const block_len = try readULeb128(in_stream); | ||
| 1472 | return parseFormValueBlockLen(allocator, in_stream, block_len); | ||
| 1473 | }, | ||
| 1474 | DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1), | ||
| 1475 | DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2), | ||
| 1476 | DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4), | ||
| 1477 | DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8), | ||
| 1478 | DW.FORM_udata, DW.FORM_sdata => { | ||
| 1479 | const block_len = try readULeb128(in_stream); | ||
| 1480 | const signed = form_id == DW.FORM_sdata; | ||
| 1481 | return parseFormValueConstant(allocator, in_stream, signed, block_len); | ||
| 1482 | }, | ||
| 1483 | DW.FORM_exprloc => { | ||
| 1484 | const size = try readULeb128(in_stream); | ||
| 1485 | const buf = try readAllocBytes(allocator, in_stream, size); | ||
| 1486 | return FormValue{ .ExprLoc = buf }; | ||
| 1487 | }, | ||
| 1488 | DW.FORM_flag => FormValue{ .Flag = (try in_stream.readByte()) != 0 }, | ||
| 1489 | DW.FORM_flag_present => FormValue{ .Flag = true }, | ||
| 1490 | DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, | ||
| 1491 | |||
| 1492 | DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, u8), | ||
| 1493 | DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, u16), | ||
| 1494 | DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, u32), | ||
| 1495 | DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64), | ||
| 1496 | DW.FORM_ref_udata => { | ||
| 1497 | const ref_len = try readULeb128(in_stream); | ||
| 1498 | return parseFormValueRefLen(allocator, in_stream, ref_len); | ||
| 1499 | }, | ||
| 1500 | |||
| 1501 | DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, | ||
| 1502 | DW.FORM_ref_sig8 => FormValue{ .RefSig8 = try in_stream.readIntLittle(u64) }, | ||
| 1503 | |||
| 1504 | DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) }, | ||
| 1505 | DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) }, | ||
| 1506 | DW.FORM_indirect => { | ||
| 1507 | const child_form_id = try readULeb128(in_stream); | ||
| 1508 | return parseFormValue(allocator, in_stream, child_form_id, is_64); | ||
| 1509 | }, | ||
| 1510 | else => error.InvalidDebugInfo, | ||
| 1511 | }; | ||
| 1512 | } | ||
| 1513 | |||
| 1514 | fn parseAbbrevTable(di: *DwarfInfo) !AbbrevTable { | ||
| 1515 | var result = AbbrevTable.init(di.allocator()); | ||
| 1516 | while (true) { | ||
| 1517 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | ||
| 1518 | if (abbrev_code == 0) return result; | ||
| 1519 | try result.append(AbbrevTableEntry{ | ||
| 1520 | .abbrev_code = abbrev_code, | ||
| 1521 | .tag_id = try readULeb128(di.dwarf_in_stream), | ||
| 1522 | .has_children = (try di.dwarf_in_stream.readByte()) == DW.CHILDREN_yes, | ||
| 1523 | .attrs = ArrayList(AbbrevAttr).init(di.allocator()), | ||
| 1524 | }); | ||
| 1525 | const attrs = &result.items[result.len - 1].attrs; | ||
| 1526 | |||
| 1527 | while (true) { | ||
| 1528 | const attr_id = try readULeb128(di.dwarf_in_stream); | ||
| 1529 | const form_id = try readULeb128(di.dwarf_in_stream); | ||
| 1530 | if (attr_id == 0 and form_id == 0) break; | ||
| 1531 | try attrs.append(AbbrevAttr{ | ||
| 1532 | .attr_id = attr_id, | ||
| 1533 | .form_id = form_id, | ||
| 1534 | }); | ||
| 1535 | } | ||
| 1536 | } | ||
| 1537 | } | ||
| 1538 | |||
| 1539 | /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found, | ||
| 1540 | /// seeks in the stream and parses it. | ||
| 1541 | fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable { | ||
| 1542 | for (di.abbrev_table_list.toSlice()) |*header| { | ||
| 1543 | if (header.offset == abbrev_offset) { | ||
| 1544 | return &header.table; | ||
| 1545 | } | ||
| 1546 | } | ||
| 1547 | try di.dwarf_seekable_stream.seekTo(di.debug_abbrev.offset + abbrev_offset); | ||
| 1548 | try di.abbrev_table_list.append(AbbrevTableHeader{ | ||
| 1549 | .offset = abbrev_offset, | ||
| 1550 | .table = try parseAbbrevTable(di), | ||
| 1551 | }); | ||
| 1552 | return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry { | ||
| 1556 | for (abbrev_table.toSliceConst()) |*table_entry| { | ||
| 1557 | if (table_entry.abbrev_code == abbrev_code) return table_entry; | ||
| 1558 | } | ||
| 1559 | return null; | ||
| 1560 | } | ||
| 1561 | |||
| 1562 | fn parseDie(di: *DwarfInfo, abbrev_table: *const AbbrevTable, is_64: bool) !Die { | ||
| 1563 | const abbrev_code = try readULeb128(di.dwarf_in_stream); | ||
| 1564 | const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo; | ||
| 1565 | |||
| 1566 | var result = Die{ | ||
| 1567 | .tag_id = table_entry.tag_id, | ||
| 1568 | .has_children = table_entry.has_children, | ||
| 1569 | .attrs = ArrayList(Die.Attr).init(di.allocator()), | ||
| 1570 | }; | ||
| 1571 | try result.attrs.resize(table_entry.attrs.len); | ||
| 1572 | for (table_entry.attrs.toSliceConst()) |attr, i| { | ||
| 1573 | result.attrs.items[i] = Die.Attr{ | ||
| 1574 | .id = attr.attr_id, | ||
| 1575 | .value = try parseFormValue(di.allocator(), di.dwarf_in_stream, attr.form_id, is_64), | ||
| 1576 | }; | ||
| 1577 | } | ||
| 1578 | return result; | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, target_address: usize) !LineInfo { | ||
| 1582 | const ofile = symbol.ofile orelse return error.MissingDebugInfo; | ||
| 1583 | const gop = try di.ofiles.getOrPut(ofile); | ||
| 1584 | const mach_o_file = if (gop.found_existing) &gop.kv.value else blk: { | ||
| 1585 | errdefer _ = di.ofiles.remove(ofile); | ||
| 1586 | const ofile_path = mem.toSliceConst(u8, di.strings.ptr + ofile.n_strx); | ||
| 1587 | |||
| 1588 | gop.kv.value = MachOFile{ | ||
| 1589 | .bytes = try std.io.readFileAllocAligned(di.ofiles.allocator, ofile_path, @alignOf(macho.mach_header_64)), | ||
| 1590 | .sect_debug_info = null, | ||
| 1591 | .sect_debug_line = null, | ||
| 1592 | }; | ||
| 1593 | const hdr = @ptrCast(*const macho.mach_header_64, gop.kv.value.bytes.ptr); | ||
| 1594 | if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo; | ||
| 1595 | |||
| 1596 | const hdr_base = @ptrCast([*]const u8, hdr); | ||
| 1597 | var ptr = hdr_base + @sizeOf(macho.mach_header_64); | ||
| 1598 | var ncmd: u32 = hdr.ncmds; | ||
| 1599 | const segcmd = while (ncmd != 0) : (ncmd -= 1) { | ||
| 1600 | const lc = @ptrCast(*const std.macho.load_command, ptr); | ||
| 1601 | switch (lc.cmd) { | ||
| 1602 | std.macho.LC_SEGMENT_64 => break @ptrCast(*const std.macho.segment_command_64, @alignCast(@alignOf(std.macho.segment_command_64), ptr)), | ||
| 1603 | else => {}, | ||
| 1604 | } | ||
| 1605 | ptr += lc.cmdsize; // TODO https://github.com/ziglang/zig/issues/1403 | ||
| 1606 | } else { | ||
| 1607 | return error.MissingDebugInfo; | ||
| 1608 | }; | ||
| 1609 | const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects]; | ||
| 1610 | for (sections) |*sect| { | ||
| 1611 | if (sect.flags & macho.SECTION_TYPE == macho.S_REGULAR and | ||
| 1612 | (sect.flags & macho.SECTION_ATTRIBUTES) & macho.S_ATTR_DEBUG == macho.S_ATTR_DEBUG) | ||
| 1613 | { | ||
| 1614 | const sect_name = mem.toSliceConst(u8, &sect.sectname); | ||
| 1615 | if (mem.eql(u8, sect_name, "__debug_line")) { | ||
| 1616 | gop.kv.value.sect_debug_line = sect; | ||
| 1617 | } else if (mem.eql(u8, sect_name, "__debug_info")) { | ||
| 1618 | gop.kv.value.sect_debug_info = sect; | ||
| 1619 | } | ||
| 1620 | } | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | break :blk &gop.kv.value; | ||
| 1624 | }; | ||
| 1625 | |||
| 1626 | const sect_debug_line = mach_o_file.sect_debug_line orelse return error.MissingDebugInfo; | ||
| 1627 | var ptr = mach_o_file.bytes.ptr + sect_debug_line.offset; | ||
| 1628 | |||
| 1629 | var is_64: bool = undefined; | ||
| 1630 | const unit_length = try readInitialLengthMem(&ptr, &is_64); | ||
| 1631 | if (unit_length == 0) return error.MissingDebugInfo; | ||
| 1632 | |||
| 1633 | const version = readIntMem(&ptr, u16, builtin.Endian.Little); | ||
| 1634 | // TODO support 3 and 5 | ||
| 1635 | if (version != 2 and version != 4) return error.InvalidDebugInfo; | ||
| 1636 | |||
| 1637 | const prologue_length = if (is_64) | ||
| 1638 | readIntMem(&ptr, u64, builtin.Endian.Little) | ||
| 1639 | else | ||
| 1640 | readIntMem(&ptr, u32, builtin.Endian.Little); | ||
| 1641 | const prog_start = ptr + prologue_length; | ||
| 1642 | |||
| 1643 | const minimum_instruction_length = readByteMem(&ptr); | ||
| 1644 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; | ||
| 1645 | |||
| 1646 | if (version >= 4) { | ||
| 1647 | // maximum_operations_per_instruction | ||
| 1648 | ptr += 1; | ||
| 1649 | } | ||
| 1650 | |||
| 1651 | const default_is_stmt = readByteMem(&ptr) != 0; | ||
| 1652 | const line_base = readByteSignedMem(&ptr); | ||
| 1653 | |||
| 1654 | const line_range = readByteMem(&ptr); | ||
| 1655 | if (line_range == 0) return error.InvalidDebugInfo; | ||
| 1656 | |||
| 1657 | const opcode_base = readByteMem(&ptr); | ||
| 1658 | |||
| 1659 | const standard_opcode_lengths = ptr[0 .. opcode_base - 1]; | ||
| 1660 | ptr += opcode_base - 1; | ||
| 1661 | |||
| 1662 | var include_directories = ArrayList([]const u8).init(di.allocator()); | ||
| 1663 | try include_directories.append(""); | ||
| 1664 | while (true) { | ||
| 1665 | const dir = readStringMem(&ptr); | ||
| 1666 | if (dir.len == 0) break; | ||
| 1667 | try include_directories.append(dir); | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | var file_entries = ArrayList(FileEntry).init(di.allocator()); | ||
| 1671 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); | ||
| 1672 | |||
| 1673 | while (true) { | ||
| 1674 | const file_name = readStringMem(&ptr); | ||
| 1675 | if (file_name.len == 0) break; | ||
| 1676 | const dir_index = try readULeb128Mem(&ptr); | ||
| 1677 | const mtime = try readULeb128Mem(&ptr); | ||
| 1678 | const len_bytes = try readULeb128Mem(&ptr); | ||
| 1679 | try file_entries.append(FileEntry{ | ||
| 1680 | .file_name = file_name, | ||
| 1681 | .dir_index = dir_index, | ||
| 1682 | .mtime = mtime, | ||
| 1683 | .len_bytes = len_bytes, | ||
| 1684 | }); | ||
| 1685 | } | ||
| 1686 | |||
| 1687 | ptr = prog_start; | ||
| 1688 | while (true) { | ||
| 1689 | const opcode = readByteMem(&ptr); | ||
| 1690 | |||
| 1691 | if (opcode == DW.LNS_extended_op) { | ||
| 1692 | const op_size = try readULeb128Mem(&ptr); | ||
| 1693 | if (op_size < 1) return error.InvalidDebugInfo; | ||
| 1694 | var sub_op = readByteMem(&ptr); | ||
| 1695 | switch (sub_op) { | ||
| 1696 | DW.LNE_end_sequence => { | ||
| 1697 | prog.end_sequence = true; | ||
| 1698 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1699 | return error.MissingDebugInfo; | ||
| 1700 | }, | ||
| 1701 | DW.LNE_set_address => { | ||
| 1702 | const addr = readIntMem(&ptr, usize, builtin.Endian.Little); | ||
| 1703 | prog.address = symbol.reloc + addr; | ||
| 1704 | }, | ||
| 1705 | DW.LNE_define_file => { | ||
| 1706 | const file_name = readStringMem(&ptr); | ||
| 1707 | const dir_index = try readULeb128Mem(&ptr); | ||
| 1708 | const mtime = try readULeb128Mem(&ptr); | ||
| 1709 | const len_bytes = try readULeb128Mem(&ptr); | ||
| 1710 | try file_entries.append(FileEntry{ | ||
| 1711 | .file_name = file_name, | ||
| 1712 | .dir_index = dir_index, | ||
| 1713 | .mtime = mtime, | ||
| 1714 | .len_bytes = len_bytes, | ||
| 1715 | }); | ||
| 1716 | }, | ||
| 1717 | else => { | ||
| 1718 | ptr += op_size - 1; | ||
| 1719 | }, | ||
| 1720 | } | ||
| 1721 | } else if (opcode >= opcode_base) { | ||
| 1722 | // special opcodes | ||
| 1723 | const adjusted_opcode = opcode - opcode_base; | ||
| 1724 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); | ||
| 1725 | const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); | ||
| 1726 | prog.line += inc_line; | ||
| 1727 | prog.address += inc_addr; | ||
| 1728 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1729 | prog.basic_block = false; | ||
| 1730 | } else { | ||
| 1731 | switch (opcode) { | ||
| 1732 | DW.LNS_copy => { | ||
| 1733 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1734 | prog.basic_block = false; | ||
| 1735 | }, | ||
| 1736 | DW.LNS_advance_pc => { | ||
| 1737 | const arg = try readULeb128Mem(&ptr); | ||
| 1738 | prog.address += arg * minimum_instruction_length; | ||
| 1739 | }, | ||
| 1740 | DW.LNS_advance_line => { | ||
| 1741 | const arg = try readILeb128Mem(&ptr); | ||
| 1742 | prog.line += arg; | ||
| 1743 | }, | ||
| 1744 | DW.LNS_set_file => { | ||
| 1745 | const arg = try readULeb128Mem(&ptr); | ||
| 1746 | prog.file = arg; | ||
| 1747 | }, | ||
| 1748 | DW.LNS_set_column => { | ||
| 1749 | const arg = try readULeb128Mem(&ptr); | ||
| 1750 | prog.column = arg; | ||
| 1751 | }, | ||
| 1752 | DW.LNS_negate_stmt => { | ||
| 1753 | prog.is_stmt = !prog.is_stmt; | ||
| 1754 | }, | ||
| 1755 | DW.LNS_set_basic_block => { | ||
| 1756 | prog.basic_block = true; | ||
| 1757 | }, | ||
| 1758 | DW.LNS_const_add_pc => { | ||
| 1759 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); | ||
| 1760 | prog.address += inc_addr; | ||
| 1761 | }, | ||
| 1762 | DW.LNS_fixed_advance_pc => { | ||
| 1763 | const arg = readIntMem(&ptr, u16, builtin.Endian.Little); | ||
| 1764 | prog.address += arg; | ||
| 1765 | }, | ||
| 1766 | DW.LNS_set_prologue_end => {}, | ||
| 1767 | else => { | ||
| 1768 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; | ||
| 1769 | const len_bytes = standard_opcode_lengths[opcode - 1]; | ||
| 1770 | ptr += len_bytes; | ||
| 1771 | }, | ||
| 1772 | } | ||
| 1773 | } | ||
| 1774 | } | ||
| 1775 | |||
| 1776 | return error.MissingDebugInfo; | ||
| 1777 | } | ||
| 1778 | |||
| 1779 | fn getLineNumberInfoDwarf(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo { | ||
| 1780 | const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir); | ||
| 1781 | |||
| 1782 | const debug_line_end = di.debug_line.offset + di.debug_line.size; | ||
| 1783 | var this_offset = di.debug_line.offset; | ||
| 1784 | var this_index: usize = 0; | ||
| 1785 | |||
| 1786 | while (this_offset < debug_line_end) : (this_index += 1) { | ||
| 1787 | try di.dwarf_seekable_stream.seekTo(this_offset); | ||
| 1788 | |||
| 1789 | var is_64: bool = undefined; | ||
| 1790 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | ||
| 1791 | if (unit_length == 0) return error.MissingDebugInfo; | ||
| 1792 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); | ||
| 1793 | |||
| 1794 | if (compile_unit.index != this_index) { | ||
| 1795 | this_offset += next_offset; | ||
| 1796 | continue; | ||
| 1797 | } | ||
| 1798 | |||
| 1799 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | ||
| 1800 | // TODO support 3 and 5 | ||
| 1801 | if (version != 2 and version != 4) return error.InvalidDebugInfo; | ||
| 1802 | |||
| 1803 | 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); | ||
| 1804 | const prog_start_offset = (try di.dwarf_seekable_stream.getPos()) + prologue_length; | ||
| 1805 | |||
| 1806 | const minimum_instruction_length = try di.dwarf_in_stream.readByte(); | ||
| 1807 | if (minimum_instruction_length == 0) return error.InvalidDebugInfo; | ||
| 1808 | |||
| 1809 | if (version >= 4) { | ||
| 1810 | // maximum_operations_per_instruction | ||
| 1811 | _ = try di.dwarf_in_stream.readByte(); | ||
| 1812 | } | ||
| 1813 | |||
| 1814 | const default_is_stmt = (try di.dwarf_in_stream.readByte()) != 0; | ||
| 1815 | const line_base = try di.dwarf_in_stream.readByteSigned(); | ||
| 1816 | |||
| 1817 | const line_range = try di.dwarf_in_stream.readByte(); | ||
| 1818 | if (line_range == 0) return error.InvalidDebugInfo; | ||
| 1819 | |||
| 1820 | const opcode_base = try di.dwarf_in_stream.readByte(); | ||
| 1821 | |||
| 1822 | const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1); | ||
| 1823 | |||
| 1824 | { | ||
| 1825 | var i: usize = 0; | ||
| 1826 | while (i < opcode_base - 1) : (i += 1) { | ||
| 1827 | standard_opcode_lengths[i] = try di.dwarf_in_stream.readByte(); | ||
| 1828 | } | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | var include_directories = ArrayList([]u8).init(di.allocator()); | ||
| 1832 | try include_directories.append(compile_unit_cwd); | ||
| 1833 | while (true) { | ||
| 1834 | const dir = try di.readString(); | ||
| 1835 | if (dir.len == 0) break; | ||
| 1836 | try include_directories.append(dir); | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | var file_entries = ArrayList(FileEntry).init(di.allocator()); | ||
| 1840 | var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address); | ||
| 1841 | |||
| 1842 | while (true) { | ||
| 1843 | const file_name = try di.readString(); | ||
| 1844 | if (file_name.len == 0) break; | ||
| 1845 | const dir_index = try readULeb128(di.dwarf_in_stream); | ||
| 1846 | const mtime = try readULeb128(di.dwarf_in_stream); | ||
| 1847 | const len_bytes = try readULeb128(di.dwarf_in_stream); | ||
| 1848 | try file_entries.append(FileEntry{ | ||
| 1849 | .file_name = file_name, | ||
| 1850 | .dir_index = dir_index, | ||
| 1851 | .mtime = mtime, | ||
| 1852 | .len_bytes = len_bytes, | ||
| 1853 | }); | ||
| 1854 | } | ||
| 1855 | |||
| 1856 | try di.dwarf_seekable_stream.seekTo(prog_start_offset); | ||
| 1857 | |||
| 1858 | while (true) { | ||
| 1859 | const opcode = try di.dwarf_in_stream.readByte(); | ||
| 1860 | |||
| 1861 | if (opcode == DW.LNS_extended_op) { | ||
| 1862 | const op_size = try readULeb128(di.dwarf_in_stream); | ||
| 1863 | if (op_size < 1) return error.InvalidDebugInfo; | ||
| 1864 | var sub_op = try di.dwarf_in_stream.readByte(); | ||
| 1865 | switch (sub_op) { | ||
| 1866 | DW.LNE_end_sequence => { | ||
| 1867 | prog.end_sequence = true; | ||
| 1868 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1869 | return error.MissingDebugInfo; | ||
| 1870 | }, | ||
| 1871 | DW.LNE_set_address => { | ||
| 1872 | const addr = try di.dwarf_in_stream.readInt(usize, di.endian); | ||
| 1873 | prog.address = addr; | ||
| 1874 | }, | ||
| 1875 | DW.LNE_define_file => { | ||
| 1876 | const file_name = try di.readString(); | ||
| 1877 | const dir_index = try readULeb128(di.dwarf_in_stream); | ||
| 1878 | const mtime = try readULeb128(di.dwarf_in_stream); | ||
| 1879 | const len_bytes = try readULeb128(di.dwarf_in_stream); | ||
| 1880 | try file_entries.append(FileEntry{ | ||
| 1881 | .file_name = file_name, | ||
| 1882 | .dir_index = dir_index, | ||
| 1883 | .mtime = mtime, | ||
| 1884 | .len_bytes = len_bytes, | ||
| 1885 | }); | ||
| 1886 | }, | ||
| 1887 | else => { | ||
| 1888 | const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo; | ||
| 1889 | try di.dwarf_seekable_stream.seekForward(fwd_amt); | ||
| 1890 | }, | ||
| 1891 | } | ||
| 1892 | } else if (opcode >= opcode_base) { | ||
| 1893 | // special opcodes | ||
| 1894 | const adjusted_opcode = opcode - opcode_base; | ||
| 1895 | const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range); | ||
| 1896 | const inc_line = i32(line_base) + i32(adjusted_opcode % line_range); | ||
| 1897 | prog.line += inc_line; | ||
| 1898 | prog.address += inc_addr; | ||
| 1899 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1900 | prog.basic_block = false; | ||
| 1901 | } else { | ||
| 1902 | switch (opcode) { | ||
| 1903 | DW.LNS_copy => { | ||
| 1904 | if (try prog.checkLineMatch()) |info| return info; | ||
| 1905 | prog.basic_block = false; | ||
| 1906 | }, | ||
| 1907 | DW.LNS_advance_pc => { | ||
| 1908 | const arg = try readULeb128(di.dwarf_in_stream); | ||
| 1909 | prog.address += arg * minimum_instruction_length; | ||
| 1910 | }, | ||
| 1911 | DW.LNS_advance_line => { | ||
| 1912 | const arg = try readILeb128(di.dwarf_in_stream); | ||
| 1913 | prog.line += arg; | ||
| 1914 | }, | ||
| 1915 | DW.LNS_set_file => { | ||
| 1916 | const arg = try readULeb128(di.dwarf_in_stream); | ||
| 1917 | prog.file = arg; | ||
| 1918 | }, | ||
| 1919 | DW.LNS_set_column => { | ||
| 1920 | const arg = try readULeb128(di.dwarf_in_stream); | ||
| 1921 | prog.column = arg; | ||
| 1922 | }, | ||
| 1923 | DW.LNS_negate_stmt => { | ||
| 1924 | prog.is_stmt = !prog.is_stmt; | ||
| 1925 | }, | ||
| 1926 | DW.LNS_set_basic_block => { | ||
| 1927 | prog.basic_block = true; | ||
| 1928 | }, | ||
| 1929 | DW.LNS_const_add_pc => { | ||
| 1930 | const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range); | ||
| 1931 | prog.address += inc_addr; | ||
| 1932 | }, | ||
| 1933 | DW.LNS_fixed_advance_pc => { | ||
| 1934 | const arg = try di.dwarf_in_stream.readInt(u16, di.endian); | ||
| 1935 | prog.address += arg; | ||
| 1936 | }, | ||
| 1937 | DW.LNS_set_prologue_end => {}, | ||
| 1938 | else => { | ||
| 1939 | if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo; | ||
| 1940 | const len_bytes = standard_opcode_lengths[opcode - 1]; | ||
| 1941 | try di.dwarf_seekable_stream.seekForward(len_bytes); | ||
| 1942 | }, | ||
| 1943 | } | ||
| 1944 | } | ||
| 1945 | } | ||
| 1946 | |||
| 1947 | this_offset += next_offset; | ||
| 1948 | } | ||
| 1949 | |||
| 1950 | return error.MissingDebugInfo; | ||
| 1951 | } | ||
| 1952 | |||
| 1953 | fn scanAllCompileUnits(di: *DwarfInfo) !void { | ||
| 1954 | const debug_info_end = di.debug_info.offset + di.debug_info.size; | ||
| 1955 | var this_unit_offset = di.debug_info.offset; | ||
| 1956 | var cu_index: usize = 0; | ||
| 1957 | |||
| 1958 | while (this_unit_offset < debug_info_end) { | ||
| 1959 | try di.dwarf_seekable_stream.seekTo(this_unit_offset); | ||
| 1960 | |||
| 1961 | var is_64: bool = undefined; | ||
| 1962 | const unit_length = try readInitialLength(@typeOf(di.dwarf_in_stream.readFn).ReturnType.ErrorSet, di.dwarf_in_stream, &is_64); | ||
| 1963 | if (unit_length == 0) return; | ||
| 1964 | const next_offset = unit_length + (if (is_64) usize(12) else usize(4)); | ||
| 1965 | |||
| 1966 | const version = try di.dwarf_in_stream.readInt(u16, di.endian); | ||
| 1967 | if (version < 2 or version > 5) return error.InvalidDebugInfo; | ||
| 1968 | |||
| 1969 | 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); | ||
| 1970 | |||
| 1971 | const address_size = try di.dwarf_in_stream.readByte(); | ||
| 1972 | if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo; | ||
| 1973 | |||
| 1974 | const compile_unit_pos = try di.dwarf_seekable_stream.getPos(); | ||
| 1975 | const abbrev_table = try getAbbrevTable(di, debug_abbrev_offset); | ||
| 1976 | |||
| 1977 | try di.dwarf_seekable_stream.seekTo(compile_unit_pos); | ||
| 1978 | |||
| 1979 | const compile_unit_die = try di.allocator().create(Die); | ||
| 1980 | compile_unit_die.* = try parseDie(di, abbrev_table, is_64); | ||
| 1981 | |||
| 1982 | if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo; | ||
| 1983 | |||
| 1984 | const pc_range = x: { | ||
| 1985 | if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| { | ||
| 1986 | if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| { | ||
| 1987 | const pc_end = switch (high_pc_value.*) { | ||
| 1988 | FormValue.Address => |value| value, | ||
| 1989 | FormValue.Const => |value| b: { | ||
| 1990 | const offset = try value.asUnsignedLe(); | ||
| 1991 | break :b (low_pc + offset); | ||
| 1992 | }, | ||
| 1993 | else => return error.InvalidDebugInfo, | ||
| 1994 | }; | ||
| 1995 | break :x PcRange{ | ||
| 1996 | .start = low_pc, | ||
| 1997 | .end = pc_end, | ||
| 1998 | }; | ||
| 1999 | } else { | ||
| 2000 | break :x null; | ||
| 2001 | } | ||
| 2002 | } else |err| { | ||
| 2003 | if (err != error.MissingDebugInfo) return err; | ||
| 2004 | break :x null; | ||
| 2005 | } | ||
| 2006 | }; | ||
| 2007 | |||
| 2008 | try di.compile_unit_list.append(CompileUnit{ | ||
| 2009 | .version = version, | ||
| 2010 | .is_64 = is_64, | ||
| 2011 | .pc_range = pc_range, | ||
| 2012 | .die = compile_unit_die, | ||
| 2013 | .index = cu_index, | ||
| 2014 | }); | ||
| 2015 | |||
| 2016 | this_unit_offset += next_offset; | ||
| 2017 | cu_index += 1; | ||
| 2018 | } | ||
| 2019 | } | ||
| 2020 | |||
| 2021 | fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit { | ||
| 2022 | for (di.compile_unit_list.toSlice()) |*compile_unit| { | ||
| 2023 | if (compile_unit.pc_range) |range| { | ||
| 2024 | if (target_address >= range.start and target_address < range.end) return compile_unit; | ||
| 2025 | } | ||
| 2026 | if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| { | ||
| 2027 | var base_address: usize = 0; | ||
| 2028 | if (di.debug_ranges) |debug_ranges| { | ||
| 2029 | try di.dwarf_seekable_stream.seekTo(debug_ranges.offset + ranges_offset); | ||
| 2030 | while (true) { | ||
| 2031 | const begin_addr = try di.dwarf_in_stream.readIntLittle(usize); | ||
| 2032 | const end_addr = try di.dwarf_in_stream.readIntLittle(usize); | ||
| 2033 | if (begin_addr == 0 and end_addr == 0) { | ||
| 2034 | break; | ||
| 2035 | } | ||
| 2036 | if (begin_addr == maxInt(usize)) { | ||
| 2037 | base_address = begin_addr; | ||
| 2038 | continue; | ||
| 2039 | } | ||
| 2040 | if (target_address >= begin_addr and target_address < end_addr) { | ||
| 2041 | return compile_unit; | ||
| 2042 | } | ||
| 2043 | } | ||
| 2044 | } | ||
| 2045 | } else |err| { | ||
| 2046 | if (err != error.MissingDebugInfo) return err; | ||
| 2047 | continue; | ||
| 2048 | } | ||
| 2049 | } | ||
| 2050 | return error.MissingDebugInfo; | ||
| 2051 | } | ||
| 2052 | |||
| 2053 | fn readIntMem(ptr: *[*]const u8, comptime T: type, endian: builtin.Endian) T { | ||
| 2054 | // TODO https://github.com/ziglang/zig/issues/863 | ||
| 2055 | const result = mem.readIntSlice(T, ptr.*[0..@sizeOf(T)], endian); | ||
| 2056 | ptr.* += @sizeOf(T); | ||
| 2057 | return result; | ||
| 2058 | } | ||
| 2059 | |||
| 2060 | fn readByteMem(ptr: *[*]const u8) u8 { | ||
| 2061 | const result = ptr.*[0]; | ||
| 2062 | ptr.* += 1; | ||
| 2063 | return result; | ||
| 2064 | } | ||
| 2065 | |||
| 2066 | fn readByteSignedMem(ptr: *[*]const u8) i8 { | ||
| 2067 | return @bitCast(i8, readByteMem(ptr)); | ||
| 2068 | } | ||
| 2069 | |||
| 2070 | fn readInitialLengthMem(ptr: *[*]const u8, is_64: *bool) !u64 { | ||
| 2071 | // TODO this code can be improved with https://github.com/ziglang/zig/issues/863 | ||
| 2072 | const first_32_bits = mem.readIntSliceLittle(u32, ptr.*[0..4]); | ||
| 2073 | is_64.* = (first_32_bits == 0xffffffff); | ||
| 2074 | if (is_64.*) { | ||
| 2075 | ptr.* += 4; | ||
| 2076 | const result = mem.readIntSliceLittle(u64, ptr.*[0..8]); | ||
| 2077 | ptr.* += 8; | ||
| 2078 | return result; | ||
| 2079 | } else { | ||
| 2080 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; | ||
| 2081 | ptr.* += 4; | ||
| 2082 | return u64(first_32_bits); | ||
| 2083 | } | ||
| 2084 | } | ||
| 2085 | |||
| 2086 | fn readStringMem(ptr: *[*]const u8) []const u8 { | ||
| 2087 | const result = mem.toSliceConst(u8, ptr.*); | ||
| 2088 | ptr.* += result.len + 1; | ||
| 2089 | return result; | ||
| 2090 | } | ||
| 2091 | |||
| 2092 | fn readULeb128Mem(ptr: *[*]const u8) !u64 { | ||
| 2093 | var result: u64 = 0; | ||
| 2094 | var shift: usize = 0; | ||
| 2095 | var i: usize = 0; | ||
| 2096 | |||
| 2097 | while (true) { | ||
| 2098 | const byte = ptr.*[i]; | ||
| 2099 | i += 1; | ||
| 2100 | |||
| 2101 | var operand: u64 = undefined; | ||
| 2102 | |||
| 2103 | if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2104 | |||
| 2105 | result |= operand; | ||
| 2106 | |||
| 2107 | if ((byte & 0b10000000) == 0) { | ||
| 2108 | ptr.* += i; | ||
| 2109 | return result; | ||
| 2110 | } | ||
| 2111 | |||
| 2112 | shift += 7; | ||
| 2113 | } | ||
| 2114 | } | ||
| 2115 | fn readILeb128Mem(ptr: *[*]const u8) !i64 { | ||
| 2116 | var result: i64 = 0; | ||
| 2117 | var shift: usize = 0; | ||
| 2118 | var i: usize = 0; | ||
| 2119 | |||
| 2120 | while (true) { | ||
| 2121 | const byte = ptr.*[i]; | ||
| 2122 | i += 1; | ||
| 2123 | |||
| 2124 | var operand: i64 = undefined; | ||
| 2125 | if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2126 | |||
| 2127 | result |= operand; | ||
| 2128 | shift += 7; | ||
| 2129 | |||
| 2130 | if ((byte & 0b10000000) == 0) { | ||
| 2131 | if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); | ||
| 2132 | ptr.* += i; | ||
| 2133 | return result; | ||
| 2134 | } | ||
| 2135 | } | ||
| 2136 | } | ||
| 2137 | |||
| 2138 | fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 { | ||
| 2139 | const first_32_bits = try in_stream.readIntLittle(u32); | ||
| 2140 | is_64.* = (first_32_bits == 0xffffffff); | ||
| 2141 | if (is_64.*) { | ||
| 2142 | return in_stream.readIntLittle(u64); | ||
| 2143 | } else { | ||
| 2144 | if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo; | ||
| 2145 | return u64(first_32_bits); | ||
| 2146 | } | ||
| 2147 | } | ||
| 2148 | |||
| 2149 | fn readULeb128(in_stream: var) !u64 { | ||
| 2150 | var result: u64 = 0; | ||
| 2151 | var shift: usize = 0; | ||
| 2152 | |||
| 2153 | while (true) { | ||
| 2154 | const byte = try in_stream.readByte(); | ||
| 2155 | |||
| 2156 | var operand: u64 = undefined; | ||
| 2157 | |||
| 2158 | if (@shlWithOverflow(u64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2159 | |||
| 2160 | result |= operand; | ||
| 2161 | |||
| 2162 | if ((byte & 0b10000000) == 0) return result; | ||
| 2163 | |||
| 2164 | shift += 7; | ||
| 2165 | } | ||
| 2166 | } | ||
| 2167 | |||
| 2168 | fn readILeb128(in_stream: var) !i64 { | ||
| 2169 | var result: i64 = 0; | ||
| 2170 | var shift: usize = 0; | ||
| 2171 | |||
| 2172 | while (true) { | ||
| 2173 | const byte = try in_stream.readByte(); | ||
| 2174 | |||
| 2175 | var operand: i64 = undefined; | ||
| 2176 | |||
| 2177 | if (@shlWithOverflow(i64, byte & 0b01111111, @intCast(u6, shift), &operand)) return error.InvalidDebugInfo; | ||
| 2178 | |||
| 2179 | result |= operand; | ||
| 2180 | shift += 7; | ||
| 2181 | |||
| 2182 | if ((byte & 0b10000000) == 0) { | ||
| 2183 | if (shift < @sizeOf(i64) * 8 and (byte & 0b01000000) != 0) result |= -(i64(1) << @intCast(u6, shift)); | ||
| 2184 | return result; | ||
| 2185 | } | ||
| 2186 | } | ||
| 2187 | } | ||
| 2188 | |||
| 2189 | /// This should only be used in temporary test programs. | ||
| 2190 | pub const global_allocator = &global_fixed_allocator.allocator; | ||
| 2191 | var global_fixed_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(global_allocator_mem[0..]); | ||
| 2192 | var global_allocator_mem: [100 * 1024]u8 = undefined; | ||
| 2193 | |||
| 2194 | /// TODO multithreaded awareness | ||
| 2195 | var debug_info_allocator: ?*mem.Allocator = null; | ||
| 2196 | var debug_info_direct_allocator: std.heap.DirectAllocator = undefined; | ||
| 2197 | var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined; | ||
| 2198 | fn getDebugInfoAllocator() *mem.Allocator { | ||
| 2199 | if (debug_info_allocator) |a| return a; | ||
| 2200 | |||
| 2201 | debug_info_direct_allocator = std.heap.DirectAllocator.init(); | ||
| 2202 | debug_info_arena_allocator = std.heap.ArenaAllocator.init(&debug_info_direct_allocator.allocator); | ||
| 2203 | debug_info_allocator = &debug_info_arena_allocator.allocator; | ||
| 2204 | return &debug_info_arena_allocator.allocator; | ||
| 2205 | } | ||
std/dynamic_library.zig+1-1| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const Os = builtin.Os; | 2 | const Os = builtin.Os; |
| 3 | 3 | ||
| 4 | const std = @import("index.zig"); | 4 | const std = @import("std.zig"); |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | const cstr = std.cstr; | 6 | const cstr = std.cstr; |
| 7 | const os = std.os; | 7 | const os = std.os; |
std/elf.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | 2 | const std = @import("std.zig"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const os = std.os; | 4 | const os = std.os; |
| 5 | const math = std.math; | 5 | const math = std.math; |
std/event/channel.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/event/fs.zig+3-10| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("../index.zig"); | 2 | const std = @import("../std.zig"); |
| 3 | const event = std.event; | 3 | const event = std.event; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
| 5 | const testing = std.testing; | 5 | const testing = std.testing; |
| ... | @@ -404,11 +404,7 @@ pub async fn openPosix( | ... | @@ -404,11 +404,7 @@ pub async fn openPosix( |
| 404 | 404 | ||
| 405 | pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { | 405 | pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { |
| 406 | switch (builtin.os) { | 406 | switch (builtin.os) { |
| 407 | builtin.Os.macosx, | 407 | builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { |
| 408 | builtin.Os.linux, | ||
| 409 | builtin.Os.freebsd, | ||
| 410 | builtin.Os.netbsd | ||
| 411 | => { | ||
| 412 | const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC; | 408 | const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC; |
| 413 | return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); | 409 | return await (async openPosix(loop, path, flags, os.File.default_mode) catch unreachable); |
| 414 | }, | 410 | }, |
| ... | @@ -876,10 +872,7 @@ pub fn Watch(comptime V: type) type { | ... | @@ -876,10 +872,7 @@ pub fn Watch(comptime V: type) type { |
| 876 | 872 | ||
| 877 | pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { | 873 | pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { |
| 878 | switch (builtin.os) { | 874 | switch (builtin.os) { |
| 879 | builtin.Os.macosx, | 875 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => return await (async addFileKEvent(self, file_path, value) catch unreachable), |
| 880 | builtin.Os.freebsd, | ||
| 881 | builtin.Os.netbsd | ||
| 882 | => return await (async addFileKEvent(self, file_path, value) catch unreachable), | ||
| 883 | builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), | 876 | builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), |
| 884 | builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable), | 877 | builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable), |
| 885 | else => @compileError("Unsupported OS"), | 878 | else => @compileError("Unsupported OS"), |
std/event/future.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const builtin = @import("builtin"); | 4 | const builtin = @import("builtin"); |
std/event/group.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Lock = std.event.Lock; | 3 | const Lock = std.event.Lock; |
| 4 | const Loop = std.event.Loop; | 4 | const Loop = std.event.Loop; |
std/event/io.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Allocator = std.mem.Allocator; | 3 | const Allocator = std.mem.Allocator; |
| 4 | const assert = std.debug.assert; | 4 | const assert = std.debug.assert; |
std/event/lock.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/event/locked.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const Lock = std.event.Lock; | 2 | const Lock = std.event.Lock; |
| 3 | const Loop = std.event.Loop; | 3 | const Loop = std.event.Loop; |
| 4 | 4 |
std/event/loop.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/event/net.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const event = std.event; | 4 | const event = std.event; |
std/event/rwlock.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/event/rwlocked.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const RwLock = std.event.RwLock; | 2 | const RwLock = std.event.RwLock; |
| 3 | const Loop = std.event.Loop; | 3 | const Loop = std.event.Loop; |
| 4 | 4 |
std/fmt.zig created+1441| ... | @@ -0,0 +1,1441 @@ | ||
| 1 | const std = @import("std.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const debug = std.debug; | ||
| 4 | const assert = debug.assert; | ||
| 5 | const testing = std.testing; | ||
| 6 | const mem = std.mem; | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | const errol = @import("fmt/errol.zig"); | ||
| 9 | const lossyCast = std.math.lossyCast; | ||
| 10 | |||
| 11 | const max_int_digits = 65; | ||
| 12 | |||
| 13 | /// Renders fmt string with args, calling output with slices of bytes. | ||
| 14 | /// If `output` returns an error, the error is returned from `format` and | ||
| 15 | /// `output` is not called again. | ||
| 16 | pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { | ||
| 17 | const State = enum { | ||
| 18 | Start, | ||
| 19 | OpenBrace, | ||
| 20 | CloseBrace, | ||
| 21 | FormatString, | ||
| 22 | Pointer, | ||
| 23 | }; | ||
| 24 | |||
| 25 | comptime var start_index = 0; | ||
| 26 | comptime var state = State.Start; | ||
| 27 | comptime var next_arg = 0; | ||
| 28 | |||
| 29 | inline for (fmt) |c, i| { | ||
| 30 | switch (state) { | ||
| 31 | State.Start => switch (c) { | ||
| 32 | '{' => { | ||
| 33 | if (start_index < i) { | ||
| 34 | try output(context, fmt[start_index..i]); | ||
| 35 | } | ||
| 36 | start_index = i; | ||
| 37 | state = State.OpenBrace; | ||
| 38 | }, | ||
| 39 | |||
| 40 | '}' => { | ||
| 41 | if (start_index < i) { | ||
| 42 | try output(context, fmt[start_index..i]); | ||
| 43 | } | ||
| 44 | state = State.CloseBrace; | ||
| 45 | }, | ||
| 46 | else => {}, | ||
| 47 | }, | ||
| 48 | State.OpenBrace => switch (c) { | ||
| 49 | '{' => { | ||
| 50 | state = State.Start; | ||
| 51 | start_index = i; | ||
| 52 | }, | ||
| 53 | '}' => { | ||
| 54 | try formatType(args[next_arg], fmt[0..0], context, Errors, output); | ||
| 55 | next_arg += 1; | ||
| 56 | state = State.Start; | ||
| 57 | start_index = i + 1; | ||
| 58 | }, | ||
| 59 | '*' => state = State.Pointer, | ||
| 60 | else => { | ||
| 61 | state = State.FormatString; | ||
| 62 | }, | ||
| 63 | }, | ||
| 64 | State.CloseBrace => switch (c) { | ||
| 65 | '}' => { | ||
| 66 | state = State.Start; | ||
| 67 | start_index = i; | ||
| 68 | }, | ||
| 69 | else => @compileError("Single '}' encountered in format string"), | ||
| 70 | }, | ||
| 71 | State.FormatString => switch (c) { | ||
| 72 | '}' => { | ||
| 73 | const s = start_index + 1; | ||
| 74 | try formatType(args[next_arg], fmt[s..i], context, Errors, output); | ||
| 75 | next_arg += 1; | ||
| 76 | state = State.Start; | ||
| 77 | start_index = i + 1; | ||
| 78 | }, | ||
| 79 | else => {}, | ||
| 80 | }, | ||
| 81 | State.Pointer => switch (c) { | ||
| 82 | '}' => { | ||
| 83 | try output(context, @typeName(@typeOf(args[next_arg]).Child)); | ||
| 84 | try output(context, "@"); | ||
| 85 | try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); | ||
| 86 | next_arg += 1; | ||
| 87 | state = State.Start; | ||
| 88 | start_index = i + 1; | ||
| 89 | }, | ||
| 90 | else => @compileError("Unexpected format character after '*'"), | ||
| 91 | }, | ||
| 92 | } | ||
| 93 | } | ||
| 94 | comptime { | ||
| 95 | if (args.len != next_arg) { | ||
| 96 | @compileError("Unused arguments"); | ||
| 97 | } | ||
| 98 | if (state != State.Start) { | ||
| 99 | @compileError("Incomplete format string: " ++ fmt); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | if (start_index < fmt.len) { | ||
| 103 | try output(context, fmt[start_index..]); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | pub fn formatType( | ||
| 108 | value: var, | ||
| 109 | comptime fmt: []const u8, | ||
| 110 | context: var, | ||
| 111 | comptime Errors: type, | ||
| 112 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 113 | ) Errors!void { | ||
| 114 | const T = @typeOf(value); | ||
| 115 | if (T == anyerror) { | ||
| 116 | try output(context, "error."); | ||
| 117 | return output(context, @errorName(value)); | ||
| 118 | } | ||
| 119 | switch (@typeInfo(T)) { | ||
| 120 | builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { | ||
| 121 | return formatValue(value, fmt, context, Errors, output); | ||
| 122 | }, | ||
| 123 | builtin.TypeId.Void => { | ||
| 124 | return output(context, "void"); | ||
| 125 | }, | ||
| 126 | builtin.TypeId.Bool => { | ||
| 127 | return output(context, if (value) "true" else "false"); | ||
| 128 | }, | ||
| 129 | builtin.TypeId.Optional => { | ||
| 130 | if (value) |payload| { | ||
| 131 | return formatType(payload, fmt, context, Errors, output); | ||
| 132 | } else { | ||
| 133 | return output(context, "null"); | ||
| 134 | } | ||
| 135 | }, | ||
| 136 | builtin.TypeId.ErrorUnion => { | ||
| 137 | if (value) |payload| { | ||
| 138 | return formatType(payload, fmt, context, Errors, output); | ||
| 139 | } else |err| { | ||
| 140 | return formatType(err, fmt, context, Errors, output); | ||
| 141 | } | ||
| 142 | }, | ||
| 143 | builtin.TypeId.ErrorSet => { | ||
| 144 | try output(context, "error."); | ||
| 145 | return output(context, @errorName(value)); | ||
| 146 | }, | ||
| 147 | builtin.TypeId.Promise => { | ||
| 148 | return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); | ||
| 149 | }, | ||
| 150 | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { | ||
| 151 | const has_cust_fmt = comptime cf: { | ||
| 152 | const info = @typeInfo(T); | ||
| 153 | const defs = switch (info) { | ||
| 154 | builtin.TypeId.Struct => |s| s.defs, | ||
| 155 | builtin.TypeId.Union => |u| u.defs, | ||
| 156 | builtin.TypeId.Enum => |e| e.defs, | ||
| 157 | else => unreachable, | ||
| 158 | }; | ||
| 159 | |||
| 160 | for (defs) |def| { | ||
| 161 | if (mem.eql(u8, def.name, "format")) { | ||
| 162 | break :cf true; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | break :cf false; | ||
| 166 | }; | ||
| 167 | if (has_cust_fmt) return value.format(fmt, context, Errors, output); | ||
| 168 | |||
| 169 | try output(context, @typeName(T)); | ||
| 170 | switch (comptime @typeId(T)) { | ||
| 171 | builtin.TypeId.Enum => { | ||
| 172 | try output(context, "."); | ||
| 173 | try formatType(@tagName(value), "", context, Errors, output); | ||
| 174 | return; | ||
| 175 | }, | ||
| 176 | builtin.TypeId.Struct => { | ||
| 177 | comptime var field_i = 0; | ||
| 178 | inline while (field_i < @memberCount(T)) : (field_i += 1) { | ||
| 179 | if (field_i == 0) { | ||
| 180 | try output(context, "{ ."); | ||
| 181 | } else { | ||
| 182 | try output(context, ", ."); | ||
| 183 | } | ||
| 184 | try output(context, @memberName(T, field_i)); | ||
| 185 | try output(context, " = "); | ||
| 186 | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output); | ||
| 187 | } | ||
| 188 | try output(context, " }"); | ||
| 189 | }, | ||
| 190 | builtin.TypeId.Union => { | ||
| 191 | const info = @typeInfo(T).Union; | ||
| 192 | if (info.tag_type) |UnionTagType| { | ||
| 193 | try output(context, "{ ."); | ||
| 194 | try output(context, @tagName(UnionTagType(value))); | ||
| 195 | try output(context, " = "); | ||
| 196 | inline for (info.fields) |u_field| { | ||
| 197 | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { | ||
| 198 | try formatType(@field(value, u_field.name), "", context, Errors, output); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | try output(context, " }"); | ||
| 202 | } else { | ||
| 203 | try format(context, Errors, output, "@{x}", @ptrToInt(&value)); | ||
| 204 | } | ||
| 205 | }, | ||
| 206 | else => unreachable, | ||
| 207 | } | ||
| 208 | return; | ||
| 209 | }, | ||
| 210 | builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { | ||
| 211 | builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { | ||
| 212 | builtin.TypeId.Array => |info| { | ||
| 213 | if (info.child == u8) { | ||
| 214 | return formatText(value, fmt, context, Errors, output); | ||
| 215 | } | ||
| 216 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | ||
| 217 | }, | ||
| 218 | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { | ||
| 219 | return formatType(value.*, fmt, context, Errors, output); | ||
| 220 | }, | ||
| 221 | else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), | ||
| 222 | }, | ||
| 223 | builtin.TypeInfo.Pointer.Size.Many => { | ||
| 224 | if (ptr_info.child == u8) { | ||
| 225 | if (fmt.len > 0 and fmt[0] == 's') { | ||
| 226 | const len = std.cstr.len(value); | ||
| 227 | return formatText(value[0..len], fmt, context, Errors, output); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | ||
| 231 | }, | ||
| 232 | builtin.TypeInfo.Pointer.Size.Slice => { | ||
| 233 | if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { | ||
| 234 | return formatText(value, fmt, context, Errors, output); | ||
| 235 | } | ||
| 236 | const casted_value = ([]const u8)(value); | ||
| 237 | return output(context, casted_value); | ||
| 238 | }, | ||
| 239 | builtin.TypeInfo.Pointer.Size.C => { | ||
| 240 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | ||
| 241 | }, | ||
| 242 | }, | ||
| 243 | builtin.TypeId.Array => |info| { | ||
| 244 | if (info.child == u8) { | ||
| 245 | return formatText(value, fmt, context, Errors, output); | ||
| 246 | } | ||
| 247 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); | ||
| 248 | }, | ||
| 249 | builtin.TypeId.Fn => { | ||
| 250 | return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); | ||
| 251 | }, | ||
| 252 | else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | fn formatValue( | ||
| 257 | value: var, | ||
| 258 | comptime fmt: []const u8, | ||
| 259 | context: var, | ||
| 260 | comptime Errors: type, | ||
| 261 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 262 | ) Errors!void { | ||
| 263 | if (fmt.len > 0) { | ||
| 264 | if (fmt[0] == 'B') { | ||
| 265 | comptime var width: ?usize = null; | ||
| 266 | if (fmt.len > 1) { | ||
| 267 | if (fmt[1] == 'i') { | ||
| 268 | if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); | ||
| 269 | return formatBytes(value, width, 1024, context, Errors, output); | ||
| 270 | } | ||
| 271 | width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 272 | } | ||
| 273 | return formatBytes(value, width, 1000, context, Errors, output); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | const T = @typeOf(value); | ||
| 278 | switch (@typeId(T)) { | ||
| 279 | builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), | ||
| 280 | builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), | ||
| 281 | builtin.TypeId.ComptimeInt => { | ||
| 282 | const Int = math.IntFittingRange(value, value); | ||
| 283 | return formatIntValue(Int(value), fmt, context, Errors, output); | ||
| 284 | }, | ||
| 285 | else => comptime unreachable, | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | pub fn formatIntValue( | ||
| 290 | value: var, | ||
| 291 | comptime fmt: []const u8, | ||
| 292 | context: var, | ||
| 293 | comptime Errors: type, | ||
| 294 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 295 | ) Errors!void { | ||
| 296 | comptime var radix = 10; | ||
| 297 | comptime var uppercase = false; | ||
| 298 | comptime var width = 0; | ||
| 299 | if (fmt.len > 0) { | ||
| 300 | switch (fmt[0]) { | ||
| 301 | 'c' => { | ||
| 302 | if (@typeOf(value).bit_count <= 8) { | ||
| 303 | if (fmt.len > 1) | ||
| 304 | @compileError("Unknown format character: " ++ []u8{fmt[1]}); | ||
| 305 | return formatAsciiChar(u8(value), context, Errors, output); | ||
| 306 | } | ||
| 307 | }, | ||
| 308 | 'b' => { | ||
| 309 | radix = 2; | ||
| 310 | uppercase = false; | ||
| 311 | width = 0; | ||
| 312 | }, | ||
| 313 | 'd' => { | ||
| 314 | radix = 10; | ||
| 315 | uppercase = false; | ||
| 316 | width = 0; | ||
| 317 | }, | ||
| 318 | 'x' => { | ||
| 319 | radix = 16; | ||
| 320 | uppercase = false; | ||
| 321 | width = 0; | ||
| 322 | }, | ||
| 323 | 'X' => { | ||
| 324 | radix = 16; | ||
| 325 | uppercase = true; | ||
| 326 | width = 0; | ||
| 327 | }, | ||
| 328 | else => @compileError("Unknown format character: " ++ []u8{fmt[0]}), | ||
| 329 | } | ||
| 330 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 331 | } | ||
| 332 | return formatInt(value, radix, uppercase, width, context, Errors, output); | ||
| 333 | } | ||
| 334 | |||
| 335 | fn formatFloatValue( | ||
| 336 | value: var, | ||
| 337 | comptime fmt: []const u8, | ||
| 338 | context: var, | ||
| 339 | comptime Errors: type, | ||
| 340 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 341 | ) Errors!void { | ||
| 342 | comptime var width: ?usize = null; | ||
| 343 | comptime var float_fmt = 'e'; | ||
| 344 | if (fmt.len > 0) { | ||
| 345 | float_fmt = fmt[0]; | ||
| 346 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 347 | } | ||
| 348 | |||
| 349 | switch (float_fmt) { | ||
| 350 | 'e' => try formatFloatScientific(value, width, context, Errors, output), | ||
| 351 | '.' => try formatFloatDecimal(value, width, context, Errors, output), | ||
| 352 | else => @compileError("Unknown format character: " ++ []u8{float_fmt}), | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | pub fn formatText( | ||
| 357 | bytes: []const u8, | ||
| 358 | comptime fmt: []const u8, | ||
| 359 | context: var, | ||
| 360 | comptime Errors: type, | ||
| 361 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 362 | ) Errors!void { | ||
| 363 | if (fmt.len > 0) { | ||
| 364 | if (fmt[0] == 's') { | ||
| 365 | comptime var width = 0; | ||
| 366 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 367 | return formatBuf(bytes, width, context, Errors, output); | ||
| 368 | } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { | ||
| 369 | for (bytes) |c| { | ||
| 370 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); | ||
| 371 | } | ||
| 372 | return; | ||
| 373 | } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); | ||
| 374 | } | ||
| 375 | return output(context, bytes); | ||
| 376 | } | ||
| 377 | |||
| 378 | pub fn formatAsciiChar( | ||
| 379 | c: u8, | ||
| 380 | context: var, | ||
| 381 | comptime Errors: type, | ||
| 382 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 383 | ) Errors!void { | ||
| 384 | return output(context, (*[1]u8)(&c)[0..]); | ||
| 385 | } | ||
| 386 | |||
| 387 | pub fn formatBuf( | ||
| 388 | buf: []const u8, | ||
| 389 | width: usize, | ||
| 390 | context: var, | ||
| 391 | comptime Errors: type, | ||
| 392 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 393 | ) Errors!void { | ||
| 394 | try output(context, buf); | ||
| 395 | |||
| 396 | var leftover_padding = if (width > buf.len) (width - buf.len) else return; | ||
| 397 | const pad_byte: u8 = ' '; | ||
| 398 | while (leftover_padding > 0) : (leftover_padding -= 1) { | ||
| 399 | try output(context, (*[1]u8)(&pad_byte)[0..1]); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | // Print a float in scientific notation to the specified precision. Null uses full precision. | ||
| 404 | // It should be the case that every full precision, printed value can be re-parsed back to the | ||
| 405 | // same type unambiguously. | ||
| 406 | pub fn formatFloatScientific( | ||
| 407 | value: var, | ||
| 408 | maybe_precision: ?usize, | ||
| 409 | context: var, | ||
| 410 | comptime Errors: type, | ||
| 411 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 412 | ) Errors!void { | ||
| 413 | var x = @floatCast(f64, value); | ||
| 414 | |||
| 415 | // Errol doesn't handle these special cases. | ||
| 416 | if (math.signbit(x)) { | ||
| 417 | try output(context, "-"); | ||
| 418 | x = -x; | ||
| 419 | } | ||
| 420 | |||
| 421 | if (math.isNan(x)) { | ||
| 422 | return output(context, "nan"); | ||
| 423 | } | ||
| 424 | if (math.isPositiveInf(x)) { | ||
| 425 | return output(context, "inf"); | ||
| 426 | } | ||
| 427 | if (x == 0.0) { | ||
| 428 | try output(context, "0"); | ||
| 429 | |||
| 430 | if (maybe_precision) |precision| { | ||
| 431 | if (precision != 0) { | ||
| 432 | try output(context, "."); | ||
| 433 | var i: usize = 0; | ||
| 434 | while (i < precision) : (i += 1) { | ||
| 435 | try output(context, "0"); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | } else { | ||
| 439 | try output(context, ".0"); | ||
| 440 | } | ||
| 441 | |||
| 442 | try output(context, "e+00"); | ||
| 443 | return; | ||
| 444 | } | ||
| 445 | |||
| 446 | var buffer: [32]u8 = undefined; | ||
| 447 | var float_decimal = errol.errol3(x, buffer[0..]); | ||
| 448 | |||
| 449 | if (maybe_precision) |precision| { | ||
| 450 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); | ||
| 451 | |||
| 452 | try output(context, float_decimal.digits[0..1]); | ||
| 453 | |||
| 454 | // {e0} case prints no `.` | ||
| 455 | if (precision != 0) { | ||
| 456 | try output(context, "."); | ||
| 457 | |||
| 458 | var printed: usize = 0; | ||
| 459 | if (float_decimal.digits.len > 1) { | ||
| 460 | const num_digits = math.min(float_decimal.digits.len, precision + 1); | ||
| 461 | try output(context, float_decimal.digits[1..num_digits]); | ||
| 462 | printed += num_digits - 1; | ||
| 463 | } | ||
| 464 | |||
| 465 | while (printed < precision) : (printed += 1) { | ||
| 466 | try output(context, "0"); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } else { | ||
| 470 | try output(context, float_decimal.digits[0..1]); | ||
| 471 | try output(context, "."); | ||
| 472 | if (float_decimal.digits.len > 1) { | ||
| 473 | const num_digits = if (@typeOf(value) == f32) math.min(usize(9), float_decimal.digits.len) else float_decimal.digits.len; | ||
| 474 | |||
| 475 | try output(context, float_decimal.digits[1..num_digits]); | ||
| 476 | } else { | ||
| 477 | try output(context, "0"); | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | try output(context, "e"); | ||
| 482 | const exp = float_decimal.exp - 1; | ||
| 483 | |||
| 484 | if (exp >= 0) { | ||
| 485 | try output(context, "+"); | ||
| 486 | if (exp > -10 and exp < 10) { | ||
| 487 | try output(context, "0"); | ||
| 488 | } | ||
| 489 | try formatInt(exp, 10, false, 0, context, Errors, output); | ||
| 490 | } else { | ||
| 491 | try output(context, "-"); | ||
| 492 | if (exp > -10 and exp < 10) { | ||
| 493 | try output(context, "0"); | ||
| 494 | } | ||
| 495 | try formatInt(-exp, 10, false, 0, context, Errors, output); | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. | ||
| 500 | // By default floats are printed at full precision (no rounding). | ||
| 501 | pub fn formatFloatDecimal( | ||
| 502 | value: var, | ||
| 503 | maybe_precision: ?usize, | ||
| 504 | context: var, | ||
| 505 | comptime Errors: type, | ||
| 506 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 507 | ) Errors!void { | ||
| 508 | var x = f64(value); | ||
| 509 | |||
| 510 | // Errol doesn't handle these special cases. | ||
| 511 | if (math.signbit(x)) { | ||
| 512 | try output(context, "-"); | ||
| 513 | x = -x; | ||
| 514 | } | ||
| 515 | |||
| 516 | if (math.isNan(x)) { | ||
| 517 | return output(context, "nan"); | ||
| 518 | } | ||
| 519 | if (math.isPositiveInf(x)) { | ||
| 520 | return output(context, "inf"); | ||
| 521 | } | ||
| 522 | if (x == 0.0) { | ||
| 523 | try output(context, "0"); | ||
| 524 | |||
| 525 | if (maybe_precision) |precision| { | ||
| 526 | if (precision != 0) { | ||
| 527 | try output(context, "."); | ||
| 528 | var i: usize = 0; | ||
| 529 | while (i < precision) : (i += 1) { | ||
| 530 | try output(context, "0"); | ||
| 531 | } | ||
| 532 | } else { | ||
| 533 | try output(context, ".0"); | ||
| 534 | } | ||
| 535 | } else { | ||
| 536 | try output(context, "0"); | ||
| 537 | } | ||
| 538 | |||
| 539 | return; | ||
| 540 | } | ||
| 541 | |||
| 542 | // non-special case, use errol3 | ||
| 543 | var buffer: [32]u8 = undefined; | ||
| 544 | var float_decimal = errol.errol3(x, buffer[0..]); | ||
| 545 | |||
| 546 | if (maybe_precision) |precision| { | ||
| 547 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); | ||
| 548 | |||
| 549 | // exp < 0 means the leading is always 0 as errol result is normalized. | ||
| 550 | var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; | ||
| 551 | |||
| 552 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | ||
| 553 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | ||
| 554 | |||
| 555 | if (num_digits_whole > 0) { | ||
| 556 | // We may have to zero pad, for instance 1e4 requires zero padding. | ||
| 557 | try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); | ||
| 558 | |||
| 559 | var i = num_digits_whole_no_pad; | ||
| 560 | while (i < num_digits_whole) : (i += 1) { | ||
| 561 | try output(context, "0"); | ||
| 562 | } | ||
| 563 | } else { | ||
| 564 | try output(context, "0"); | ||
| 565 | } | ||
| 566 | |||
| 567 | // {.0} special case doesn't want a trailing '.' | ||
| 568 | if (precision == 0) { | ||
| 569 | return; | ||
| 570 | } | ||
| 571 | |||
| 572 | try output(context, "."); | ||
| 573 | |||
| 574 | // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. | ||
| 575 | var printed: usize = 0; | ||
| 576 | |||
| 577 | // Zero-fill until we reach significant digits or run out of precision. | ||
| 578 | if (float_decimal.exp <= 0) { | ||
| 579 | const zero_digit_count = @intCast(usize, -float_decimal.exp); | ||
| 580 | const zeros_to_print = math.min(zero_digit_count, precision); | ||
| 581 | |||
| 582 | var i: usize = 0; | ||
| 583 | while (i < zeros_to_print) : (i += 1) { | ||
| 584 | try output(context, "0"); | ||
| 585 | printed += 1; | ||
| 586 | } | ||
| 587 | |||
| 588 | if (printed >= precision) { | ||
| 589 | return; | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | // Remaining fractional portion, zero-padding if insufficient. | ||
| 594 | assert(precision >= printed); | ||
| 595 | if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { | ||
| 596 | try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); | ||
| 597 | return; | ||
| 598 | } else { | ||
| 599 | try output(context, float_decimal.digits[num_digits_whole_no_pad..]); | ||
| 600 | printed += float_decimal.digits.len - num_digits_whole_no_pad; | ||
| 601 | |||
| 602 | while (printed < precision) : (printed += 1) { | ||
| 603 | try output(context, "0"); | ||
| 604 | } | ||
| 605 | } | ||
| 606 | } else { | ||
| 607 | // exp < 0 means the leading is always 0 as errol result is normalized. | ||
| 608 | var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; | ||
| 609 | |||
| 610 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | ||
| 611 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | ||
| 612 | |||
| 613 | if (num_digits_whole > 0) { | ||
| 614 | // We may have to zero pad, for instance 1e4 requires zero padding. | ||
| 615 | try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); | ||
| 616 | |||
| 617 | var i = num_digits_whole_no_pad; | ||
| 618 | while (i < num_digits_whole) : (i += 1) { | ||
| 619 | try output(context, "0"); | ||
| 620 | } | ||
| 621 | } else { | ||
| 622 | try output(context, "0"); | ||
| 623 | } | ||
| 624 | |||
| 625 | // Omit `.` if no fractional portion | ||
| 626 | if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { | ||
| 627 | return; | ||
| 628 | } | ||
| 629 | |||
| 630 | try output(context, "."); | ||
| 631 | |||
| 632 | // Zero-fill until we reach significant digits or run out of precision. | ||
| 633 | if (float_decimal.exp < 0) { | ||
| 634 | const zero_digit_count = @intCast(usize, -float_decimal.exp); | ||
| 635 | |||
| 636 | var i: usize = 0; | ||
| 637 | while (i < zero_digit_count) : (i += 1) { | ||
| 638 | try output(context, "0"); | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | try output(context, float_decimal.digits[num_digits_whole_no_pad..]); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | pub fn formatBytes( | ||
| 647 | value: var, | ||
| 648 | width: ?usize, | ||
| 649 | comptime radix: usize, | ||
| 650 | context: var, | ||
| 651 | comptime Errors: type, | ||
| 652 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 653 | ) Errors!void { | ||
| 654 | if (value == 0) { | ||
| 655 | return output(context, "0B"); | ||
| 656 | } | ||
| 657 | |||
| 658 | const mags_si = " kMGTPEZY"; | ||
| 659 | const mags_iec = " KMGTPEZY"; | ||
| 660 | const magnitude = switch (radix) { | ||
| 661 | 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), | ||
| 662 | 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), | ||
| 663 | else => unreachable, | ||
| 664 | }; | ||
| 665 | const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); | ||
| 666 | const suffix = switch (radix) { | ||
| 667 | 1000 => mags_si[magnitude], | ||
| 668 | 1024 => mags_iec[magnitude], | ||
| 669 | else => unreachable, | ||
| 670 | }; | ||
| 671 | |||
| 672 | try formatFloatDecimal(new_value, width, context, Errors, output); | ||
| 673 | |||
| 674 | if (suffix == ' ') { | ||
| 675 | return output(context, "B"); | ||
| 676 | } | ||
| 677 | |||
| 678 | const buf = switch (radix) { | ||
| 679 | 1000 => []u8{ suffix, 'B' }, | ||
| 680 | 1024 => []u8{ suffix, 'i', 'B' }, | ||
| 681 | else => unreachable, | ||
| 682 | }; | ||
| 683 | return output(context, buf); | ||
| 684 | } | ||
| 685 | |||
| 686 | pub fn formatInt( | ||
| 687 | value: var, | ||
| 688 | base: u8, | ||
| 689 | uppercase: bool, | ||
| 690 | width: usize, | ||
| 691 | context: var, | ||
| 692 | comptime Errors: type, | ||
| 693 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 694 | ) Errors!void { | ||
| 695 | if (@typeOf(value).is_signed) { | ||
| 696 | return formatIntSigned(value, base, uppercase, width, context, Errors, output); | ||
| 697 | } else { | ||
| 698 | return formatIntUnsigned(value, base, uppercase, width, context, Errors, output); | ||
| 699 | } | ||
| 700 | } | ||
| 701 | |||
| 702 | fn formatIntSigned( | ||
| 703 | value: var, | ||
| 704 | base: u8, | ||
| 705 | uppercase: bool, | ||
| 706 | width: usize, | ||
| 707 | context: var, | ||
| 708 | comptime Errors: type, | ||
| 709 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 710 | ) Errors!void { | ||
| 711 | const uint = @IntType(false, @typeOf(value).bit_count); | ||
| 712 | if (value < 0) { | ||
| 713 | const minus_sign: u8 = '-'; | ||
| 714 | try output(context, (*[1]u8)(&minus_sign)[0..]); | ||
| 715 | const new_value = @intCast(uint, -(value + 1)) + 1; | ||
| 716 | const new_width = if (width == 0) 0 else (width - 1); | ||
| 717 | return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); | ||
| 718 | } else if (width == 0) { | ||
| 719 | return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); | ||
| 720 | } else { | ||
| 721 | const plus_sign: u8 = '+'; | ||
| 722 | try output(context, (*[1]u8)(&plus_sign)[0..]); | ||
| 723 | const new_value = @intCast(uint, value); | ||
| 724 | const new_width = if (width == 0) 0 else (width - 1); | ||
| 725 | return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | fn formatIntUnsigned( | ||
| 730 | value: var, | ||
| 731 | base: u8, | ||
| 732 | uppercase: bool, | ||
| 733 | width: usize, | ||
| 734 | context: var, | ||
| 735 | comptime Errors: type, | ||
| 736 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 737 | ) Errors!void { | ||
| 738 | // max_int_digits accounts for the minus sign. when printing an unsigned | ||
| 739 | // number we don't need to do that. | ||
| 740 | var buf: [max_int_digits - 1]u8 = undefined; | ||
| 741 | const min_int_bits = comptime math.max(@typeOf(value).bit_count, @typeOf(base).bit_count); | ||
| 742 | const MinInt = @IntType(@typeOf(value).is_signed, min_int_bits); | ||
| 743 | var a: MinInt = value; | ||
| 744 | var index: usize = buf.len; | ||
| 745 | |||
| 746 | while (true) { | ||
| 747 | const digit = a % base; | ||
| 748 | index -= 1; | ||
| 749 | buf[index] = digitToChar(@intCast(u8, digit), uppercase); | ||
| 750 | a /= base; | ||
| 751 | if (a == 0) break; | ||
| 752 | } | ||
| 753 | |||
| 754 | const digits_buf = buf[index..]; | ||
| 755 | const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; | ||
| 756 | |||
| 757 | if (padding > index) { | ||
| 758 | const zero_byte: u8 = '0'; | ||
| 759 | var leftover_padding = padding - index; | ||
| 760 | while (true) { | ||
| 761 | try output(context, (*[1]u8)(&zero_byte)[0..]); | ||
| 762 | leftover_padding -= 1; | ||
| 763 | if (leftover_padding == 0) break; | ||
| 764 | } | ||
| 765 | mem.set(u8, buf[0..index], '0'); | ||
| 766 | return output(context, buf); | ||
| 767 | } else { | ||
| 768 | const padded_buf = buf[index - padding ..]; | ||
| 769 | mem.set(u8, padded_buf[0..padding], '0'); | ||
| 770 | return output(context, padded_buf); | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { | ||
| 775 | var context = FormatIntBuf{ | ||
| 776 | .out_buf = out_buf, | ||
| 777 | .index = 0, | ||
| 778 | }; | ||
| 779 | formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable; | ||
| 780 | return context.index; | ||
| 781 | } | ||
| 782 | const FormatIntBuf = struct { | ||
| 783 | out_buf: []u8, | ||
| 784 | index: usize, | ||
| 785 | }; | ||
| 786 | fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { | ||
| 787 | mem.copy(u8, context.out_buf[context.index..], bytes); | ||
| 788 | context.index += bytes.len; | ||
| 789 | } | ||
| 790 | |||
| 791 | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { | ||
| 792 | if (!T.is_signed) return parseUnsigned(T, buf, radix); | ||
| 793 | if (buf.len == 0) return T(0); | ||
| 794 | if (buf[0] == '-') { | ||
| 795 | return math.negate(try parseUnsigned(T, buf[1..], radix)); | ||
| 796 | } else if (buf[0] == '+') { | ||
| 797 | return parseUnsigned(T, buf[1..], radix); | ||
| 798 | } else { | ||
| 799 | return parseUnsigned(T, buf, radix); | ||
| 800 | } | ||
| 801 | } | ||
| 802 | |||
| 803 | test "fmt.parseInt" { | ||
| 804 | testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); | ||
| 805 | testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); | ||
| 806 | testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); | ||
| 807 | testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); | ||
| 808 | testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); | ||
| 809 | testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); | ||
| 810 | testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); | ||
| 811 | } | ||
| 812 | |||
| 813 | const ParseUnsignedError = error{ | ||
| 814 | /// The result cannot fit in the type specified | ||
| 815 | Overflow, | ||
| 816 | |||
| 817 | /// The input had a byte that was not a digit | ||
| 818 | InvalidCharacter, | ||
| 819 | }; | ||
| 820 | |||
| 821 | pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { | ||
| 822 | var x: T = 0; | ||
| 823 | |||
| 824 | for (buf) |c| { | ||
| 825 | const digit = try charToDigit(c, radix); | ||
| 826 | |||
| 827 | if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); | ||
| 828 | x = try math.add(T, x, try math.cast(T, digit)); | ||
| 829 | } | ||
| 830 | |||
| 831 | return x; | ||
| 832 | } | ||
| 833 | |||
| 834 | test "fmt.parseUnsigned" { | ||
| 835 | testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); | ||
| 836 | testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); | ||
| 837 | testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); | ||
| 838 | |||
| 839 | testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); | ||
| 840 | testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); | ||
| 841 | |||
| 842 | testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); | ||
| 843 | |||
| 844 | testing.expect((try parseUnsigned(u7, "1", 10)) == 1); | ||
| 845 | testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); | ||
| 846 | |||
| 847 | testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); | ||
| 848 | testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); | ||
| 849 | |||
| 850 | testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); | ||
| 851 | |||
| 852 | // these numbers should fit even though the radix itself doesn't fit in the destination type | ||
| 853 | testing.expect((try parseUnsigned(u1, "0", 10)) == 0); | ||
| 854 | testing.expect((try parseUnsigned(u1, "1", 10)) == 1); | ||
| 855 | testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); | ||
| 856 | testing.expect((try parseUnsigned(u1, "001", 16)) == 1); | ||
| 857 | testing.expect((try parseUnsigned(u2, "3", 16)) == 3); | ||
| 858 | testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); | ||
| 859 | } | ||
| 860 | |||
| 861 | pub const parseFloat = @import("fmt/parse_float.zig").parseFloat; | ||
| 862 | |||
| 863 | test "fmt.parseFloat" { | ||
| 864 | _ = @import("fmt/parse_float.zig"); | ||
| 865 | } | ||
| 866 | |||
| 867 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { | ||
| 868 | const value = switch (c) { | ||
| 869 | '0'...'9' => c - '0', | ||
| 870 | 'A'...'Z' => c - 'A' + 10, | ||
| 871 | 'a'...'z' => c - 'a' + 10, | ||
| 872 | else => return error.InvalidCharacter, | ||
| 873 | }; | ||
| 874 | |||
| 875 | if (value >= radix) return error.InvalidCharacter; | ||
| 876 | |||
| 877 | return value; | ||
| 878 | } | ||
| 879 | |||
| 880 | fn digitToChar(digit: u8, uppercase: bool) u8 { | ||
| 881 | return switch (digit) { | ||
| 882 | 0...9 => digit + '0', | ||
| 883 | 10...35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), | ||
| 884 | else => unreachable, | ||
| 885 | }; | ||
| 886 | } | ||
| 887 | |||
| 888 | const BufPrintContext = struct { | ||
| 889 | remaining: []u8, | ||
| 890 | }; | ||
| 891 | |||
| 892 | fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { | ||
| 893 | if (context.remaining.len < bytes.len) return error.BufferTooSmall; | ||
| 894 | mem.copy(u8, context.remaining, bytes); | ||
| 895 | context.remaining = context.remaining[bytes.len..]; | ||
| 896 | } | ||
| 897 | |||
| 898 | pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { | ||
| 899 | var context = BufPrintContext{ .remaining = buf }; | ||
| 900 | try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); | ||
| 901 | return buf[0 .. buf.len - context.remaining.len]; | ||
| 902 | } | ||
| 903 | |||
| 904 | pub const AllocPrintError = error{OutOfMemory}; | ||
| 905 | |||
| 906 | pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { | ||
| 907 | var size: usize = 0; | ||
| 908 | format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; | ||
| 909 | const buf = try allocator.alloc(u8, size); | ||
| 910 | return bufPrint(buf, fmt, args) catch |err| switch (err) { | ||
| 911 | error.BufferTooSmall => unreachable, // we just counted the size above | ||
| 912 | }; | ||
| 913 | } | ||
| 914 | |||
| 915 | fn countSize(size: *usize, bytes: []const u8) (error{}!void) { | ||
| 916 | size.* += bytes.len; | ||
| 917 | } | ||
| 918 | |||
| 919 | test "buf print int" { | ||
| 920 | var buffer: [max_int_digits]u8 = undefined; | ||
| 921 | const buf = buffer[0..]; | ||
| 922 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); | ||
| 923 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); | ||
| 924 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); | ||
| 925 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); | ||
| 926 | |||
| 927 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); | ||
| 928 | |||
| 929 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); | ||
| 930 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); | ||
| 931 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); | ||
| 932 | |||
| 933 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); | ||
| 934 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); | ||
| 935 | } | ||
| 936 | |||
| 937 | fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { | ||
| 938 | return buf[0..formatIntBuf(buf, value, base, uppercase, width)]; | ||
| 939 | } | ||
| 940 | |||
| 941 | test "parse u64 digit too big" { | ||
| 942 | _ = parseUnsigned(u64, "123a", 10) catch |err| { | ||
| 943 | if (err == error.InvalidCharacter) return; | ||
| 944 | unreachable; | ||
| 945 | }; | ||
| 946 | unreachable; | ||
| 947 | } | ||
| 948 | |||
| 949 | test "parse unsigned comptime" { | ||
| 950 | comptime { | ||
| 951 | testing.expect((try parseUnsigned(usize, "2", 10)) == 2); | ||
| 952 | } | ||
| 953 | } | ||
| 954 | |||
| 955 | test "fmt.format" { | ||
| 956 | { | ||
| 957 | const value: ?i32 = 1234; | ||
| 958 | try testFmt("optional: 1234\n", "optional: {}\n", value); | ||
| 959 | } | ||
| 960 | { | ||
| 961 | const value: ?i32 = null; | ||
| 962 | try testFmt("optional: null\n", "optional: {}\n", value); | ||
| 963 | } | ||
| 964 | { | ||
| 965 | const value: anyerror!i32 = 1234; | ||
| 966 | try testFmt("error union: 1234\n", "error union: {}\n", value); | ||
| 967 | } | ||
| 968 | { | ||
| 969 | const value: anyerror!i32 = error.InvalidChar; | ||
| 970 | try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); | ||
| 971 | } | ||
| 972 | { | ||
| 973 | const value: u3 = 0b101; | ||
| 974 | try testFmt("u3: 5\n", "u3: {}\n", value); | ||
| 975 | } | ||
| 976 | { | ||
| 977 | const value: u8 = 'a'; | ||
| 978 | try testFmt("u8: a\n", "u8: {c}\n", value); | ||
| 979 | } | ||
| 980 | { | ||
| 981 | const value: u8 = 0b1100; | ||
| 982 | try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); | ||
| 983 | } | ||
| 984 | { | ||
| 985 | var buf1: [32]u8 = undefined; | ||
| 986 | var context = BufPrintContext{ .remaining = buf1[0..] }; | ||
| 987 | try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite); | ||
| 988 | var res = buf1[0 .. buf1.len - context.remaining.len]; | ||
| 989 | testing.expect(mem.eql(u8, res, "1234")); | ||
| 990 | |||
| 991 | context = BufPrintContext{ .remaining = buf1[0..] }; | ||
| 992 | try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite); | ||
| 993 | res = buf1[0 .. buf1.len - context.remaining.len]; | ||
| 994 | testing.expect(mem.eql(u8, res, "a")); | ||
| 995 | |||
| 996 | context = BufPrintContext{ .remaining = buf1[0..] }; | ||
| 997 | try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite); | ||
| 998 | res = buf1[0 .. buf1.len - context.remaining.len]; | ||
| 999 | testing.expect(mem.eql(u8, res, "1100")); | ||
| 1000 | } | ||
| 1001 | { | ||
| 1002 | const value: [3]u8 = "abc"; | ||
| 1003 | try testFmt("array: abc\n", "array: {}\n", value); | ||
| 1004 | try testFmt("array: abc\n", "array: {}\n", &value); | ||
| 1005 | |||
| 1006 | var buf: [100]u8 = undefined; | ||
| 1007 | try testFmt( | ||
| 1008 | try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), | ||
| 1009 | "array: {*}\n", | ||
| 1010 | &value, | ||
| 1011 | ); | ||
| 1012 | } | ||
| 1013 | { | ||
| 1014 | const value: []const u8 = "abc"; | ||
| 1015 | try testFmt("slice: abc\n", "slice: {}\n", value); | ||
| 1016 | } | ||
| 1017 | { | ||
| 1018 | const value = @intToPtr(*i32, 0xdeadbeef); | ||
| 1019 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); | ||
| 1020 | try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); | ||
| 1021 | } | ||
| 1022 | { | ||
| 1023 | const value = @intToPtr(fn () void, 0xdeadbeef); | ||
| 1024 | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); | ||
| 1025 | } | ||
| 1026 | { | ||
| 1027 | const value = @intToPtr(fn () void, 0xdeadbeef); | ||
| 1028 | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); | ||
| 1029 | } | ||
| 1030 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); | ||
| 1031 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); | ||
| 1032 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); | ||
| 1033 | try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); | ||
| 1034 | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); | ||
| 1035 | try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); | ||
| 1036 | { | ||
| 1037 | const Struct = struct { | ||
| 1038 | field: u8, | ||
| 1039 | }; | ||
| 1040 | const value = Struct{ .field = 42 }; | ||
| 1041 | try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value); | ||
| 1042 | try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value); | ||
| 1043 | } | ||
| 1044 | { | ||
| 1045 | const Struct = struct { | ||
| 1046 | a: u0, | ||
| 1047 | b: u1, | ||
| 1048 | }; | ||
| 1049 | const value = Struct{ .a = 0, .b = 1 }; | ||
| 1050 | try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); | ||
| 1051 | } | ||
| 1052 | { | ||
| 1053 | const Enum = enum { | ||
| 1054 | One, | ||
| 1055 | Two, | ||
| 1056 | }; | ||
| 1057 | const value = Enum.Two; | ||
| 1058 | try testFmt("enum: Enum.Two\n", "enum: {}\n", value); | ||
| 1059 | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); | ||
| 1060 | } | ||
| 1061 | { | ||
| 1062 | var buf1: [32]u8 = undefined; | ||
| 1063 | const value: f32 = 1.34; | ||
| 1064 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | ||
| 1065 | testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n")); | ||
| 1066 | } | ||
| 1067 | { | ||
| 1068 | var buf1: [32]u8 = undefined; | ||
| 1069 | const value: f32 = 12.34; | ||
| 1070 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | ||
| 1071 | testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n")); | ||
| 1072 | } | ||
| 1073 | { | ||
| 1074 | var buf1: [32]u8 = undefined; | ||
| 1075 | const value: f64 = -12.34e10; | ||
| 1076 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | ||
| 1077 | testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n")); | ||
| 1078 | } | ||
| 1079 | { | ||
| 1080 | // This fails on release due to a minor rounding difference. | ||
| 1081 | // --release-fast outputs 9.999960000000001e-40 vs. the expected. | ||
| 1082 | // TODO fix this, it should be the same in Debug and ReleaseFast | ||
| 1083 | if (builtin.mode == builtin.Mode.Debug) { | ||
| 1084 | var buf1: [32]u8 = undefined; | ||
| 1085 | const value: f64 = 9.999960e-40; | ||
| 1086 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | ||
| 1087 | testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | { | ||
| 1091 | var buf1: [32]u8 = undefined; | ||
| 1092 | const value: f64 = 1.409706e-42; | ||
| 1093 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1094 | testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); | ||
| 1095 | } | ||
| 1096 | { | ||
| 1097 | var buf1: [32]u8 = undefined; | ||
| 1098 | const value: f64 = @bitCast(f32, u32(814313563)); | ||
| 1099 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1100 | testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n")); | ||
| 1101 | } | ||
| 1102 | { | ||
| 1103 | var buf1: [32]u8 = undefined; | ||
| 1104 | const value: f64 = @bitCast(f32, u32(1006632960)); | ||
| 1105 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1106 | testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n")); | ||
| 1107 | } | ||
| 1108 | { | ||
| 1109 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. | ||
| 1110 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. | ||
| 1111 | var buf1: [32]u8 = undefined; | ||
| 1112 | const value: f64 = @bitCast(f32, u32(1203982400)); | ||
| 1113 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1114 | testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); | ||
| 1115 | } | ||
| 1116 | { | ||
| 1117 | var buf1: [32]u8 = undefined; | ||
| 1118 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | ||
| 1119 | testing.expect(mem.eql(u8, result, "f64: nan\n")); | ||
| 1120 | } | ||
| 1121 | if (builtin.arch != builtin.Arch.arm) { | ||
| 1122 | // negative nan is not defined by IEE 754, | ||
| 1123 | // and ARM thus normalizes it to positive nan | ||
| 1124 | var buf1: [32]u8 = undefined; | ||
| 1125 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); | ||
| 1126 | testing.expect(mem.eql(u8, result, "f64: -nan\n")); | ||
| 1127 | } | ||
| 1128 | { | ||
| 1129 | var buf1: [32]u8 = undefined; | ||
| 1130 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); | ||
| 1131 | testing.expect(mem.eql(u8, result, "f64: inf\n")); | ||
| 1132 | } | ||
| 1133 | { | ||
| 1134 | var buf1: [32]u8 = undefined; | ||
| 1135 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); | ||
| 1136 | testing.expect(mem.eql(u8, result, "f64: -inf\n")); | ||
| 1137 | } | ||
| 1138 | { | ||
| 1139 | var buf1: [64]u8 = undefined; | ||
| 1140 | const value: f64 = 1.52314e+29; | ||
| 1141 | const result = try bufPrint(buf1[0..], "f64: {.}\n", value); | ||
| 1142 | testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); | ||
| 1143 | } | ||
| 1144 | { | ||
| 1145 | var buf1: [32]u8 = undefined; | ||
| 1146 | const value: f32 = 1.1234; | ||
| 1147 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | ||
| 1148 | testing.expect(mem.eql(u8, result, "f32: 1.1\n")); | ||
| 1149 | } | ||
| 1150 | { | ||
| 1151 | var buf1: [32]u8 = undefined; | ||
| 1152 | const value: f32 = 1234.567; | ||
| 1153 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); | ||
| 1154 | testing.expect(mem.eql(u8, result, "f32: 1234.57\n")); | ||
| 1155 | } | ||
| 1156 | { | ||
| 1157 | var buf1: [32]u8 = undefined; | ||
| 1158 | const value: f32 = -11.1234; | ||
| 1159 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); | ||
| 1160 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). | ||
| 1161 | // -11.12339... is rounded back up to -11.1234 | ||
| 1162 | testing.expect(mem.eql(u8, result, "f32: -11.1234\n")); | ||
| 1163 | } | ||
| 1164 | { | ||
| 1165 | var buf1: [32]u8 = undefined; | ||
| 1166 | const value: f32 = 91.12345; | ||
| 1167 | const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); | ||
| 1168 | testing.expect(mem.eql(u8, result, "f32: 91.12345\n")); | ||
| 1169 | } | ||
| 1170 | { | ||
| 1171 | var buf1: [32]u8 = undefined; | ||
| 1172 | const value: f64 = 91.12345678901235; | ||
| 1173 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | ||
| 1174 | testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n")); | ||
| 1175 | } | ||
| 1176 | { | ||
| 1177 | var buf1: [32]u8 = undefined; | ||
| 1178 | const value: f64 = 0.0; | ||
| 1179 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1180 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | ||
| 1181 | } | ||
| 1182 | { | ||
| 1183 | var buf1: [32]u8 = undefined; | ||
| 1184 | const value: f64 = 5.700; | ||
| 1185 | const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); | ||
| 1186 | testing.expect(mem.eql(u8, result, "f64: 6\n")); | ||
| 1187 | } | ||
| 1188 | { | ||
| 1189 | var buf1: [32]u8 = undefined; | ||
| 1190 | const value: f64 = 9.999; | ||
| 1191 | const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); | ||
| 1192 | testing.expect(mem.eql(u8, result, "f64: 10.0\n")); | ||
| 1193 | } | ||
| 1194 | { | ||
| 1195 | var buf1: [32]u8 = undefined; | ||
| 1196 | const value: f64 = 1.0; | ||
| 1197 | const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); | ||
| 1198 | testing.expect(mem.eql(u8, result, "f64: 1.000\n")); | ||
| 1199 | } | ||
| 1200 | { | ||
| 1201 | var buf1: [32]u8 = undefined; | ||
| 1202 | const value: f64 = 0.0003; | ||
| 1203 | const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); | ||
| 1204 | testing.expect(mem.eql(u8, result, "f64: 0.00030000\n")); | ||
| 1205 | } | ||
| 1206 | { | ||
| 1207 | var buf1: [32]u8 = undefined; | ||
| 1208 | const value: f64 = 1.40130e-45; | ||
| 1209 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1210 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | ||
| 1211 | } | ||
| 1212 | { | ||
| 1213 | var buf1: [32]u8 = undefined; | ||
| 1214 | const value: f64 = 9.999960e-40; | ||
| 1215 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1216 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | ||
| 1217 | } | ||
| 1218 | // libc checks | ||
| 1219 | { | ||
| 1220 | var buf1: [32]u8 = undefined; | ||
| 1221 | const value: f64 = f64(@bitCast(f32, u32(916964781))); | ||
| 1222 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1223 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | ||
| 1224 | } | ||
| 1225 | { | ||
| 1226 | var buf1: [32]u8 = undefined; | ||
| 1227 | const value: f64 = f64(@bitCast(f32, u32(925353389))); | ||
| 1228 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1229 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | ||
| 1230 | } | ||
| 1231 | { | ||
| 1232 | var buf1: [32]u8 = undefined; | ||
| 1233 | const value: f64 = f64(@bitCast(f32, u32(1036831278))); | ||
| 1234 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1235 | testing.expect(mem.eql(u8, result, "f64: 0.10000\n")); | ||
| 1236 | } | ||
| 1237 | { | ||
| 1238 | var buf1: [32]u8 = undefined; | ||
| 1239 | const value: f64 = f64(@bitCast(f32, u32(1065353133))); | ||
| 1240 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1241 | testing.expect(mem.eql(u8, result, "f64: 1.00000\n")); | ||
| 1242 | } | ||
| 1243 | { | ||
| 1244 | var buf1: [32]u8 = undefined; | ||
| 1245 | const value: f64 = f64(@bitCast(f32, u32(1092616192))); | ||
| 1246 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1247 | testing.expect(mem.eql(u8, result, "f64: 10.00000\n")); | ||
| 1248 | } | ||
| 1249 | // libc differences | ||
| 1250 | { | ||
| 1251 | var buf1: [32]u8 = undefined; | ||
| 1252 | // This is 0.015625 exactly according to gdb. We thus round down, | ||
| 1253 | // however glibc rounds up for some reason. This occurs for all | ||
| 1254 | // floats of the form x.yyyy25 on a precision point. | ||
| 1255 | const value: f64 = f64(@bitCast(f32, u32(1015021568))); | ||
| 1256 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1257 | testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); | ||
| 1258 | } | ||
| 1259 | // std-windows-x86_64-Debug-bare test case fails | ||
| 1260 | { | ||
| 1261 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 | ||
| 1262 | // also rounds to 630 so I'm inclined to believe libc is not | ||
| 1263 | // optimal here. | ||
| 1264 | var buf1: [32]u8 = undefined; | ||
| 1265 | const value: f64 = f64(@bitCast(f32, u32(1518338049))); | ||
| 1266 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1267 | testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); | ||
| 1268 | } | ||
| 1269 | //custom type format | ||
| 1270 | { | ||
| 1271 | const Vec2 = struct { | ||
| 1272 | const SelfType = @This(); | ||
| 1273 | x: f32, | ||
| 1274 | y: f32, | ||
| 1275 | |||
| 1276 | pub fn format( | ||
| 1277 | self: SelfType, | ||
| 1278 | comptime fmt: []const u8, | ||
| 1279 | context: var, | ||
| 1280 | comptime Errors: type, | ||
| 1281 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 1282 | ) Errors!void { | ||
| 1283 | switch (fmt.len) { | ||
| 1284 | 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | ||
| 1285 | 1 => switch (fmt[0]) { | ||
| 1286 | //point format | ||
| 1287 | 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | ||
| 1288 | //dimension format | ||
| 1289 | 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), | ||
| 1290 | else => unreachable, | ||
| 1291 | }, | ||
| 1292 | else => unreachable, | ||
| 1293 | } | ||
| 1294 | } | ||
| 1295 | }; | ||
| 1296 | |||
| 1297 | var buf1: [32]u8 = undefined; | ||
| 1298 | var value = Vec2{ | ||
| 1299 | .x = 10.2, | ||
| 1300 | .y = 2.22, | ||
| 1301 | }; | ||
| 1302 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); | ||
| 1303 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); | ||
| 1304 | |||
| 1305 | // same thing but not passing a pointer | ||
| 1306 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); | ||
| 1307 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); | ||
| 1308 | } | ||
| 1309 | //struct format | ||
| 1310 | { | ||
| 1311 | const S = struct { | ||
| 1312 | a: u32, | ||
| 1313 | b: anyerror, | ||
| 1314 | }; | ||
| 1315 | |||
| 1316 | const inst = S{ | ||
| 1317 | .a = 456, | ||
| 1318 | .b = error.Unused, | ||
| 1319 | }; | ||
| 1320 | |||
| 1321 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); | ||
| 1322 | } | ||
| 1323 | //union format | ||
| 1324 | { | ||
| 1325 | const TU = union(enum) { | ||
| 1326 | float: f32, | ||
| 1327 | int: u32, | ||
| 1328 | }; | ||
| 1329 | |||
| 1330 | const UU = union { | ||
| 1331 | float: f32, | ||
| 1332 | int: u32, | ||
| 1333 | }; | ||
| 1334 | |||
| 1335 | const EU = extern union { | ||
| 1336 | float: f32, | ||
| 1337 | int: u32, | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | const tu_inst = TU{ .int = 123 }; | ||
| 1341 | const uu_inst = UU{ .int = 456 }; | ||
| 1342 | const eu_inst = EU{ .float = 321.123 }; | ||
| 1343 | |||
| 1344 | try testFmt("TU{ .int = 123 }", "{}", tu_inst); | ||
| 1345 | |||
| 1346 | var buf: [100]u8 = undefined; | ||
| 1347 | const uu_result = try bufPrint(buf[0..], "{}", uu_inst); | ||
| 1348 | testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); | ||
| 1349 | |||
| 1350 | const eu_result = try bufPrint(buf[0..], "{}", eu_inst); | ||
| 1351 | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); | ||
| 1352 | } | ||
| 1353 | //enum format | ||
| 1354 | { | ||
| 1355 | const E = enum { | ||
| 1356 | One, | ||
| 1357 | Two, | ||
| 1358 | Three, | ||
| 1359 | }; | ||
| 1360 | |||
| 1361 | const inst = E.Two; | ||
| 1362 | |||
| 1363 | try testFmt("E.Two", "{}", inst); | ||
| 1364 | } | ||
| 1365 | //print bytes as hex | ||
| 1366 | { | ||
| 1367 | const some_bytes = "\xCA\xFE\xBA\xBE"; | ||
| 1368 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); | ||
| 1369 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); | ||
| 1370 | //Test Slices | ||
| 1371 | try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); | ||
| 1372 | try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); | ||
| 1373 | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; | ||
| 1374 | try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); | ||
| 1375 | } | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { | ||
| 1379 | var buf: [100]u8 = undefined; | ||
| 1380 | const result = try bufPrint(buf[0..], template, args); | ||
| 1381 | if (mem.eql(u8, result, expected)) return; | ||
| 1382 | |||
| 1383 | std.debug.warn("\n====== expected this output: =========\n"); | ||
| 1384 | std.debug.warn("{}", expected); | ||
| 1385 | std.debug.warn("\n======== instead found this: =========\n"); | ||
| 1386 | std.debug.warn("{}", result); | ||
| 1387 | std.debug.warn("\n======================================\n"); | ||
| 1388 | return error.TestFailed; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | pub fn trim(buf: []const u8) []const u8 { | ||
| 1392 | var start: usize = 0; | ||
| 1393 | while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} | ||
| 1394 | |||
| 1395 | var end: usize = buf.len; | ||
| 1396 | while (true) { | ||
| 1397 | if (end > start) { | ||
| 1398 | const new_end = end - 1; | ||
| 1399 | if (isWhiteSpace(buf[new_end])) { | ||
| 1400 | end = new_end; | ||
| 1401 | continue; | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | break; | ||
| 1405 | } | ||
| 1406 | return buf[start..end]; | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | test "fmt.trim" { | ||
| 1410 | testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); | ||
| 1411 | testing.expect(mem.eql(u8, "", trim(" "))); | ||
| 1412 | testing.expect(mem.eql(u8, "", trim(""))); | ||
| 1413 | testing.expect(mem.eql(u8, "abc", trim(" abc"))); | ||
| 1414 | testing.expect(mem.eql(u8, "abc", trim("abc "))); | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | pub fn isWhiteSpace(byte: u8) bool { | ||
| 1418 | return switch (byte) { | ||
| 1419 | ' ', '\t', '\n', '\r' => true, | ||
| 1420 | else => false, | ||
| 1421 | }; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | pub fn hexToBytes(out: []u8, input: []const u8) !void { | ||
| 1425 | if (out.len * 2 < input.len) | ||
| 1426 | return error.InvalidLength; | ||
| 1427 | |||
| 1428 | var in_i: usize = 0; | ||
| 1429 | while (in_i != input.len) : (in_i += 2) { | ||
| 1430 | const hi = try charToDigit(input[in_i], 16); | ||
| 1431 | const lo = try charToDigit(input[in_i + 1], 16); | ||
| 1432 | out[in_i / 2] = (hi << 4) | lo; | ||
| 1433 | } | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | test "fmt.hexToBytes" { | ||
| 1437 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; | ||
| 1438 | var pb: [32]u8 = undefined; | ||
| 1439 | try hexToBytes(pb[0..], test_hex_str); | ||
| 1440 | try testFmt(test_hex_str, "{X}", pb); | ||
| 1441 | } | ||
std/fmt/errol.zig created+704| ... | @@ -0,0 +1,704 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const enum3 = @import("errol/enum3.zig").enum3; | ||
| 3 | const enum3_data = @import("errol/enum3.zig").enum3_data; | ||
| 4 | const lookup_table = @import("errol/lookup.zig").lookup_table; | ||
| 5 | const HP = @import("errol/lookup.zig").HP; | ||
| 6 | const math = std.math; | ||
| 7 | const mem = std.mem; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | |||
| 10 | pub const FloatDecimal = struct { | ||
| 11 | digits: []u8, | ||
| 12 | exp: i32, | ||
| 13 | }; | ||
| 14 | |||
| 15 | pub const RoundMode = enum { | ||
| 16 | // Round only the fractional portion (e.g. 1234.23 has precision 2) | ||
| 17 | Decimal, | ||
| 18 | // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5) | ||
| 19 | Scientific, | ||
| 20 | }; | ||
| 21 | |||
| 22 | /// Round a FloatDecimal as returned by errol3 to the specified fractional precision. | ||
| 23 | /// All digits after the specified precision should be considered invalid. | ||
| 24 | pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: RoundMode) void { | ||
| 25 | // The round digit refers to the index which we should look at to determine | ||
| 26 | // whether we need to round to match the specified precision. | ||
| 27 | var round_digit: usize = 0; | ||
| 28 | |||
| 29 | switch (mode) { | ||
| 30 | RoundMode.Decimal => { | ||
| 31 | if (float_decimal.exp >= 0) { | ||
| 32 | round_digit = precision + @intCast(usize, float_decimal.exp); | ||
| 33 | } else { | ||
| 34 | // if a small negative exp, then adjust we need to offset by the number | ||
| 35 | // of leading zeros that will occur. | ||
| 36 | const min_exp_required = @intCast(usize, -float_decimal.exp); | ||
| 37 | if (precision > min_exp_required) { | ||
| 38 | round_digit = precision - min_exp_required; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | }, | ||
| 42 | RoundMode.Scientific => { | ||
| 43 | round_digit = 1 + precision; | ||
| 44 | }, | ||
| 45 | } | ||
| 46 | |||
| 47 | // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05 | ||
| 48 | // first, and then to 0.1 in the case of a {.1} single precision. | ||
| 49 | |||
| 50 | // Find the digit which will signify the round point and start rounding backwards. | ||
| 51 | if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) { | ||
| 52 | assert(round_digit >= 0); | ||
| 53 | |||
| 54 | var i = round_digit; | ||
| 55 | while (true) { | ||
| 56 | if (i == 0) { | ||
| 57 | // Rounded all the way past the start. This was of the form 9.999... | ||
| 58 | // Slot the new digit in place and increase the exponent. | ||
| 59 | float_decimal.exp += 1; | ||
| 60 | |||
| 61 | // Re-size the buffer to use the reserved leading byte. | ||
| 62 | const one_before = @intToPtr([*]u8, @ptrToInt(&float_decimal.digits[0]) - 1); | ||
| 63 | float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; | ||
| 64 | float_decimal.digits[0] = '1'; | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | i -= 1; | ||
| 69 | |||
| 70 | const new_value = (float_decimal.digits[i] - '0' + 1) % 10; | ||
| 71 | float_decimal.digits[i] = new_value + '0'; | ||
| 72 | |||
| 73 | // must continue rounding until non-9 | ||
| 74 | if (new_value != 0) { | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Corrected Errol3 double to ASCII conversion. | ||
| 82 | pub fn errol3(value: f64, buffer: []u8) FloatDecimal { | ||
| 83 | const bits = @bitCast(u64, value); | ||
| 84 | const i = tableLowerBound(bits); | ||
| 85 | if (i < enum3.len and enum3[i] == bits) { | ||
| 86 | const data = enum3_data[i]; | ||
| 87 | const digits = buffer[1 .. data.str.len + 1]; | ||
| 88 | mem.copy(u8, digits, data.str); | ||
| 89 | return FloatDecimal{ | ||
| 90 | .digits = digits, | ||
| 91 | .exp = data.exp, | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | return errol3u(value, buffer); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Uncorrected Errol3 double to ASCII conversion. | ||
| 99 | fn errol3u(val: f64, buffer: []u8) FloatDecimal { | ||
| 100 | // check if in integer or fixed range | ||
| 101 | if (val > 9.007199254740992e15 and val < 3.40282366920938e+38) { | ||
| 102 | return errolInt(val, buffer); | ||
| 103 | } else if (val >= 16.0 and val < 9.007199254740992e15) { | ||
| 104 | return errolFixed(val, buffer); | ||
| 105 | } | ||
| 106 | |||
| 107 | // normalize the midpoint | ||
| 108 | |||
| 109 | const e = math.frexp(val).exponent; | ||
| 110 | var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); | ||
| 111 | if (exp < 20) { | ||
| 112 | exp = 20; | ||
| 113 | } else if (@intCast(usize, exp) >= lookup_table.len) { | ||
| 114 | exp = @intCast(i16, lookup_table.len - 1); | ||
| 115 | } | ||
| 116 | |||
| 117 | var mid = lookup_table[@intCast(usize, exp)]; | ||
| 118 | mid = hpProd(mid, val); | ||
| 119 | const lten = lookup_table[@intCast(usize, exp)].val; | ||
| 120 | |||
| 121 | exp -= 307; | ||
| 122 | |||
| 123 | var ten: f64 = 1.0; | ||
| 124 | |||
| 125 | while (mid.val > 10.0 or (mid.val == 10.0 and mid.off >= 0.0)) { | ||
| 126 | exp += 1; | ||
| 127 | hpDiv10(&mid); | ||
| 128 | ten /= 10.0; | ||
| 129 | } | ||
| 130 | |||
| 131 | while (mid.val < 1.0 or (mid.val == 1.0 and mid.off < 0.0)) { | ||
| 132 | exp -= 1; | ||
| 133 | hpMul10(&mid); | ||
| 134 | ten *= 10.0; | ||
| 135 | } | ||
| 136 | |||
| 137 | // compute boundaries | ||
| 138 | var high = HP{ | ||
| 139 | .val = mid.val, | ||
| 140 | .off = mid.off + (fpnext(val) - val) * lten * ten / 2.0, | ||
| 141 | }; | ||
| 142 | var low = HP{ | ||
| 143 | .val = mid.val, | ||
| 144 | .off = mid.off + (fpprev(val) - val) * lten * ten / 2.0, | ||
| 145 | }; | ||
| 146 | |||
| 147 | hpNormalize(&high); | ||
| 148 | hpNormalize(&low); | ||
| 149 | |||
| 150 | // normalized boundaries | ||
| 151 | |||
| 152 | while (high.val > 10.0 or (high.val == 10.0 and high.off >= 0.0)) { | ||
| 153 | exp += 1; | ||
| 154 | hpDiv10(&high); | ||
| 155 | hpDiv10(&low); | ||
| 156 | } | ||
| 157 | |||
| 158 | while (high.val < 1.0 or (high.val == 1.0 and high.off < 0.0)) { | ||
| 159 | exp -= 1; | ||
| 160 | hpMul10(&high); | ||
| 161 | hpMul10(&low); | ||
| 162 | } | ||
| 163 | |||
| 164 | // digit generation | ||
| 165 | |||
| 166 | // We generate digits starting at index 1. If rounding a buffer later then it may be | ||
| 167 | // required to generate a preceding digit in some cases (9.999) in which case we use | ||
| 168 | // the 0-index for this extra digit. | ||
| 169 | var buf_index: usize = 1; | ||
| 170 | while (true) { | ||
| 171 | var hdig = @floatToInt(u8, math.floor(high.val)); | ||
| 172 | if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; | ||
| 173 | |||
| 174 | var ldig = @floatToInt(u8, math.floor(low.val)); | ||
| 175 | if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; | ||
| 176 | |||
| 177 | if (ldig != hdig) break; | ||
| 178 | |||
| 179 | buffer[buf_index] = hdig + '0'; | ||
| 180 | buf_index += 1; | ||
| 181 | high.val -= @intToFloat(f64, hdig); | ||
| 182 | low.val -= @intToFloat(f64, ldig); | ||
| 183 | hpMul10(&high); | ||
| 184 | hpMul10(&low); | ||
| 185 | } | ||
| 186 | |||
| 187 | const tmp = (high.val + low.val) / 2.0; | ||
| 188 | var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); | ||
| 189 | if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; | ||
| 190 | |||
| 191 | buffer[buf_index] = mdig + '0'; | ||
| 192 | buf_index += 1; | ||
| 193 | |||
| 194 | return FloatDecimal{ | ||
| 195 | .digits = buffer[1..buf_index], | ||
| 196 | .exp = exp, | ||
| 197 | }; | ||
| 198 | } | ||
| 199 | |||
| 200 | fn tableLowerBound(k: u64) usize { | ||
| 201 | var i = enum3.len; | ||
| 202 | var j: usize = 0; | ||
| 203 | |||
| 204 | while (j < enum3.len) { | ||
| 205 | if (enum3[j] < k) { | ||
| 206 | j = 2 * j + 2; | ||
| 207 | } else { | ||
| 208 | i = j; | ||
| 209 | j = 2 * j + 1; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | return i; | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Compute the product of an HP number and a double. | ||
| 217 | /// @in: The HP number. | ||
| 218 | /// @val: The double. | ||
| 219 | /// &returns: The HP number. | ||
| 220 | fn hpProd(in: HP, val: f64) HP { | ||
| 221 | var hi: f64 = undefined; | ||
| 222 | var lo: f64 = undefined; | ||
| 223 | split(in.val, &hi, &lo); | ||
| 224 | |||
| 225 | var hi2: f64 = undefined; | ||
| 226 | var lo2: f64 = undefined; | ||
| 227 | split(val, &hi2, &lo2); | ||
| 228 | |||
| 229 | const p = in.val * val; | ||
| 230 | const e = ((hi * hi2 - p) + lo * hi2 + hi * lo2) + lo * lo2; | ||
| 231 | |||
| 232 | return HP{ | ||
| 233 | .val = p, | ||
| 234 | .off = in.off * val + e, | ||
| 235 | }; | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Split a double into two halves. | ||
| 239 | /// @val: The double. | ||
| 240 | /// @hi: The high bits. | ||
| 241 | /// @lo: The low bits. | ||
| 242 | fn split(val: f64, hi: *f64, lo: *f64) void { | ||
| 243 | hi.* = gethi(val); | ||
| 244 | lo.* = val - hi.*; | ||
| 245 | } | ||
| 246 | |||
| 247 | fn gethi(in: f64) f64 { | ||
| 248 | const bits = @bitCast(u64, in); | ||
| 249 | const new_bits = bits & 0xFFFFFFFFF8000000; | ||
| 250 | return @bitCast(f64, new_bits); | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Normalize the number by factoring in the error. | ||
| 254 | /// @hp: The float pair. | ||
| 255 | fn hpNormalize(hp: *HP) void { | ||
| 256 | const val = hp.val; | ||
| 257 | hp.val += hp.off; | ||
| 258 | hp.off += val - hp.val; | ||
| 259 | } | ||
| 260 | |||
| 261 | /// Divide the high-precision number by ten. | ||
| 262 | /// @hp: The high-precision number | ||
| 263 | fn hpDiv10(hp: *HP) void { | ||
| 264 | var val = hp.val; | ||
| 265 | |||
| 266 | hp.val /= 10.0; | ||
| 267 | hp.off /= 10.0; | ||
| 268 | |||
| 269 | val -= hp.val * 8.0; | ||
| 270 | val -= hp.val * 2.0; | ||
| 271 | |||
| 272 | hp.off += val / 10.0; | ||
| 273 | |||
| 274 | hpNormalize(hp); | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Multiply the high-precision number by ten. | ||
| 278 | /// @hp: The high-precision number | ||
| 279 | fn hpMul10(hp: *HP) void { | ||
| 280 | const val = hp.val; | ||
| 281 | |||
| 282 | hp.val *= 10.0; | ||
| 283 | hp.off *= 10.0; | ||
| 284 | |||
| 285 | var off = hp.val; | ||
| 286 | off -= val * 8.0; | ||
| 287 | off -= val * 2.0; | ||
| 288 | |||
| 289 | hp.off -= off; | ||
| 290 | |||
| 291 | hpNormalize(hp); | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Integer conversion algorithm, guaranteed correct, optimal, and best. | ||
| 295 | /// @val: The val. | ||
| 296 | /// @buf: The output buffer. | ||
| 297 | /// &return: The exponent. | ||
| 298 | fn errolInt(val: f64, buffer: []u8) FloatDecimal { | ||
| 299 | const pow19 = u128(1e19); | ||
| 300 | |||
| 301 | assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); | ||
| 302 | |||
| 303 | var mid = @floatToInt(u128, val); | ||
| 304 | var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); | ||
| 305 | var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); | ||
| 306 | |||
| 307 | if (@bitCast(u64, val) & 0x1 != 0) { | ||
| 308 | high -= 1; | ||
| 309 | } else { | ||
| 310 | low -= 1; | ||
| 311 | } | ||
| 312 | |||
| 313 | var l64 = @intCast(u64, low % pow19); | ||
| 314 | const lf = @intCast(u64, (low / pow19) % pow19); | ||
| 315 | |||
| 316 | var h64 = @intCast(u64, high % pow19); | ||
| 317 | const hf = @intCast(u64, (high / pow19) % pow19); | ||
| 318 | |||
| 319 | if (lf != hf) { | ||
| 320 | l64 = lf; | ||
| 321 | h64 = hf; | ||
| 322 | mid = mid / (pow19 / 10); | ||
| 323 | } | ||
| 324 | |||
| 325 | var mi: i32 = mismatch10(l64, h64); | ||
| 326 | var x: u64 = 1; | ||
| 327 | { | ||
| 328 | var i: i32 = @boolToInt(lf == hf); | ||
| 329 | while (i < mi) : (i += 1) { | ||
| 330 | x *= 10; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | const m64 = @truncate(u64, @divTrunc(mid, x)); | ||
| 334 | |||
| 335 | if (lf != hf) mi += 19; | ||
| 336 | |||
| 337 | var buf_index = u64toa(m64, buffer) - 1; | ||
| 338 | |||
| 339 | if (mi != 0) { | ||
| 340 | buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); | ||
| 341 | } else { | ||
| 342 | buf_index += 1; | ||
| 343 | } | ||
| 344 | |||
| 345 | return FloatDecimal{ | ||
| 346 | .digits = buffer[0..buf_index], | ||
| 347 | .exp = @intCast(i32, buf_index) + mi, | ||
| 348 | }; | ||
| 349 | } | ||
| 350 | |||
| 351 | /// Fixed point conversion algorithm, guaranteed correct, optimal, and best. | ||
| 352 | /// @val: The val. | ||
| 353 | /// @buf: The output buffer. | ||
| 354 | /// &return: The exponent. | ||
| 355 | fn errolFixed(val: f64, buffer: []u8) FloatDecimal { | ||
| 356 | assert((val >= 16.0) and (val < 9.007199254740992e15)); | ||
| 357 | |||
| 358 | const u = @floatToInt(u64, val); | ||
| 359 | const n = @intToFloat(f64, u); | ||
| 360 | |||
| 361 | var mid = val - n; | ||
| 362 | var lo = ((fpprev(val) - n) + mid) / 2.0; | ||
| 363 | var hi = ((fpnext(val) - n) + mid) / 2.0; | ||
| 364 | |||
| 365 | var buf_index = u64toa(u, buffer); | ||
| 366 | var exp = @intCast(i32, buf_index); | ||
| 367 | var j = buf_index; | ||
| 368 | buffer[j] = 0; | ||
| 369 | |||
| 370 | if (mid != 0.0) { | ||
| 371 | while (mid != 0.0) { | ||
| 372 | lo *= 10.0; | ||
| 373 | const ldig = @floatToInt(i32, lo); | ||
| 374 | lo -= @intToFloat(f64, ldig); | ||
| 375 | |||
| 376 | mid *= 10.0; | ||
| 377 | const mdig = @floatToInt(i32, mid); | ||
| 378 | mid -= @intToFloat(f64, mdig); | ||
| 379 | |||
| 380 | hi *= 10.0; | ||
| 381 | const hdig = @floatToInt(i32, hi); | ||
| 382 | hi -= @intToFloat(f64, hdig); | ||
| 383 | |||
| 384 | buffer[j] = @intCast(u8, mdig + '0'); | ||
| 385 | j += 1; | ||
| 386 | |||
| 387 | if (hdig != ldig or j > 50) break; | ||
| 388 | } | ||
| 389 | |||
| 390 | if (mid > 0.5) { | ||
| 391 | buffer[j - 1] += 1; | ||
| 392 | } else if ((mid == 0.5) and (buffer[j - 1] & 0x1) != 0) { | ||
| 393 | buffer[j - 1] += 1; | ||
| 394 | } | ||
| 395 | } else { | ||
| 396 | while (buffer[j - 1] == '0') { | ||
| 397 | buffer[j - 1] = 0; | ||
| 398 | j -= 1; | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | buffer[j] = 0; | ||
| 403 | |||
| 404 | return FloatDecimal{ | ||
| 405 | .digits = buffer[0..j], | ||
| 406 | .exp = exp, | ||
| 407 | }; | ||
| 408 | } | ||
| 409 | |||
| 410 | fn fpnext(val: f64) f64 { | ||
| 411 | return @bitCast(f64, @bitCast(u64, val) +% 1); | ||
| 412 | } | ||
| 413 | |||
| 414 | fn fpprev(val: f64) f64 { | ||
| 415 | return @bitCast(f64, @bitCast(u64, val) -% 1); | ||
| 416 | } | ||
| 417 | |||
| 418 | pub const c_digits_lut = []u8{ | ||
| 419 | '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', | ||
| 420 | '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', | ||
| 421 | '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', | ||
| 422 | '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', | ||
| 423 | '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', | ||
| 424 | '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', | ||
| 425 | '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', | ||
| 426 | '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', | ||
| 427 | '5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', | ||
| 428 | '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', | ||
| 429 | '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', | ||
| 430 | '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', | ||
| 431 | '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', | ||
| 432 | '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', | ||
| 433 | '9', '8', '9', '9', | ||
| 434 | }; | ||
| 435 | |||
| 436 | fn u64toa(value_param: u64, buffer: []u8) usize { | ||
| 437 | var value = value_param; | ||
| 438 | const kTen8: u64 = 100000000; | ||
| 439 | const kTen9: u64 = kTen8 * 10; | ||
| 440 | const kTen10: u64 = kTen8 * 100; | ||
| 441 | const kTen11: u64 = kTen8 * 1000; | ||
| 442 | const kTen12: u64 = kTen8 * 10000; | ||
| 443 | const kTen13: u64 = kTen8 * 100000; | ||
| 444 | const kTen14: u64 = kTen8 * 1000000; | ||
| 445 | const kTen15: u64 = kTen8 * 10000000; | ||
| 446 | const kTen16: u64 = kTen8 * kTen8; | ||
| 447 | |||
| 448 | var buf_index: usize = 0; | ||
| 449 | |||
| 450 | if (value < kTen8) { | ||
| 451 | const v = @intCast(u32, value); | ||
| 452 | if (v < 10000) { | ||
| 453 | const d1: u32 = (v / 100) << 1; | ||
| 454 | const d2: u32 = (v % 100) << 1; | ||
| 455 | |||
| 456 | if (v >= 1000) { | ||
| 457 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 458 | buf_index += 1; | ||
| 459 | } | ||
| 460 | if (v >= 100) { | ||
| 461 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 462 | buf_index += 1; | ||
| 463 | } | ||
| 464 | if (v >= 10) { | ||
| 465 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 466 | buf_index += 1; | ||
| 467 | } | ||
| 468 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 469 | buf_index += 1; | ||
| 470 | } else { | ||
| 471 | // value = bbbbcccc | ||
| 472 | const b: u32 = v / 10000; | ||
| 473 | const c: u32 = v % 10000; | ||
| 474 | |||
| 475 | const d1: u32 = (b / 100) << 1; | ||
| 476 | const d2: u32 = (b % 100) << 1; | ||
| 477 | |||
| 478 | const d3: u32 = (c / 100) << 1; | ||
| 479 | const d4: u32 = (c % 100) << 1; | ||
| 480 | |||
| 481 | if (value >= 10000000) { | ||
| 482 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 483 | buf_index += 1; | ||
| 484 | } | ||
| 485 | if (value >= 1000000) { | ||
| 486 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 487 | buf_index += 1; | ||
| 488 | } | ||
| 489 | if (value >= 100000) { | ||
| 490 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 491 | buf_index += 1; | ||
| 492 | } | ||
| 493 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 494 | buf_index += 1; | ||
| 495 | |||
| 496 | buffer[buf_index] = c_digits_lut[d3]; | ||
| 497 | buf_index += 1; | ||
| 498 | buffer[buf_index] = c_digits_lut[d3 + 1]; | ||
| 499 | buf_index += 1; | ||
| 500 | buffer[buf_index] = c_digits_lut[d4]; | ||
| 501 | buf_index += 1; | ||
| 502 | buffer[buf_index] = c_digits_lut[d4 + 1]; | ||
| 503 | buf_index += 1; | ||
| 504 | } | ||
| 505 | } else if (value < kTen16) { | ||
| 506 | const v0: u32 = @intCast(u32, value / kTen8); | ||
| 507 | const v1: u32 = @intCast(u32, value % kTen8); | ||
| 508 | |||
| 509 | const b0: u32 = v0 / 10000; | ||
| 510 | const c0: u32 = v0 % 10000; | ||
| 511 | |||
| 512 | const d1: u32 = (b0 / 100) << 1; | ||
| 513 | const d2: u32 = (b0 % 100) << 1; | ||
| 514 | |||
| 515 | const d3: u32 = (c0 / 100) << 1; | ||
| 516 | const d4: u32 = (c0 % 100) << 1; | ||
| 517 | |||
| 518 | const b1: u32 = v1 / 10000; | ||
| 519 | const c1: u32 = v1 % 10000; | ||
| 520 | |||
| 521 | const d5: u32 = (b1 / 100) << 1; | ||
| 522 | const d6: u32 = (b1 % 100) << 1; | ||
| 523 | |||
| 524 | const d7: u32 = (c1 / 100) << 1; | ||
| 525 | const d8: u32 = (c1 % 100) << 1; | ||
| 526 | |||
| 527 | if (value >= kTen15) { | ||
| 528 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 529 | buf_index += 1; | ||
| 530 | } | ||
| 531 | if (value >= kTen14) { | ||
| 532 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 533 | buf_index += 1; | ||
| 534 | } | ||
| 535 | if (value >= kTen13) { | ||
| 536 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 537 | buf_index += 1; | ||
| 538 | } | ||
| 539 | if (value >= kTen12) { | ||
| 540 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 541 | buf_index += 1; | ||
| 542 | } | ||
| 543 | if (value >= kTen11) { | ||
| 544 | buffer[buf_index] = c_digits_lut[d3]; | ||
| 545 | buf_index += 1; | ||
| 546 | } | ||
| 547 | if (value >= kTen10) { | ||
| 548 | buffer[buf_index] = c_digits_lut[d3 + 1]; | ||
| 549 | buf_index += 1; | ||
| 550 | } | ||
| 551 | if (value >= kTen9) { | ||
| 552 | buffer[buf_index] = c_digits_lut[d4]; | ||
| 553 | buf_index += 1; | ||
| 554 | } | ||
| 555 | if (value >= kTen8) { | ||
| 556 | buffer[buf_index] = c_digits_lut[d4 + 1]; | ||
| 557 | buf_index += 1; | ||
| 558 | } | ||
| 559 | |||
| 560 | buffer[buf_index] = c_digits_lut[d5]; | ||
| 561 | buf_index += 1; | ||
| 562 | buffer[buf_index] = c_digits_lut[d5 + 1]; | ||
| 563 | buf_index += 1; | ||
| 564 | buffer[buf_index] = c_digits_lut[d6]; | ||
| 565 | buf_index += 1; | ||
| 566 | buffer[buf_index] = c_digits_lut[d6 + 1]; | ||
| 567 | buf_index += 1; | ||
| 568 | buffer[buf_index] = c_digits_lut[d7]; | ||
| 569 | buf_index += 1; | ||
| 570 | buffer[buf_index] = c_digits_lut[d7 + 1]; | ||
| 571 | buf_index += 1; | ||
| 572 | buffer[buf_index] = c_digits_lut[d8]; | ||
| 573 | buf_index += 1; | ||
| 574 | buffer[buf_index] = c_digits_lut[d8 + 1]; | ||
| 575 | buf_index += 1; | ||
| 576 | } else { | ||
| 577 | const a = @intCast(u32, value / kTen16); // 1 to 1844 | ||
| 578 | value %= kTen16; | ||
| 579 | |||
| 580 | if (a < 10) { | ||
| 581 | buffer[buf_index] = '0' + @intCast(u8, a); | ||
| 582 | buf_index += 1; | ||
| 583 | } else if (a < 100) { | ||
| 584 | const i: u32 = a << 1; | ||
| 585 | buffer[buf_index] = c_digits_lut[i]; | ||
| 586 | buf_index += 1; | ||
| 587 | buffer[buf_index] = c_digits_lut[i + 1]; | ||
| 588 | buf_index += 1; | ||
| 589 | } else if (a < 1000) { | ||
| 590 | buffer[buf_index] = '0' + @intCast(u8, a / 100); | ||
| 591 | buf_index += 1; | ||
| 592 | |||
| 593 | const i: u32 = (a % 100) << 1; | ||
| 594 | buffer[buf_index] = c_digits_lut[i]; | ||
| 595 | buf_index += 1; | ||
| 596 | buffer[buf_index] = c_digits_lut[i + 1]; | ||
| 597 | buf_index += 1; | ||
| 598 | } else { | ||
| 599 | const i: u32 = (a / 100) << 1; | ||
| 600 | const j: u32 = (a % 100) << 1; | ||
| 601 | buffer[buf_index] = c_digits_lut[i]; | ||
| 602 | buf_index += 1; | ||
| 603 | buffer[buf_index] = c_digits_lut[i + 1]; | ||
| 604 | buf_index += 1; | ||
| 605 | buffer[buf_index] = c_digits_lut[j]; | ||
| 606 | buf_index += 1; | ||
| 607 | buffer[buf_index] = c_digits_lut[j + 1]; | ||
| 608 | buf_index += 1; | ||
| 609 | } | ||
| 610 | |||
| 611 | const v0 = @intCast(u32, value / kTen8); | ||
| 612 | const v1 = @intCast(u32, value % kTen8); | ||
| 613 | |||
| 614 | const b0: u32 = v0 / 10000; | ||
| 615 | const c0: u32 = v0 % 10000; | ||
| 616 | |||
| 617 | const d1: u32 = (b0 / 100) << 1; | ||
| 618 | const d2: u32 = (b0 % 100) << 1; | ||
| 619 | |||
| 620 | const d3: u32 = (c0 / 100) << 1; | ||
| 621 | const d4: u32 = (c0 % 100) << 1; | ||
| 622 | |||
| 623 | const b1: u32 = v1 / 10000; | ||
| 624 | const c1: u32 = v1 % 10000; | ||
| 625 | |||
| 626 | const d5: u32 = (b1 / 100) << 1; | ||
| 627 | const d6: u32 = (b1 % 100) << 1; | ||
| 628 | |||
| 629 | const d7: u32 = (c1 / 100) << 1; | ||
| 630 | const d8: u32 = (c1 % 100) << 1; | ||
| 631 | |||
| 632 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 633 | buf_index += 1; | ||
| 634 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 635 | buf_index += 1; | ||
| 636 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 637 | buf_index += 1; | ||
| 638 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 639 | buf_index += 1; | ||
| 640 | buffer[buf_index] = c_digits_lut[d3]; | ||
| 641 | buf_index += 1; | ||
| 642 | buffer[buf_index] = c_digits_lut[d3 + 1]; | ||
| 643 | buf_index += 1; | ||
| 644 | buffer[buf_index] = c_digits_lut[d4]; | ||
| 645 | buf_index += 1; | ||
| 646 | buffer[buf_index] = c_digits_lut[d4 + 1]; | ||
| 647 | buf_index += 1; | ||
| 648 | buffer[buf_index] = c_digits_lut[d5]; | ||
| 649 | buf_index += 1; | ||
| 650 | buffer[buf_index] = c_digits_lut[d5 + 1]; | ||
| 651 | buf_index += 1; | ||
| 652 | buffer[buf_index] = c_digits_lut[d6]; | ||
| 653 | buf_index += 1; | ||
| 654 | buffer[buf_index] = c_digits_lut[d6 + 1]; | ||
| 655 | buf_index += 1; | ||
| 656 | buffer[buf_index] = c_digits_lut[d7]; | ||
| 657 | buf_index += 1; | ||
| 658 | buffer[buf_index] = c_digits_lut[d7 + 1]; | ||
| 659 | buf_index += 1; | ||
| 660 | buffer[buf_index] = c_digits_lut[d8]; | ||
| 661 | buf_index += 1; | ||
| 662 | buffer[buf_index] = c_digits_lut[d8 + 1]; | ||
| 663 | buf_index += 1; | ||
| 664 | } | ||
| 665 | |||
| 666 | return buf_index; | ||
| 667 | } | ||
| 668 | |||
| 669 | fn fpeint(from: f64) u128 { | ||
| 670 | const bits = @bitCast(u64, from); | ||
| 671 | assert((bits & ((1 << 52) - 1)) == 0); | ||
| 672 | |||
| 673 | return u128(1) << @truncate(u7, (bits >> 52) -% 1023); | ||
| 674 | } | ||
| 675 | |||
| 676 | /// Given two different integers with the same length in terms of the number | ||
| 677 | /// of decimal digits, index the digits from the right-most position starting | ||
| 678 | /// from zero, find the first index where the digits in the two integers | ||
| 679 | /// divergent starting from the highest index. | ||
| 680 | /// @a: Integer a. | ||
| 681 | /// @b: Integer b. | ||
| 682 | /// &returns: An index within [0, 19). | ||
| 683 | fn mismatch10(a: u64, b: u64) i32 { | ||
| 684 | const pow10 = 10000000000; | ||
| 685 | const af = a / pow10; | ||
| 686 | const bf = b / pow10; | ||
| 687 | |||
| 688 | var i: i32 = 0; | ||
| 689 | var a_copy = a; | ||
| 690 | var b_copy = b; | ||
| 691 | |||
| 692 | if (af != bf) { | ||
| 693 | i = 10; | ||
| 694 | a_copy = af; | ||
| 695 | b_copy = bf; | ||
| 696 | } | ||
| 697 | |||
| 698 | while (true) : (i += 1) { | ||
| 699 | a_copy /= 10; | ||
| 700 | b_copy /= 10; | ||
| 701 | |||
| 702 | if (a_copy == b_copy) return i; | ||
| 703 | } | ||
| 704 | } | ||
std/fmt/errol/index.zig deleted-704| ... | @@ -1,704 +0,0 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const enum3 = @import("enum3.zig").enum3; | ||
| 3 | const enum3_data = @import("enum3.zig").enum3_data; | ||
| 4 | const lookup_table = @import("lookup.zig").lookup_table; | ||
| 5 | const HP = @import("lookup.zig").HP; | ||
| 6 | const math = std.math; | ||
| 7 | const mem = std.mem; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | |||
| 10 | pub const FloatDecimal = struct { | ||
| 11 | digits: []u8, | ||
| 12 | exp: i32, | ||
| 13 | }; | ||
| 14 | |||
| 15 | pub const RoundMode = enum { | ||
| 16 | // Round only the fractional portion (e.g. 1234.23 has precision 2) | ||
| 17 | Decimal, | ||
| 18 | // Round the entire whole/fractional portion (e.g. 1.23423e3 has precision 5) | ||
| 19 | Scientific, | ||
| 20 | }; | ||
| 21 | |||
| 22 | /// Round a FloatDecimal as returned by errol3 to the specified fractional precision. | ||
| 23 | /// All digits after the specified precision should be considered invalid. | ||
| 24 | pub fn roundToPrecision(float_decimal: *FloatDecimal, precision: usize, mode: RoundMode) void { | ||
| 25 | // The round digit refers to the index which we should look at to determine | ||
| 26 | // whether we need to round to match the specified precision. | ||
| 27 | var round_digit: usize = 0; | ||
| 28 | |||
| 29 | switch (mode) { | ||
| 30 | RoundMode.Decimal => { | ||
| 31 | if (float_decimal.exp >= 0) { | ||
| 32 | round_digit = precision + @intCast(usize, float_decimal.exp); | ||
| 33 | } else { | ||
| 34 | // if a small negative exp, then adjust we need to offset by the number | ||
| 35 | // of leading zeros that will occur. | ||
| 36 | const min_exp_required = @intCast(usize, -float_decimal.exp); | ||
| 37 | if (precision > min_exp_required) { | ||
| 38 | round_digit = precision - min_exp_required; | ||
| 39 | } | ||
| 40 | } | ||
| 41 | }, | ||
| 42 | RoundMode.Scientific => { | ||
| 43 | round_digit = 1 + precision; | ||
| 44 | }, | ||
| 45 | } | ||
| 46 | |||
| 47 | // It suffices to look at just this digit. We don't round and propagate say 0.04999 to 0.05 | ||
| 48 | // first, and then to 0.1 in the case of a {.1} single precision. | ||
| 49 | |||
| 50 | // Find the digit which will signify the round point and start rounding backwards. | ||
| 51 | if (round_digit < float_decimal.digits.len and float_decimal.digits[round_digit] - '0' >= 5) { | ||
| 52 | assert(round_digit >= 0); | ||
| 53 | |||
| 54 | var i = round_digit; | ||
| 55 | while (true) { | ||
| 56 | if (i == 0) { | ||
| 57 | // Rounded all the way past the start. This was of the form 9.999... | ||
| 58 | // Slot the new digit in place and increase the exponent. | ||
| 59 | float_decimal.exp += 1; | ||
| 60 | |||
| 61 | // Re-size the buffer to use the reserved leading byte. | ||
| 62 | const one_before = @intToPtr([*]u8, @ptrToInt(&float_decimal.digits[0]) - 1); | ||
| 63 | float_decimal.digits = one_before[0 .. float_decimal.digits.len + 1]; | ||
| 64 | float_decimal.digits[0] = '1'; | ||
| 65 | return; | ||
| 66 | } | ||
| 67 | |||
| 68 | i -= 1; | ||
| 69 | |||
| 70 | const new_value = (float_decimal.digits[i] - '0' + 1) % 10; | ||
| 71 | float_decimal.digits[i] = new_value + '0'; | ||
| 72 | |||
| 73 | // must continue rounding until non-9 | ||
| 74 | if (new_value != 0) { | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | /// Corrected Errol3 double to ASCII conversion. | ||
| 82 | pub fn errol3(value: f64, buffer: []u8) FloatDecimal { | ||
| 83 | const bits = @bitCast(u64, value); | ||
| 84 | const i = tableLowerBound(bits); | ||
| 85 | if (i < enum3.len and enum3[i] == bits) { | ||
| 86 | const data = enum3_data[i]; | ||
| 87 | const digits = buffer[1 .. data.str.len + 1]; | ||
| 88 | mem.copy(u8, digits, data.str); | ||
| 89 | return FloatDecimal{ | ||
| 90 | .digits = digits, | ||
| 91 | .exp = data.exp, | ||
| 92 | }; | ||
| 93 | } | ||
| 94 | |||
| 95 | return errol3u(value, buffer); | ||
| 96 | } | ||
| 97 | |||
| 98 | /// Uncorrected Errol3 double to ASCII conversion. | ||
| 99 | fn errol3u(val: f64, buffer: []u8) FloatDecimal { | ||
| 100 | // check if in integer or fixed range | ||
| 101 | if (val > 9.007199254740992e15 and val < 3.40282366920938e+38) { | ||
| 102 | return errolInt(val, buffer); | ||
| 103 | } else if (val >= 16.0 and val < 9.007199254740992e15) { | ||
| 104 | return errolFixed(val, buffer); | ||
| 105 | } | ||
| 106 | |||
| 107 | // normalize the midpoint | ||
| 108 | |||
| 109 | const e = math.frexp(val).exponent; | ||
| 110 | var exp = @floatToInt(i16, math.floor(307 + @intToFloat(f64, e) * 0.30103)); | ||
| 111 | if (exp < 20) { | ||
| 112 | exp = 20; | ||
| 113 | } else if (@intCast(usize, exp) >= lookup_table.len) { | ||
| 114 | exp = @intCast(i16, lookup_table.len - 1); | ||
| 115 | } | ||
| 116 | |||
| 117 | var mid = lookup_table[@intCast(usize, exp)]; | ||
| 118 | mid = hpProd(mid, val); | ||
| 119 | const lten = lookup_table[@intCast(usize, exp)].val; | ||
| 120 | |||
| 121 | exp -= 307; | ||
| 122 | |||
| 123 | var ten: f64 = 1.0; | ||
| 124 | |||
| 125 | while (mid.val > 10.0 or (mid.val == 10.0 and mid.off >= 0.0)) { | ||
| 126 | exp += 1; | ||
| 127 | hpDiv10(&mid); | ||
| 128 | ten /= 10.0; | ||
| 129 | } | ||
| 130 | |||
| 131 | while (mid.val < 1.0 or (mid.val == 1.0 and mid.off < 0.0)) { | ||
| 132 | exp -= 1; | ||
| 133 | hpMul10(&mid); | ||
| 134 | ten *= 10.0; | ||
| 135 | } | ||
| 136 | |||
| 137 | // compute boundaries | ||
| 138 | var high = HP{ | ||
| 139 | .val = mid.val, | ||
| 140 | .off = mid.off + (fpnext(val) - val) * lten * ten / 2.0, | ||
| 141 | }; | ||
| 142 | var low = HP{ | ||
| 143 | .val = mid.val, | ||
| 144 | .off = mid.off + (fpprev(val) - val) * lten * ten / 2.0, | ||
| 145 | }; | ||
| 146 | |||
| 147 | hpNormalize(&high); | ||
| 148 | hpNormalize(&low); | ||
| 149 | |||
| 150 | // normalized boundaries | ||
| 151 | |||
| 152 | while (high.val > 10.0 or (high.val == 10.0 and high.off >= 0.0)) { | ||
| 153 | exp += 1; | ||
| 154 | hpDiv10(&high); | ||
| 155 | hpDiv10(&low); | ||
| 156 | } | ||
| 157 | |||
| 158 | while (high.val < 1.0 or (high.val == 1.0 and high.off < 0.0)) { | ||
| 159 | exp -= 1; | ||
| 160 | hpMul10(&high); | ||
| 161 | hpMul10(&low); | ||
| 162 | } | ||
| 163 | |||
| 164 | // digit generation | ||
| 165 | |||
| 166 | // We generate digits starting at index 1. If rounding a buffer later then it may be | ||
| 167 | // required to generate a preceding digit in some cases (9.999) in which case we use | ||
| 168 | // the 0-index for this extra digit. | ||
| 169 | var buf_index: usize = 1; | ||
| 170 | while (true) { | ||
| 171 | var hdig = @floatToInt(u8, math.floor(high.val)); | ||
| 172 | if ((high.val == @intToFloat(f64, hdig)) and (high.off < 0)) hdig -= 1; | ||
| 173 | |||
| 174 | var ldig = @floatToInt(u8, math.floor(low.val)); | ||
| 175 | if ((low.val == @intToFloat(f64, ldig)) and (low.off < 0)) ldig -= 1; | ||
| 176 | |||
| 177 | if (ldig != hdig) break; | ||
| 178 | |||
| 179 | buffer[buf_index] = hdig + '0'; | ||
| 180 | buf_index += 1; | ||
| 181 | high.val -= @intToFloat(f64, hdig); | ||
| 182 | low.val -= @intToFloat(f64, ldig); | ||
| 183 | hpMul10(&high); | ||
| 184 | hpMul10(&low); | ||
| 185 | } | ||
| 186 | |||
| 187 | const tmp = (high.val + low.val) / 2.0; | ||
| 188 | var mdig = @floatToInt(u8, math.floor(tmp + 0.5)); | ||
| 189 | if ((@intToFloat(f64, mdig) - tmp) == 0.5 and (mdig & 0x1) != 0) mdig -= 1; | ||
| 190 | |||
| 191 | buffer[buf_index] = mdig + '0'; | ||
| 192 | buf_index += 1; | ||
| 193 | |||
| 194 | return FloatDecimal{ | ||
| 195 | .digits = buffer[1..buf_index], | ||
| 196 | .exp = exp, | ||
| 197 | }; | ||
| 198 | } | ||
| 199 | |||
| 200 | fn tableLowerBound(k: u64) usize { | ||
| 201 | var i = enum3.len; | ||
| 202 | var j: usize = 0; | ||
| 203 | |||
| 204 | while (j < enum3.len) { | ||
| 205 | if (enum3[j] < k) { | ||
| 206 | j = 2 * j + 2; | ||
| 207 | } else { | ||
| 208 | i = j; | ||
| 209 | j = 2 * j + 1; | ||
| 210 | } | ||
| 211 | } | ||
| 212 | |||
| 213 | return i; | ||
| 214 | } | ||
| 215 | |||
| 216 | /// Compute the product of an HP number and a double. | ||
| 217 | /// @in: The HP number. | ||
| 218 | /// @val: The double. | ||
| 219 | /// &returns: The HP number. | ||
| 220 | fn hpProd(in: HP, val: f64) HP { | ||
| 221 | var hi: f64 = undefined; | ||
| 222 | var lo: f64 = undefined; | ||
| 223 | split(in.val, &hi, &lo); | ||
| 224 | |||
| 225 | var hi2: f64 = undefined; | ||
| 226 | var lo2: f64 = undefined; | ||
| 227 | split(val, &hi2, &lo2); | ||
| 228 | |||
| 229 | const p = in.val * val; | ||
| 230 | const e = ((hi * hi2 - p) + lo * hi2 + hi * lo2) + lo * lo2; | ||
| 231 | |||
| 232 | return HP{ | ||
| 233 | .val = p, | ||
| 234 | .off = in.off * val + e, | ||
| 235 | }; | ||
| 236 | } | ||
| 237 | |||
| 238 | /// Split a double into two halves. | ||
| 239 | /// @val: The double. | ||
| 240 | /// @hi: The high bits. | ||
| 241 | /// @lo: The low bits. | ||
| 242 | fn split(val: f64, hi: *f64, lo: *f64) void { | ||
| 243 | hi.* = gethi(val); | ||
| 244 | lo.* = val - hi.*; | ||
| 245 | } | ||
| 246 | |||
| 247 | fn gethi(in: f64) f64 { | ||
| 248 | const bits = @bitCast(u64, in); | ||
| 249 | const new_bits = bits & 0xFFFFFFFFF8000000; | ||
| 250 | return @bitCast(f64, new_bits); | ||
| 251 | } | ||
| 252 | |||
| 253 | /// Normalize the number by factoring in the error. | ||
| 254 | /// @hp: The float pair. | ||
| 255 | fn hpNormalize(hp: *HP) void { | ||
| 256 | const val = hp.val; | ||
| 257 | hp.val += hp.off; | ||
| 258 | hp.off += val - hp.val; | ||
| 259 | } | ||
| 260 | |||
| 261 | /// Divide the high-precision number by ten. | ||
| 262 | /// @hp: The high-precision number | ||
| 263 | fn hpDiv10(hp: *HP) void { | ||
| 264 | var val = hp.val; | ||
| 265 | |||
| 266 | hp.val /= 10.0; | ||
| 267 | hp.off /= 10.0; | ||
| 268 | |||
| 269 | val -= hp.val * 8.0; | ||
| 270 | val -= hp.val * 2.0; | ||
| 271 | |||
| 272 | hp.off += val / 10.0; | ||
| 273 | |||
| 274 | hpNormalize(hp); | ||
| 275 | } | ||
| 276 | |||
| 277 | /// Multiply the high-precision number by ten. | ||
| 278 | /// @hp: The high-precision number | ||
| 279 | fn hpMul10(hp: *HP) void { | ||
| 280 | const val = hp.val; | ||
| 281 | |||
| 282 | hp.val *= 10.0; | ||
| 283 | hp.off *= 10.0; | ||
| 284 | |||
| 285 | var off = hp.val; | ||
| 286 | off -= val * 8.0; | ||
| 287 | off -= val * 2.0; | ||
| 288 | |||
| 289 | hp.off -= off; | ||
| 290 | |||
| 291 | hpNormalize(hp); | ||
| 292 | } | ||
| 293 | |||
| 294 | /// Integer conversion algorithm, guaranteed correct, optimal, and best. | ||
| 295 | /// @val: The val. | ||
| 296 | /// @buf: The output buffer. | ||
| 297 | /// &return: The exponent. | ||
| 298 | fn errolInt(val: f64, buffer: []u8) FloatDecimal { | ||
| 299 | const pow19 = u128(1e19); | ||
| 300 | |||
| 301 | assert((val > 9.007199254740992e15) and val < (3.40282366920938e38)); | ||
| 302 | |||
| 303 | var mid = @floatToInt(u128, val); | ||
| 304 | var low: u128 = mid - fpeint((fpnext(val) - val) / 2.0); | ||
| 305 | var high: u128 = mid + fpeint((val - fpprev(val)) / 2.0); | ||
| 306 | |||
| 307 | if (@bitCast(u64, val) & 0x1 != 0) { | ||
| 308 | high -= 1; | ||
| 309 | } else { | ||
| 310 | low -= 1; | ||
| 311 | } | ||
| 312 | |||
| 313 | var l64 = @intCast(u64, low % pow19); | ||
| 314 | const lf = @intCast(u64, (low / pow19) % pow19); | ||
| 315 | |||
| 316 | var h64 = @intCast(u64, high % pow19); | ||
| 317 | const hf = @intCast(u64, (high / pow19) % pow19); | ||
| 318 | |||
| 319 | if (lf != hf) { | ||
| 320 | l64 = lf; | ||
| 321 | h64 = hf; | ||
| 322 | mid = mid / (pow19 / 10); | ||
| 323 | } | ||
| 324 | |||
| 325 | var mi: i32 = mismatch10(l64, h64); | ||
| 326 | var x: u64 = 1; | ||
| 327 | { | ||
| 328 | var i: i32 = @boolToInt(lf == hf); | ||
| 329 | while (i < mi) : (i += 1) { | ||
| 330 | x *= 10; | ||
| 331 | } | ||
| 332 | } | ||
| 333 | const m64 = @truncate(u64, @divTrunc(mid, x)); | ||
| 334 | |||
| 335 | if (lf != hf) mi += 19; | ||
| 336 | |||
| 337 | var buf_index = u64toa(m64, buffer) - 1; | ||
| 338 | |||
| 339 | if (mi != 0) { | ||
| 340 | buffer[buf_index - 1] += @boolToInt(buffer[buf_index] >= '5'); | ||
| 341 | } else { | ||
| 342 | buf_index += 1; | ||
| 343 | } | ||
| 344 | |||
| 345 | return FloatDecimal{ | ||
| 346 | .digits = buffer[0..buf_index], | ||
| 347 | .exp = @intCast(i32, buf_index) + mi, | ||
| 348 | }; | ||
| 349 | } | ||
| 350 | |||
| 351 | /// Fixed point conversion algorithm, guaranteed correct, optimal, and best. | ||
| 352 | /// @val: The val. | ||
| 353 | /// @buf: The output buffer. | ||
| 354 | /// &return: The exponent. | ||
| 355 | fn errolFixed(val: f64, buffer: []u8) FloatDecimal { | ||
| 356 | assert((val >= 16.0) and (val < 9.007199254740992e15)); | ||
| 357 | |||
| 358 | const u = @floatToInt(u64, val); | ||
| 359 | const n = @intToFloat(f64, u); | ||
| 360 | |||
| 361 | var mid = val - n; | ||
| 362 | var lo = ((fpprev(val) - n) + mid) / 2.0; | ||
| 363 | var hi = ((fpnext(val) - n) + mid) / 2.0; | ||
| 364 | |||
| 365 | var buf_index = u64toa(u, buffer); | ||
| 366 | var exp = @intCast(i32, buf_index); | ||
| 367 | var j = buf_index; | ||
| 368 | buffer[j] = 0; | ||
| 369 | |||
| 370 | if (mid != 0.0) { | ||
| 371 | while (mid != 0.0) { | ||
| 372 | lo *= 10.0; | ||
| 373 | const ldig = @floatToInt(i32, lo); | ||
| 374 | lo -= @intToFloat(f64, ldig); | ||
| 375 | |||
| 376 | mid *= 10.0; | ||
| 377 | const mdig = @floatToInt(i32, mid); | ||
| 378 | mid -= @intToFloat(f64, mdig); | ||
| 379 | |||
| 380 | hi *= 10.0; | ||
| 381 | const hdig = @floatToInt(i32, hi); | ||
| 382 | hi -= @intToFloat(f64, hdig); | ||
| 383 | |||
| 384 | buffer[j] = @intCast(u8, mdig + '0'); | ||
| 385 | j += 1; | ||
| 386 | |||
| 387 | if (hdig != ldig or j > 50) break; | ||
| 388 | } | ||
| 389 | |||
| 390 | if (mid > 0.5) { | ||
| 391 | buffer[j - 1] += 1; | ||
| 392 | } else if ((mid == 0.5) and (buffer[j - 1] & 0x1) != 0) { | ||
| 393 | buffer[j - 1] += 1; | ||
| 394 | } | ||
| 395 | } else { | ||
| 396 | while (buffer[j - 1] == '0') { | ||
| 397 | buffer[j - 1] = 0; | ||
| 398 | j -= 1; | ||
| 399 | } | ||
| 400 | } | ||
| 401 | |||
| 402 | buffer[j] = 0; | ||
| 403 | |||
| 404 | return FloatDecimal{ | ||
| 405 | .digits = buffer[0..j], | ||
| 406 | .exp = exp, | ||
| 407 | }; | ||
| 408 | } | ||
| 409 | |||
| 410 | fn fpnext(val: f64) f64 { | ||
| 411 | return @bitCast(f64, @bitCast(u64, val) +% 1); | ||
| 412 | } | ||
| 413 | |||
| 414 | fn fpprev(val: f64) f64 { | ||
| 415 | return @bitCast(f64, @bitCast(u64, val) -% 1); | ||
| 416 | } | ||
| 417 | |||
| 418 | pub const c_digits_lut = []u8{ | ||
| 419 | '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', '0', '6', | ||
| 420 | '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', | ||
| 421 | '1', '4', '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', | ||
| 422 | '2', '1', '2', '2', '2', '3', '2', '4', '2', '5', '2', '6', '2', '7', | ||
| 423 | '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', | ||
| 424 | '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', | ||
| 425 | '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', | ||
| 426 | '4', '9', '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', | ||
| 427 | '5', '6', '5', '7', '5', '8', '5', '9', '6', '0', '6', '1', '6', '2', | ||
| 428 | '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', | ||
| 429 | '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', | ||
| 430 | '7', '7', '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', | ||
| 431 | '8', '4', '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', '9', '0', | ||
| 432 | '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', '9', '6', '9', '7', | ||
| 433 | '9', '8', '9', '9', | ||
| 434 | }; | ||
| 435 | |||
| 436 | fn u64toa(value_param: u64, buffer: []u8) usize { | ||
| 437 | var value = value_param; | ||
| 438 | const kTen8: u64 = 100000000; | ||
| 439 | const kTen9: u64 = kTen8 * 10; | ||
| 440 | const kTen10: u64 = kTen8 * 100; | ||
| 441 | const kTen11: u64 = kTen8 * 1000; | ||
| 442 | const kTen12: u64 = kTen8 * 10000; | ||
| 443 | const kTen13: u64 = kTen8 * 100000; | ||
| 444 | const kTen14: u64 = kTen8 * 1000000; | ||
| 445 | const kTen15: u64 = kTen8 * 10000000; | ||
| 446 | const kTen16: u64 = kTen8 * kTen8; | ||
| 447 | |||
| 448 | var buf_index: usize = 0; | ||
| 449 | |||
| 450 | if (value < kTen8) { | ||
| 451 | const v = @intCast(u32, value); | ||
| 452 | if (v < 10000) { | ||
| 453 | const d1: u32 = (v / 100) << 1; | ||
| 454 | const d2: u32 = (v % 100) << 1; | ||
| 455 | |||
| 456 | if (v >= 1000) { | ||
| 457 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 458 | buf_index += 1; | ||
| 459 | } | ||
| 460 | if (v >= 100) { | ||
| 461 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 462 | buf_index += 1; | ||
| 463 | } | ||
| 464 | if (v >= 10) { | ||
| 465 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 466 | buf_index += 1; | ||
| 467 | } | ||
| 468 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 469 | buf_index += 1; | ||
| 470 | } else { | ||
| 471 | // value = bbbbcccc | ||
| 472 | const b: u32 = v / 10000; | ||
| 473 | const c: u32 = v % 10000; | ||
| 474 | |||
| 475 | const d1: u32 = (b / 100) << 1; | ||
| 476 | const d2: u32 = (b % 100) << 1; | ||
| 477 | |||
| 478 | const d3: u32 = (c / 100) << 1; | ||
| 479 | const d4: u32 = (c % 100) << 1; | ||
| 480 | |||
| 481 | if (value >= 10000000) { | ||
| 482 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 483 | buf_index += 1; | ||
| 484 | } | ||
| 485 | if (value >= 1000000) { | ||
| 486 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 487 | buf_index += 1; | ||
| 488 | } | ||
| 489 | if (value >= 100000) { | ||
| 490 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 491 | buf_index += 1; | ||
| 492 | } | ||
| 493 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 494 | buf_index += 1; | ||
| 495 | |||
| 496 | buffer[buf_index] = c_digits_lut[d3]; | ||
| 497 | buf_index += 1; | ||
| 498 | buffer[buf_index] = c_digits_lut[d3 + 1]; | ||
| 499 | buf_index += 1; | ||
| 500 | buffer[buf_index] = c_digits_lut[d4]; | ||
| 501 | buf_index += 1; | ||
| 502 | buffer[buf_index] = c_digits_lut[d4 + 1]; | ||
| 503 | buf_index += 1; | ||
| 504 | } | ||
| 505 | } else if (value < kTen16) { | ||
| 506 | const v0: u32 = @intCast(u32, value / kTen8); | ||
| 507 | const v1: u32 = @intCast(u32, value % kTen8); | ||
| 508 | |||
| 509 | const b0: u32 = v0 / 10000; | ||
| 510 | const c0: u32 = v0 % 10000; | ||
| 511 | |||
| 512 | const d1: u32 = (b0 / 100) << 1; | ||
| 513 | const d2: u32 = (b0 % 100) << 1; | ||
| 514 | |||
| 515 | const d3: u32 = (c0 / 100) << 1; | ||
| 516 | const d4: u32 = (c0 % 100) << 1; | ||
| 517 | |||
| 518 | const b1: u32 = v1 / 10000; | ||
| 519 | const c1: u32 = v1 % 10000; | ||
| 520 | |||
| 521 | const d5: u32 = (b1 / 100) << 1; | ||
| 522 | const d6: u32 = (b1 % 100) << 1; | ||
| 523 | |||
| 524 | const d7: u32 = (c1 / 100) << 1; | ||
| 525 | const d8: u32 = (c1 % 100) << 1; | ||
| 526 | |||
| 527 | if (value >= kTen15) { | ||
| 528 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 529 | buf_index += 1; | ||
| 530 | } | ||
| 531 | if (value >= kTen14) { | ||
| 532 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 533 | buf_index += 1; | ||
| 534 | } | ||
| 535 | if (value >= kTen13) { | ||
| 536 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 537 | buf_index += 1; | ||
| 538 | } | ||
| 539 | if (value >= kTen12) { | ||
| 540 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 541 | buf_index += 1; | ||
| 542 | } | ||
| 543 | if (value >= kTen11) { | ||
| 544 | buffer[buf_index] = c_digits_lut[d3]; | ||
| 545 | buf_index += 1; | ||
| 546 | } | ||
| 547 | if (value >= kTen10) { | ||
| 548 | buffer[buf_index] = c_digits_lut[d3 + 1]; | ||
| 549 | buf_index += 1; | ||
| 550 | } | ||
| 551 | if (value >= kTen9) { | ||
| 552 | buffer[buf_index] = c_digits_lut[d4]; | ||
| 553 | buf_index += 1; | ||
| 554 | } | ||
| 555 | if (value >= kTen8) { | ||
| 556 | buffer[buf_index] = c_digits_lut[d4 + 1]; | ||
| 557 | buf_index += 1; | ||
| 558 | } | ||
| 559 | |||
| 560 | buffer[buf_index] = c_digits_lut[d5]; | ||
| 561 | buf_index += 1; | ||
| 562 | buffer[buf_index] = c_digits_lut[d5 + 1]; | ||
| 563 | buf_index += 1; | ||
| 564 | buffer[buf_index] = c_digits_lut[d6]; | ||
| 565 | buf_index += 1; | ||
| 566 | buffer[buf_index] = c_digits_lut[d6 + 1]; | ||
| 567 | buf_index += 1; | ||
| 568 | buffer[buf_index] = c_digits_lut[d7]; | ||
| 569 | buf_index += 1; | ||
| 570 | buffer[buf_index] = c_digits_lut[d7 + 1]; | ||
| 571 | buf_index += 1; | ||
| 572 | buffer[buf_index] = c_digits_lut[d8]; | ||
| 573 | buf_index += 1; | ||
| 574 | buffer[buf_index] = c_digits_lut[d8 + 1]; | ||
| 575 | buf_index += 1; | ||
| 576 | } else { | ||
| 577 | const a = @intCast(u32, value / kTen16); // 1 to 1844 | ||
| 578 | value %= kTen16; | ||
| 579 | |||
| 580 | if (a < 10) { | ||
| 581 | buffer[buf_index] = '0' + @intCast(u8, a); | ||
| 582 | buf_index += 1; | ||
| 583 | } else if (a < 100) { | ||
| 584 | const i: u32 = a << 1; | ||
| 585 | buffer[buf_index] = c_digits_lut[i]; | ||
| 586 | buf_index += 1; | ||
| 587 | buffer[buf_index] = c_digits_lut[i + 1]; | ||
| 588 | buf_index += 1; | ||
| 589 | } else if (a < 1000) { | ||
| 590 | buffer[buf_index] = '0' + @intCast(u8, a / 100); | ||
| 591 | buf_index += 1; | ||
| 592 | |||
| 593 | const i: u32 = (a % 100) << 1; | ||
| 594 | buffer[buf_index] = c_digits_lut[i]; | ||
| 595 | buf_index += 1; | ||
| 596 | buffer[buf_index] = c_digits_lut[i + 1]; | ||
| 597 | buf_index += 1; | ||
| 598 | } else { | ||
| 599 | const i: u32 = (a / 100) << 1; | ||
| 600 | const j: u32 = (a % 100) << 1; | ||
| 601 | buffer[buf_index] = c_digits_lut[i]; | ||
| 602 | buf_index += 1; | ||
| 603 | buffer[buf_index] = c_digits_lut[i + 1]; | ||
| 604 | buf_index += 1; | ||
| 605 | buffer[buf_index] = c_digits_lut[j]; | ||
| 606 | buf_index += 1; | ||
| 607 | buffer[buf_index] = c_digits_lut[j + 1]; | ||
| 608 | buf_index += 1; | ||
| 609 | } | ||
| 610 | |||
| 611 | const v0 = @intCast(u32, value / kTen8); | ||
| 612 | const v1 = @intCast(u32, value % kTen8); | ||
| 613 | |||
| 614 | const b0: u32 = v0 / 10000; | ||
| 615 | const c0: u32 = v0 % 10000; | ||
| 616 | |||
| 617 | const d1: u32 = (b0 / 100) << 1; | ||
| 618 | const d2: u32 = (b0 % 100) << 1; | ||
| 619 | |||
| 620 | const d3: u32 = (c0 / 100) << 1; | ||
| 621 | const d4: u32 = (c0 % 100) << 1; | ||
| 622 | |||
| 623 | const b1: u32 = v1 / 10000; | ||
| 624 | const c1: u32 = v1 % 10000; | ||
| 625 | |||
| 626 | const d5: u32 = (b1 / 100) << 1; | ||
| 627 | const d6: u32 = (b1 % 100) << 1; | ||
| 628 | |||
| 629 | const d7: u32 = (c1 / 100) << 1; | ||
| 630 | const d8: u32 = (c1 % 100) << 1; | ||
| 631 | |||
| 632 | buffer[buf_index] = c_digits_lut[d1]; | ||
| 633 | buf_index += 1; | ||
| 634 | buffer[buf_index] = c_digits_lut[d1 + 1]; | ||
| 635 | buf_index += 1; | ||
| 636 | buffer[buf_index] = c_digits_lut[d2]; | ||
| 637 | buf_index += 1; | ||
| 638 | buffer[buf_index] = c_digits_lut[d2 + 1]; | ||
| 639 | buf_index += 1; | ||
| 640 | buffer[buf_index] = c_digits_lut[d3]; | ||
| 641 | buf_index += 1; | ||
| 642 | buffer[buf_index] = c_digits_lut[d3 + 1]; | ||
| 643 | buf_index += 1; | ||
| 644 | buffer[buf_index] = c_digits_lut[d4]; | ||
| 645 | buf_index += 1; | ||
| 646 | buffer[buf_index] = c_digits_lut[d4 + 1]; | ||
| 647 | buf_index += 1; | ||
| 648 | buffer[buf_index] = c_digits_lut[d5]; | ||
| 649 | buf_index += 1; | ||
| 650 | buffer[buf_index] = c_digits_lut[d5 + 1]; | ||
| 651 | buf_index += 1; | ||
| 652 | buffer[buf_index] = c_digits_lut[d6]; | ||
| 653 | buf_index += 1; | ||
| 654 | buffer[buf_index] = c_digits_lut[d6 + 1]; | ||
| 655 | buf_index += 1; | ||
| 656 | buffer[buf_index] = c_digits_lut[d7]; | ||
| 657 | buf_index += 1; | ||
| 658 | buffer[buf_index] = c_digits_lut[d7 + 1]; | ||
| 659 | buf_index += 1; | ||
| 660 | buffer[buf_index] = c_digits_lut[d8]; | ||
| 661 | buf_index += 1; | ||
| 662 | buffer[buf_index] = c_digits_lut[d8 + 1]; | ||
| 663 | buf_index += 1; | ||
| 664 | } | ||
| 665 | |||
| 666 | return buf_index; | ||
| 667 | } | ||
| 668 | |||
| 669 | fn fpeint(from: f64) u128 { | ||
| 670 | const bits = @bitCast(u64, from); | ||
| 671 | assert((bits & ((1 << 52) - 1)) == 0); | ||
| 672 | |||
| 673 | return u128(1) << @truncate(u7, (bits >> 52) -% 1023); | ||
| 674 | } | ||
| 675 | |||
| 676 | /// Given two different integers with the same length in terms of the number | ||
| 677 | /// of decimal digits, index the digits from the right-most position starting | ||
| 678 | /// from zero, find the first index where the digits in the two integers | ||
| 679 | /// divergent starting from the highest index. | ||
| 680 | /// @a: Integer a. | ||
| 681 | /// @b: Integer b. | ||
| 682 | /// &returns: An index within [0, 19). | ||
| 683 | fn mismatch10(a: u64, b: u64) i32 { | ||
| 684 | const pow10 = 10000000000; | ||
| 685 | const af = a / pow10; | ||
| 686 | const bf = b / pow10; | ||
| 687 | |||
| 688 | var i: i32 = 0; | ||
| 689 | var a_copy = a; | ||
| 690 | var b_copy = b; | ||
| 691 | |||
| 692 | if (af != bf) { | ||
| 693 | i = 10; | ||
| 694 | a_copy = af; | ||
| 695 | b_copy = bf; | ||
| 696 | } | ||
| 697 | |||
| 698 | while (true) : (i += 1) { | ||
| 699 | a_copy /= 10; | ||
| 700 | b_copy /= 10; | ||
| 701 | |||
| 702 | if (a_copy == b_copy) return i; | ||
| 703 | } | ||
| 704 | } | ||
std/fmt/index.zig deleted-1441| ... | @@ -1,1441 +0,0 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const math = std.math; | ||
| 3 | const debug = std.debug; | ||
| 4 | const assert = debug.assert; | ||
| 5 | const testing = std.testing; | ||
| 6 | const mem = std.mem; | ||
| 7 | const builtin = @import("builtin"); | ||
| 8 | const errol = @import("errol/index.zig"); | ||
| 9 | const lossyCast = std.math.lossyCast; | ||
| 10 | |||
| 11 | const max_int_digits = 65; | ||
| 12 | |||
| 13 | /// Renders fmt string with args, calling output with slices of bytes. | ||
| 14 | /// If `output` returns an error, the error is returned from `format` and | ||
| 15 | /// `output` is not called again. | ||
| 16 | pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context), []const u8) Errors!void, comptime fmt: []const u8, args: ...) Errors!void { | ||
| 17 | const State = enum { | ||
| 18 | Start, | ||
| 19 | OpenBrace, | ||
| 20 | CloseBrace, | ||
| 21 | FormatString, | ||
| 22 | Pointer, | ||
| 23 | }; | ||
| 24 | |||
| 25 | comptime var start_index = 0; | ||
| 26 | comptime var state = State.Start; | ||
| 27 | comptime var next_arg = 0; | ||
| 28 | |||
| 29 | inline for (fmt) |c, i| { | ||
| 30 | switch (state) { | ||
| 31 | State.Start => switch (c) { | ||
| 32 | '{' => { | ||
| 33 | if (start_index < i) { | ||
| 34 | try output(context, fmt[start_index..i]); | ||
| 35 | } | ||
| 36 | start_index = i; | ||
| 37 | state = State.OpenBrace; | ||
| 38 | }, | ||
| 39 | |||
| 40 | '}' => { | ||
| 41 | if (start_index < i) { | ||
| 42 | try output(context, fmt[start_index..i]); | ||
| 43 | } | ||
| 44 | state = State.CloseBrace; | ||
| 45 | }, | ||
| 46 | else => {}, | ||
| 47 | }, | ||
| 48 | State.OpenBrace => switch (c) { | ||
| 49 | '{' => { | ||
| 50 | state = State.Start; | ||
| 51 | start_index = i; | ||
| 52 | }, | ||
| 53 | '}' => { | ||
| 54 | try formatType(args[next_arg], fmt[0..0], context, Errors, output); | ||
| 55 | next_arg += 1; | ||
| 56 | state = State.Start; | ||
| 57 | start_index = i + 1; | ||
| 58 | }, | ||
| 59 | '*' => state = State.Pointer, | ||
| 60 | else => { | ||
| 61 | state = State.FormatString; | ||
| 62 | }, | ||
| 63 | }, | ||
| 64 | State.CloseBrace => switch (c) { | ||
| 65 | '}' => { | ||
| 66 | state = State.Start; | ||
| 67 | start_index = i; | ||
| 68 | }, | ||
| 69 | else => @compileError("Single '}' encountered in format string"), | ||
| 70 | }, | ||
| 71 | State.FormatString => switch (c) { | ||
| 72 | '}' => { | ||
| 73 | const s = start_index + 1; | ||
| 74 | try formatType(args[next_arg], fmt[s..i], context, Errors, output); | ||
| 75 | next_arg += 1; | ||
| 76 | state = State.Start; | ||
| 77 | start_index = i + 1; | ||
| 78 | }, | ||
| 79 | else => {}, | ||
| 80 | }, | ||
| 81 | State.Pointer => switch (c) { | ||
| 82 | '}' => { | ||
| 83 | try output(context, @typeName(@typeOf(args[next_arg]).Child)); | ||
| 84 | try output(context, "@"); | ||
| 85 | try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output); | ||
| 86 | next_arg += 1; | ||
| 87 | state = State.Start; | ||
| 88 | start_index = i + 1; | ||
| 89 | }, | ||
| 90 | else => @compileError("Unexpected format character after '*'"), | ||
| 91 | }, | ||
| 92 | } | ||
| 93 | } | ||
| 94 | comptime { | ||
| 95 | if (args.len != next_arg) { | ||
| 96 | @compileError("Unused arguments"); | ||
| 97 | } | ||
| 98 | if (state != State.Start) { | ||
| 99 | @compileError("Incomplete format string: " ++ fmt); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | if (start_index < fmt.len) { | ||
| 103 | try output(context, fmt[start_index..]); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | pub fn formatType( | ||
| 108 | value: var, | ||
| 109 | comptime fmt: []const u8, | ||
| 110 | context: var, | ||
| 111 | comptime Errors: type, | ||
| 112 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 113 | ) Errors!void { | ||
| 114 | const T = @typeOf(value); | ||
| 115 | if (T == anyerror) { | ||
| 116 | try output(context, "error."); | ||
| 117 | return output(context, @errorName(value)); | ||
| 118 | } | ||
| 119 | switch (@typeInfo(T)) { | ||
| 120 | builtin.TypeId.ComptimeInt, builtin.TypeId.Int, builtin.TypeId.Float => { | ||
| 121 | return formatValue(value, fmt, context, Errors, output); | ||
| 122 | }, | ||
| 123 | builtin.TypeId.Void => { | ||
| 124 | return output(context, "void"); | ||
| 125 | }, | ||
| 126 | builtin.TypeId.Bool => { | ||
| 127 | return output(context, if (value) "true" else "false"); | ||
| 128 | }, | ||
| 129 | builtin.TypeId.Optional => { | ||
| 130 | if (value) |payload| { | ||
| 131 | return formatType(payload, fmt, context, Errors, output); | ||
| 132 | } else { | ||
| 133 | return output(context, "null"); | ||
| 134 | } | ||
| 135 | }, | ||
| 136 | builtin.TypeId.ErrorUnion => { | ||
| 137 | if (value) |payload| { | ||
| 138 | return formatType(payload, fmt, context, Errors, output); | ||
| 139 | } else |err| { | ||
| 140 | return formatType(err, fmt, context, Errors, output); | ||
| 141 | } | ||
| 142 | }, | ||
| 143 | builtin.TypeId.ErrorSet => { | ||
| 144 | try output(context, "error."); | ||
| 145 | return output(context, @errorName(value)); | ||
| 146 | }, | ||
| 147 | builtin.TypeId.Promise => { | ||
| 148 | return format(context, Errors, output, "promise@{x}", @ptrToInt(value)); | ||
| 149 | }, | ||
| 150 | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { | ||
| 151 | const has_cust_fmt = comptime cf: { | ||
| 152 | const info = @typeInfo(T); | ||
| 153 | const defs = switch (info) { | ||
| 154 | builtin.TypeId.Struct => |s| s.defs, | ||
| 155 | builtin.TypeId.Union => |u| u.defs, | ||
| 156 | builtin.TypeId.Enum => |e| e.defs, | ||
| 157 | else => unreachable, | ||
| 158 | }; | ||
| 159 | |||
| 160 | for (defs) |def| { | ||
| 161 | if (mem.eql(u8, def.name, "format")) { | ||
| 162 | break :cf true; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | break :cf false; | ||
| 166 | }; | ||
| 167 | if (has_cust_fmt) return value.format(fmt, context, Errors, output); | ||
| 168 | |||
| 169 | try output(context, @typeName(T)); | ||
| 170 | switch (comptime @typeId(T)) { | ||
| 171 | builtin.TypeId.Enum => { | ||
| 172 | try output(context, "."); | ||
| 173 | try formatType(@tagName(value), "", context, Errors, output); | ||
| 174 | return; | ||
| 175 | }, | ||
| 176 | builtin.TypeId.Struct => { | ||
| 177 | comptime var field_i = 0; | ||
| 178 | inline while (field_i < @memberCount(T)) : (field_i += 1) { | ||
| 179 | if (field_i == 0) { | ||
| 180 | try output(context, "{ ."); | ||
| 181 | } else { | ||
| 182 | try output(context, ", ."); | ||
| 183 | } | ||
| 184 | try output(context, @memberName(T, field_i)); | ||
| 185 | try output(context, " = "); | ||
| 186 | try formatType(@field(value, @memberName(T, field_i)), "", context, Errors, output); | ||
| 187 | } | ||
| 188 | try output(context, " }"); | ||
| 189 | }, | ||
| 190 | builtin.TypeId.Union => { | ||
| 191 | const info = @typeInfo(T).Union; | ||
| 192 | if (info.tag_type) |UnionTagType| { | ||
| 193 | try output(context, "{ ."); | ||
| 194 | try output(context, @tagName(UnionTagType(value))); | ||
| 195 | try output(context, " = "); | ||
| 196 | inline for (info.fields) |u_field| { | ||
| 197 | if (@enumToInt(UnionTagType(value)) == u_field.enum_field.?.value) { | ||
| 198 | try formatType(@field(value, u_field.name), "", context, Errors, output); | ||
| 199 | } | ||
| 200 | } | ||
| 201 | try output(context, " }"); | ||
| 202 | } else { | ||
| 203 | try format(context, Errors, output, "@{x}", @ptrToInt(&value)); | ||
| 204 | } | ||
| 205 | }, | ||
| 206 | else => unreachable, | ||
| 207 | } | ||
| 208 | return; | ||
| 209 | }, | ||
| 210 | builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) { | ||
| 211 | builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) { | ||
| 212 | builtin.TypeId.Array => |info| { | ||
| 213 | if (info.child == u8) { | ||
| 214 | return formatText(value, fmt, context, Errors, output); | ||
| 215 | } | ||
| 216 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | ||
| 217 | }, | ||
| 218 | builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { | ||
| 219 | return formatType(value.*, fmt, context, Errors, output); | ||
| 220 | }, | ||
| 221 | else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), | ||
| 222 | }, | ||
| 223 | builtin.TypeInfo.Pointer.Size.Many => { | ||
| 224 | if (ptr_info.child == u8) { | ||
| 225 | if (fmt.len > 0 and fmt[0] == 's') { | ||
| 226 | const len = std.cstr.len(value); | ||
| 227 | return formatText(value[0..len], fmt, context, Errors, output); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | ||
| 231 | }, | ||
| 232 | builtin.TypeInfo.Pointer.Size.Slice => { | ||
| 233 | if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) { | ||
| 234 | return formatText(value, fmt, context, Errors, output); | ||
| 235 | } | ||
| 236 | const casted_value = ([]const u8)(value); | ||
| 237 | return output(context, casted_value); | ||
| 238 | }, | ||
| 239 | builtin.TypeInfo.Pointer.Size.C => { | ||
| 240 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); | ||
| 241 | }, | ||
| 242 | }, | ||
| 243 | builtin.TypeId.Array => |info| { | ||
| 244 | if (info.child == u8) { | ||
| 245 | return formatText(value, fmt, context, Errors, output); | ||
| 246 | } | ||
| 247 | return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(&value)); | ||
| 248 | }, | ||
| 249 | builtin.TypeId.Fn => { | ||
| 250 | return format(context, Errors, output, "{}@{x}", @typeName(T), @ptrToInt(value)); | ||
| 251 | }, | ||
| 252 | else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"), | ||
| 253 | } | ||
| 254 | } | ||
| 255 | |||
| 256 | fn formatValue( | ||
| 257 | value: var, | ||
| 258 | comptime fmt: []const u8, | ||
| 259 | context: var, | ||
| 260 | comptime Errors: type, | ||
| 261 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 262 | ) Errors!void { | ||
| 263 | if (fmt.len > 0) { | ||
| 264 | if (fmt[0] == 'B') { | ||
| 265 | comptime var width: ?usize = null; | ||
| 266 | if (fmt.len > 1) { | ||
| 267 | if (fmt[1] == 'i') { | ||
| 268 | if (fmt.len > 2) width = comptime (parseUnsigned(usize, fmt[2..], 10) catch unreachable); | ||
| 269 | return formatBytes(value, width, 1024, context, Errors, output); | ||
| 270 | } | ||
| 271 | width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 272 | } | ||
| 273 | return formatBytes(value, width, 1000, context, Errors, output); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | |||
| 277 | const T = @typeOf(value); | ||
| 278 | switch (@typeId(T)) { | ||
| 279 | builtin.TypeId.Float => return formatFloatValue(value, fmt, context, Errors, output), | ||
| 280 | builtin.TypeId.Int => return formatIntValue(value, fmt, context, Errors, output), | ||
| 281 | builtin.TypeId.ComptimeInt => { | ||
| 282 | const Int = math.IntFittingRange(value, value); | ||
| 283 | return formatIntValue(Int(value), fmt, context, Errors, output); | ||
| 284 | }, | ||
| 285 | else => comptime unreachable, | ||
| 286 | } | ||
| 287 | } | ||
| 288 | |||
| 289 | pub fn formatIntValue( | ||
| 290 | value: var, | ||
| 291 | comptime fmt: []const u8, | ||
| 292 | context: var, | ||
| 293 | comptime Errors: type, | ||
| 294 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 295 | ) Errors!void { | ||
| 296 | comptime var radix = 10; | ||
| 297 | comptime var uppercase = false; | ||
| 298 | comptime var width = 0; | ||
| 299 | if (fmt.len > 0) { | ||
| 300 | switch (fmt[0]) { | ||
| 301 | 'c' => { | ||
| 302 | if (@typeOf(value).bit_count <= 8) { | ||
| 303 | if (fmt.len > 1) | ||
| 304 | @compileError("Unknown format character: " ++ []u8{fmt[1]}); | ||
| 305 | return formatAsciiChar(u8(value), context, Errors, output); | ||
| 306 | } | ||
| 307 | }, | ||
| 308 | 'b' => { | ||
| 309 | radix = 2; | ||
| 310 | uppercase = false; | ||
| 311 | width = 0; | ||
| 312 | }, | ||
| 313 | 'd' => { | ||
| 314 | radix = 10; | ||
| 315 | uppercase = false; | ||
| 316 | width = 0; | ||
| 317 | }, | ||
| 318 | 'x' => { | ||
| 319 | radix = 16; | ||
| 320 | uppercase = false; | ||
| 321 | width = 0; | ||
| 322 | }, | ||
| 323 | 'X' => { | ||
| 324 | radix = 16; | ||
| 325 | uppercase = true; | ||
| 326 | width = 0; | ||
| 327 | }, | ||
| 328 | else => @compileError("Unknown format character: " ++ []u8{fmt[0]}), | ||
| 329 | } | ||
| 330 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 331 | } | ||
| 332 | return formatInt(value, radix, uppercase, width, context, Errors, output); | ||
| 333 | } | ||
| 334 | |||
| 335 | fn formatFloatValue( | ||
| 336 | value: var, | ||
| 337 | comptime fmt: []const u8, | ||
| 338 | context: var, | ||
| 339 | comptime Errors: type, | ||
| 340 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 341 | ) Errors!void { | ||
| 342 | comptime var width: ?usize = null; | ||
| 343 | comptime var float_fmt = 'e'; | ||
| 344 | if (fmt.len > 0) { | ||
| 345 | float_fmt = fmt[0]; | ||
| 346 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 347 | } | ||
| 348 | |||
| 349 | switch (float_fmt) { | ||
| 350 | 'e' => try formatFloatScientific(value, width, context, Errors, output), | ||
| 351 | '.' => try formatFloatDecimal(value, width, context, Errors, output), | ||
| 352 | else => @compileError("Unknown format character: " ++ []u8{float_fmt}), | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | pub fn formatText( | ||
| 357 | bytes: []const u8, | ||
| 358 | comptime fmt: []const u8, | ||
| 359 | context: var, | ||
| 360 | comptime Errors: type, | ||
| 361 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 362 | ) Errors!void { | ||
| 363 | if (fmt.len > 0) { | ||
| 364 | if (fmt[0] == 's') { | ||
| 365 | comptime var width = 0; | ||
| 366 | if (fmt.len > 1) width = comptime (parseUnsigned(usize, fmt[1..], 10) catch unreachable); | ||
| 367 | return formatBuf(bytes, width, context, Errors, output); | ||
| 368 | } else if ((fmt[0] == 'x') or (fmt[0] == 'X')) { | ||
| 369 | for (bytes) |c| { | ||
| 370 | try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output); | ||
| 371 | } | ||
| 372 | return; | ||
| 373 | } else @compileError("Unknown format character: " ++ []u8{fmt[0]}); | ||
| 374 | } | ||
| 375 | return output(context, bytes); | ||
| 376 | } | ||
| 377 | |||
| 378 | pub fn formatAsciiChar( | ||
| 379 | c: u8, | ||
| 380 | context: var, | ||
| 381 | comptime Errors: type, | ||
| 382 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 383 | ) Errors!void { | ||
| 384 | return output(context, (*[1]u8)(&c)[0..]); | ||
| 385 | } | ||
| 386 | |||
| 387 | pub fn formatBuf( | ||
| 388 | buf: []const u8, | ||
| 389 | width: usize, | ||
| 390 | context: var, | ||
| 391 | comptime Errors: type, | ||
| 392 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 393 | ) Errors!void { | ||
| 394 | try output(context, buf); | ||
| 395 | |||
| 396 | var leftover_padding = if (width > buf.len) (width - buf.len) else return; | ||
| 397 | const pad_byte: u8 = ' '; | ||
| 398 | while (leftover_padding > 0) : (leftover_padding -= 1) { | ||
| 399 | try output(context, (*[1]u8)(&pad_byte)[0..1]); | ||
| 400 | } | ||
| 401 | } | ||
| 402 | |||
| 403 | // Print a float in scientific notation to the specified precision. Null uses full precision. | ||
| 404 | // It should be the case that every full precision, printed value can be re-parsed back to the | ||
| 405 | // same type unambiguously. | ||
| 406 | pub fn formatFloatScientific( | ||
| 407 | value: var, | ||
| 408 | maybe_precision: ?usize, | ||
| 409 | context: var, | ||
| 410 | comptime Errors: type, | ||
| 411 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 412 | ) Errors!void { | ||
| 413 | var x = @floatCast(f64, value); | ||
| 414 | |||
| 415 | // Errol doesn't handle these special cases. | ||
| 416 | if (math.signbit(x)) { | ||
| 417 | try output(context, "-"); | ||
| 418 | x = -x; | ||
| 419 | } | ||
| 420 | |||
| 421 | if (math.isNan(x)) { | ||
| 422 | return output(context, "nan"); | ||
| 423 | } | ||
| 424 | if (math.isPositiveInf(x)) { | ||
| 425 | return output(context, "inf"); | ||
| 426 | } | ||
| 427 | if (x == 0.0) { | ||
| 428 | try output(context, "0"); | ||
| 429 | |||
| 430 | if (maybe_precision) |precision| { | ||
| 431 | if (precision != 0) { | ||
| 432 | try output(context, "."); | ||
| 433 | var i: usize = 0; | ||
| 434 | while (i < precision) : (i += 1) { | ||
| 435 | try output(context, "0"); | ||
| 436 | } | ||
| 437 | } | ||
| 438 | } else { | ||
| 439 | try output(context, ".0"); | ||
| 440 | } | ||
| 441 | |||
| 442 | try output(context, "e+00"); | ||
| 443 | return; | ||
| 444 | } | ||
| 445 | |||
| 446 | var buffer: [32]u8 = undefined; | ||
| 447 | var float_decimal = errol.errol3(x, buffer[0..]); | ||
| 448 | |||
| 449 | if (maybe_precision) |precision| { | ||
| 450 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); | ||
| 451 | |||
| 452 | try output(context, float_decimal.digits[0..1]); | ||
| 453 | |||
| 454 | // {e0} case prints no `.` | ||
| 455 | if (precision != 0) { | ||
| 456 | try output(context, "."); | ||
| 457 | |||
| 458 | var printed: usize = 0; | ||
| 459 | if (float_decimal.digits.len > 1) { | ||
| 460 | const num_digits = math.min(float_decimal.digits.len, precision + 1); | ||
| 461 | try output(context, float_decimal.digits[1..num_digits]); | ||
| 462 | printed += num_digits - 1; | ||
| 463 | } | ||
| 464 | |||
| 465 | while (printed < precision) : (printed += 1) { | ||
| 466 | try output(context, "0"); | ||
| 467 | } | ||
| 468 | } | ||
| 469 | } else { | ||
| 470 | try output(context, float_decimal.digits[0..1]); | ||
| 471 | try output(context, "."); | ||
| 472 | if (float_decimal.digits.len > 1) { | ||
| 473 | const num_digits = if (@typeOf(value) == f32) math.min(usize(9), float_decimal.digits.len) else float_decimal.digits.len; | ||
| 474 | |||
| 475 | try output(context, float_decimal.digits[1..num_digits]); | ||
| 476 | } else { | ||
| 477 | try output(context, "0"); | ||
| 478 | } | ||
| 479 | } | ||
| 480 | |||
| 481 | try output(context, "e"); | ||
| 482 | const exp = float_decimal.exp - 1; | ||
| 483 | |||
| 484 | if (exp >= 0) { | ||
| 485 | try output(context, "+"); | ||
| 486 | if (exp > -10 and exp < 10) { | ||
| 487 | try output(context, "0"); | ||
| 488 | } | ||
| 489 | try formatInt(exp, 10, false, 0, context, Errors, output); | ||
| 490 | } else { | ||
| 491 | try output(context, "-"); | ||
| 492 | if (exp > -10 and exp < 10) { | ||
| 493 | try output(context, "0"); | ||
| 494 | } | ||
| 495 | try formatInt(-exp, 10, false, 0, context, Errors, output); | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | // Print a float of the format x.yyyyy where the number of y is specified by the precision argument. | ||
| 500 | // By default floats are printed at full precision (no rounding). | ||
| 501 | pub fn formatFloatDecimal( | ||
| 502 | value: var, | ||
| 503 | maybe_precision: ?usize, | ||
| 504 | context: var, | ||
| 505 | comptime Errors: type, | ||
| 506 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 507 | ) Errors!void { | ||
| 508 | var x = f64(value); | ||
| 509 | |||
| 510 | // Errol doesn't handle these special cases. | ||
| 511 | if (math.signbit(x)) { | ||
| 512 | try output(context, "-"); | ||
| 513 | x = -x; | ||
| 514 | } | ||
| 515 | |||
| 516 | if (math.isNan(x)) { | ||
| 517 | return output(context, "nan"); | ||
| 518 | } | ||
| 519 | if (math.isPositiveInf(x)) { | ||
| 520 | return output(context, "inf"); | ||
| 521 | } | ||
| 522 | if (x == 0.0) { | ||
| 523 | try output(context, "0"); | ||
| 524 | |||
| 525 | if (maybe_precision) |precision| { | ||
| 526 | if (precision != 0) { | ||
| 527 | try output(context, "."); | ||
| 528 | var i: usize = 0; | ||
| 529 | while (i < precision) : (i += 1) { | ||
| 530 | try output(context, "0"); | ||
| 531 | } | ||
| 532 | } else { | ||
| 533 | try output(context, ".0"); | ||
| 534 | } | ||
| 535 | } else { | ||
| 536 | try output(context, "0"); | ||
| 537 | } | ||
| 538 | |||
| 539 | return; | ||
| 540 | } | ||
| 541 | |||
| 542 | // non-special case, use errol3 | ||
| 543 | var buffer: [32]u8 = undefined; | ||
| 544 | var float_decimal = errol.errol3(x, buffer[0..]); | ||
| 545 | |||
| 546 | if (maybe_precision) |precision| { | ||
| 547 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); | ||
| 548 | |||
| 549 | // exp < 0 means the leading is always 0 as errol result is normalized. | ||
| 550 | var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; | ||
| 551 | |||
| 552 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | ||
| 553 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | ||
| 554 | |||
| 555 | if (num_digits_whole > 0) { | ||
| 556 | // We may have to zero pad, for instance 1e4 requires zero padding. | ||
| 557 | try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); | ||
| 558 | |||
| 559 | var i = num_digits_whole_no_pad; | ||
| 560 | while (i < num_digits_whole) : (i += 1) { | ||
| 561 | try output(context, "0"); | ||
| 562 | } | ||
| 563 | } else { | ||
| 564 | try output(context, "0"); | ||
| 565 | } | ||
| 566 | |||
| 567 | // {.0} special case doesn't want a trailing '.' | ||
| 568 | if (precision == 0) { | ||
| 569 | return; | ||
| 570 | } | ||
| 571 | |||
| 572 | try output(context, "."); | ||
| 573 | |||
| 574 | // Keep track of fractional count printed for case where we pre-pad then post-pad with 0's. | ||
| 575 | var printed: usize = 0; | ||
| 576 | |||
| 577 | // Zero-fill until we reach significant digits or run out of precision. | ||
| 578 | if (float_decimal.exp <= 0) { | ||
| 579 | const zero_digit_count = @intCast(usize, -float_decimal.exp); | ||
| 580 | const zeros_to_print = math.min(zero_digit_count, precision); | ||
| 581 | |||
| 582 | var i: usize = 0; | ||
| 583 | while (i < zeros_to_print) : (i += 1) { | ||
| 584 | try output(context, "0"); | ||
| 585 | printed += 1; | ||
| 586 | } | ||
| 587 | |||
| 588 | if (printed >= precision) { | ||
| 589 | return; | ||
| 590 | } | ||
| 591 | } | ||
| 592 | |||
| 593 | // Remaining fractional portion, zero-padding if insufficient. | ||
| 594 | assert(precision >= printed); | ||
| 595 | if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) { | ||
| 596 | try output(context, float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]); | ||
| 597 | return; | ||
| 598 | } else { | ||
| 599 | try output(context, float_decimal.digits[num_digits_whole_no_pad..]); | ||
| 600 | printed += float_decimal.digits.len - num_digits_whole_no_pad; | ||
| 601 | |||
| 602 | while (printed < precision) : (printed += 1) { | ||
| 603 | try output(context, "0"); | ||
| 604 | } | ||
| 605 | } | ||
| 606 | } else { | ||
| 607 | // exp < 0 means the leading is always 0 as errol result is normalized. | ||
| 608 | var num_digits_whole = if (float_decimal.exp > 0) @intCast(usize, float_decimal.exp) else 0; | ||
| 609 | |||
| 610 | // the actual slice into the buffer, we may need to zero-pad between num_digits_whole and this. | ||
| 611 | var num_digits_whole_no_pad = math.min(num_digits_whole, float_decimal.digits.len); | ||
| 612 | |||
| 613 | if (num_digits_whole > 0) { | ||
| 614 | // We may have to zero pad, for instance 1e4 requires zero padding. | ||
| 615 | try output(context, float_decimal.digits[0..num_digits_whole_no_pad]); | ||
| 616 | |||
| 617 | var i = num_digits_whole_no_pad; | ||
| 618 | while (i < num_digits_whole) : (i += 1) { | ||
| 619 | try output(context, "0"); | ||
| 620 | } | ||
| 621 | } else { | ||
| 622 | try output(context, "0"); | ||
| 623 | } | ||
| 624 | |||
| 625 | // Omit `.` if no fractional portion | ||
| 626 | if (float_decimal.exp >= 0 and num_digits_whole_no_pad == float_decimal.digits.len) { | ||
| 627 | return; | ||
| 628 | } | ||
| 629 | |||
| 630 | try output(context, "."); | ||
| 631 | |||
| 632 | // Zero-fill until we reach significant digits or run out of precision. | ||
| 633 | if (float_decimal.exp < 0) { | ||
| 634 | const zero_digit_count = @intCast(usize, -float_decimal.exp); | ||
| 635 | |||
| 636 | var i: usize = 0; | ||
| 637 | while (i < zero_digit_count) : (i += 1) { | ||
| 638 | try output(context, "0"); | ||
| 639 | } | ||
| 640 | } | ||
| 641 | |||
| 642 | try output(context, float_decimal.digits[num_digits_whole_no_pad..]); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | |||
| 646 | pub fn formatBytes( | ||
| 647 | value: var, | ||
| 648 | width: ?usize, | ||
| 649 | comptime radix: usize, | ||
| 650 | context: var, | ||
| 651 | comptime Errors: type, | ||
| 652 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 653 | ) Errors!void { | ||
| 654 | if (value == 0) { | ||
| 655 | return output(context, "0B"); | ||
| 656 | } | ||
| 657 | |||
| 658 | const mags_si = " kMGTPEZY"; | ||
| 659 | const mags_iec = " KMGTPEZY"; | ||
| 660 | const magnitude = switch (radix) { | ||
| 661 | 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1), | ||
| 662 | 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1), | ||
| 663 | else => unreachable, | ||
| 664 | }; | ||
| 665 | const new_value = lossyCast(f64, value) / math.pow(f64, lossyCast(f64, radix), lossyCast(f64, magnitude)); | ||
| 666 | const suffix = switch (radix) { | ||
| 667 | 1000 => mags_si[magnitude], | ||
| 668 | 1024 => mags_iec[magnitude], | ||
| 669 | else => unreachable, | ||
| 670 | }; | ||
| 671 | |||
| 672 | try formatFloatDecimal(new_value, width, context, Errors, output); | ||
| 673 | |||
| 674 | if (suffix == ' ') { | ||
| 675 | return output(context, "B"); | ||
| 676 | } | ||
| 677 | |||
| 678 | const buf = switch (radix) { | ||
| 679 | 1000 => []u8{ suffix, 'B' }, | ||
| 680 | 1024 => []u8{ suffix, 'i', 'B' }, | ||
| 681 | else => unreachable, | ||
| 682 | }; | ||
| 683 | return output(context, buf); | ||
| 684 | } | ||
| 685 | |||
| 686 | pub fn formatInt( | ||
| 687 | value: var, | ||
| 688 | base: u8, | ||
| 689 | uppercase: bool, | ||
| 690 | width: usize, | ||
| 691 | context: var, | ||
| 692 | comptime Errors: type, | ||
| 693 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 694 | ) Errors!void { | ||
| 695 | if (@typeOf(value).is_signed) { | ||
| 696 | return formatIntSigned(value, base, uppercase, width, context, Errors, output); | ||
| 697 | } else { | ||
| 698 | return formatIntUnsigned(value, base, uppercase, width, context, Errors, output); | ||
| 699 | } | ||
| 700 | } | ||
| 701 | |||
| 702 | fn formatIntSigned( | ||
| 703 | value: var, | ||
| 704 | base: u8, | ||
| 705 | uppercase: bool, | ||
| 706 | width: usize, | ||
| 707 | context: var, | ||
| 708 | comptime Errors: type, | ||
| 709 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 710 | ) Errors!void { | ||
| 711 | const uint = @IntType(false, @typeOf(value).bit_count); | ||
| 712 | if (value < 0) { | ||
| 713 | const minus_sign: u8 = '-'; | ||
| 714 | try output(context, (*[1]u8)(&minus_sign)[0..]); | ||
| 715 | const new_value = @intCast(uint, -(value + 1)) + 1; | ||
| 716 | const new_width = if (width == 0) 0 else (width - 1); | ||
| 717 | return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); | ||
| 718 | } else if (width == 0) { | ||
| 719 | return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); | ||
| 720 | } else { | ||
| 721 | const plus_sign: u8 = '+'; | ||
| 722 | try output(context, (*[1]u8)(&plus_sign)[0..]); | ||
| 723 | const new_value = @intCast(uint, value); | ||
| 724 | const new_width = if (width == 0) 0 else (width - 1); | ||
| 725 | return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); | ||
| 726 | } | ||
| 727 | } | ||
| 728 | |||
| 729 | fn formatIntUnsigned( | ||
| 730 | value: var, | ||
| 731 | base: u8, | ||
| 732 | uppercase: bool, | ||
| 733 | width: usize, | ||
| 734 | context: var, | ||
| 735 | comptime Errors: type, | ||
| 736 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 737 | ) Errors!void { | ||
| 738 | // max_int_digits accounts for the minus sign. when printing an unsigned | ||
| 739 | // number we don't need to do that. | ||
| 740 | var buf: [max_int_digits - 1]u8 = undefined; | ||
| 741 | const min_int_bits = comptime math.max(@typeOf(value).bit_count, @typeOf(base).bit_count); | ||
| 742 | const MinInt = @IntType(@typeOf(value).is_signed, min_int_bits); | ||
| 743 | var a: MinInt = value; | ||
| 744 | var index: usize = buf.len; | ||
| 745 | |||
| 746 | while (true) { | ||
| 747 | const digit = a % base; | ||
| 748 | index -= 1; | ||
| 749 | buf[index] = digitToChar(@intCast(u8, digit), uppercase); | ||
| 750 | a /= base; | ||
| 751 | if (a == 0) break; | ||
| 752 | } | ||
| 753 | |||
| 754 | const digits_buf = buf[index..]; | ||
| 755 | const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; | ||
| 756 | |||
| 757 | if (padding > index) { | ||
| 758 | const zero_byte: u8 = '0'; | ||
| 759 | var leftover_padding = padding - index; | ||
| 760 | while (true) { | ||
| 761 | try output(context, (*[1]u8)(&zero_byte)[0..]); | ||
| 762 | leftover_padding -= 1; | ||
| 763 | if (leftover_padding == 0) break; | ||
| 764 | } | ||
| 765 | mem.set(u8, buf[0..index], '0'); | ||
| 766 | return output(context, buf); | ||
| 767 | } else { | ||
| 768 | const padded_buf = buf[index - padding ..]; | ||
| 769 | mem.set(u8, padded_buf[0..padding], '0'); | ||
| 770 | return output(context, padded_buf); | ||
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 | pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { | ||
| 775 | var context = FormatIntBuf{ | ||
| 776 | .out_buf = out_buf, | ||
| 777 | .index = 0, | ||
| 778 | }; | ||
| 779 | formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable; | ||
| 780 | return context.index; | ||
| 781 | } | ||
| 782 | const FormatIntBuf = struct { | ||
| 783 | out_buf: []u8, | ||
| 784 | index: usize, | ||
| 785 | }; | ||
| 786 | fn formatIntCallback(context: *FormatIntBuf, bytes: []const u8) (error{}!void) { | ||
| 787 | mem.copy(u8, context.out_buf[context.index..], bytes); | ||
| 788 | context.index += bytes.len; | ||
| 789 | } | ||
| 790 | |||
| 791 | pub fn parseInt(comptime T: type, buf: []const u8, radix: u8) !T { | ||
| 792 | if (!T.is_signed) return parseUnsigned(T, buf, radix); | ||
| 793 | if (buf.len == 0) return T(0); | ||
| 794 | if (buf[0] == '-') { | ||
| 795 | return math.negate(try parseUnsigned(T, buf[1..], radix)); | ||
| 796 | } else if (buf[0] == '+') { | ||
| 797 | return parseUnsigned(T, buf[1..], radix); | ||
| 798 | } else { | ||
| 799 | return parseUnsigned(T, buf, radix); | ||
| 800 | } | ||
| 801 | } | ||
| 802 | |||
| 803 | test "fmt.parseInt" { | ||
| 804 | testing.expect((parseInt(i32, "-10", 10) catch unreachable) == -10); | ||
| 805 | testing.expect((parseInt(i32, "+10", 10) catch unreachable) == 10); | ||
| 806 | testing.expect(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidCharacter); | ||
| 807 | testing.expect(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidCharacter); | ||
| 808 | testing.expect(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidCharacter); | ||
| 809 | testing.expect((parseInt(u8, "255", 10) catch unreachable) == 255); | ||
| 810 | testing.expect(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow); | ||
| 811 | } | ||
| 812 | |||
| 813 | const ParseUnsignedError = error{ | ||
| 814 | /// The result cannot fit in the type specified | ||
| 815 | Overflow, | ||
| 816 | |||
| 817 | /// The input had a byte that was not a digit | ||
| 818 | InvalidCharacter, | ||
| 819 | }; | ||
| 820 | |||
| 821 | pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) ParseUnsignedError!T { | ||
| 822 | var x: T = 0; | ||
| 823 | |||
| 824 | for (buf) |c| { | ||
| 825 | const digit = try charToDigit(c, radix); | ||
| 826 | |||
| 827 | if (x != 0) x = try math.mul(T, x, try math.cast(T, radix)); | ||
| 828 | x = try math.add(T, x, try math.cast(T, digit)); | ||
| 829 | } | ||
| 830 | |||
| 831 | return x; | ||
| 832 | } | ||
| 833 | |||
| 834 | test "fmt.parseUnsigned" { | ||
| 835 | testing.expect((try parseUnsigned(u16, "050124", 10)) == 50124); | ||
| 836 | testing.expect((try parseUnsigned(u16, "65535", 10)) == 65535); | ||
| 837 | testing.expectError(error.Overflow, parseUnsigned(u16, "65536", 10)); | ||
| 838 | |||
| 839 | testing.expect((try parseUnsigned(u64, "0ffffffffffffffff", 16)) == 0xffffffffffffffff); | ||
| 840 | testing.expectError(error.Overflow, parseUnsigned(u64, "10000000000000000", 16)); | ||
| 841 | |||
| 842 | testing.expect((try parseUnsigned(u32, "DeadBeef", 16)) == 0xDEADBEEF); | ||
| 843 | |||
| 844 | testing.expect((try parseUnsigned(u7, "1", 10)) == 1); | ||
| 845 | testing.expect((try parseUnsigned(u7, "1000", 2)) == 8); | ||
| 846 | |||
| 847 | testing.expectError(error.InvalidCharacter, parseUnsigned(u32, "f", 10)); | ||
| 848 | testing.expectError(error.InvalidCharacter, parseUnsigned(u8, "109", 8)); | ||
| 849 | |||
| 850 | testing.expect((try parseUnsigned(u32, "NUMBER", 36)) == 1442151747); | ||
| 851 | |||
| 852 | // these numbers should fit even though the radix itself doesn't fit in the destination type | ||
| 853 | testing.expect((try parseUnsigned(u1, "0", 10)) == 0); | ||
| 854 | testing.expect((try parseUnsigned(u1, "1", 10)) == 1); | ||
| 855 | testing.expectError(error.Overflow, parseUnsigned(u1, "2", 10)); | ||
| 856 | testing.expect((try parseUnsigned(u1, "001", 16)) == 1); | ||
| 857 | testing.expect((try parseUnsigned(u2, "3", 16)) == 3); | ||
| 858 | testing.expectError(error.Overflow, parseUnsigned(u2, "4", 16)); | ||
| 859 | } | ||
| 860 | |||
| 861 | pub const parseFloat = @import("parse_float.zig").parseFloat; | ||
| 862 | |||
| 863 | test "fmt.parseFloat" { | ||
| 864 | _ = @import("parse_float.zig"); | ||
| 865 | } | ||
| 866 | |||
| 867 | pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { | ||
| 868 | const value = switch (c) { | ||
| 869 | '0'...'9' => c - '0', | ||
| 870 | 'A'...'Z' => c - 'A' + 10, | ||
| 871 | 'a'...'z' => c - 'a' + 10, | ||
| 872 | else => return error.InvalidCharacter, | ||
| 873 | }; | ||
| 874 | |||
| 875 | if (value >= radix) return error.InvalidCharacter; | ||
| 876 | |||
| 877 | return value; | ||
| 878 | } | ||
| 879 | |||
| 880 | fn digitToChar(digit: u8, uppercase: bool) u8 { | ||
| 881 | return switch (digit) { | ||
| 882 | 0...9 => digit + '0', | ||
| 883 | 10...35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), | ||
| 884 | else => unreachable, | ||
| 885 | }; | ||
| 886 | } | ||
| 887 | |||
| 888 | const BufPrintContext = struct { | ||
| 889 | remaining: []u8, | ||
| 890 | }; | ||
| 891 | |||
| 892 | fn bufPrintWrite(context: *BufPrintContext, bytes: []const u8) !void { | ||
| 893 | if (context.remaining.len < bytes.len) return error.BufferTooSmall; | ||
| 894 | mem.copy(u8, context.remaining, bytes); | ||
| 895 | context.remaining = context.remaining[bytes.len..]; | ||
| 896 | } | ||
| 897 | |||
| 898 | pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { | ||
| 899 | var context = BufPrintContext{ .remaining = buf }; | ||
| 900 | try format(&context, error{BufferTooSmall}, bufPrintWrite, fmt, args); | ||
| 901 | return buf[0 .. buf.len - context.remaining.len]; | ||
| 902 | } | ||
| 903 | |||
| 904 | pub const AllocPrintError = error{OutOfMemory}; | ||
| 905 | |||
| 906 | pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 { | ||
| 907 | var size: usize = 0; | ||
| 908 | format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; | ||
| 909 | const buf = try allocator.alloc(u8, size); | ||
| 910 | return bufPrint(buf, fmt, args) catch |err| switch (err) { | ||
| 911 | error.BufferTooSmall => unreachable, // we just counted the size above | ||
| 912 | }; | ||
| 913 | } | ||
| 914 | |||
| 915 | fn countSize(size: *usize, bytes: []const u8) (error{}!void) { | ||
| 916 | size.* += bytes.len; | ||
| 917 | } | ||
| 918 | |||
| 919 | test "buf print int" { | ||
| 920 | var buffer: [max_int_digits]u8 = undefined; | ||
| 921 | const buf = buffer[0..]; | ||
| 922 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); | ||
| 923 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); | ||
| 924 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); | ||
| 925 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); | ||
| 926 | |||
| 927 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); | ||
| 928 | |||
| 929 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); | ||
| 930 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); | ||
| 931 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); | ||
| 932 | |||
| 933 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); | ||
| 934 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); | ||
| 935 | } | ||
| 936 | |||
| 937 | fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { | ||
| 938 | return buf[0..formatIntBuf(buf, value, base, uppercase, width)]; | ||
| 939 | } | ||
| 940 | |||
| 941 | test "parse u64 digit too big" { | ||
| 942 | _ = parseUnsigned(u64, "123a", 10) catch |err| { | ||
| 943 | if (err == error.InvalidCharacter) return; | ||
| 944 | unreachable; | ||
| 945 | }; | ||
| 946 | unreachable; | ||
| 947 | } | ||
| 948 | |||
| 949 | test "parse unsigned comptime" { | ||
| 950 | comptime { | ||
| 951 | testing.expect((try parseUnsigned(usize, "2", 10)) == 2); | ||
| 952 | } | ||
| 953 | } | ||
| 954 | |||
| 955 | test "fmt.format" { | ||
| 956 | { | ||
| 957 | const value: ?i32 = 1234; | ||
| 958 | try testFmt("optional: 1234\n", "optional: {}\n", value); | ||
| 959 | } | ||
| 960 | { | ||
| 961 | const value: ?i32 = null; | ||
| 962 | try testFmt("optional: null\n", "optional: {}\n", value); | ||
| 963 | } | ||
| 964 | { | ||
| 965 | const value: anyerror!i32 = 1234; | ||
| 966 | try testFmt("error union: 1234\n", "error union: {}\n", value); | ||
| 967 | } | ||
| 968 | { | ||
| 969 | const value: anyerror!i32 = error.InvalidChar; | ||
| 970 | try testFmt("error union: error.InvalidChar\n", "error union: {}\n", value); | ||
| 971 | } | ||
| 972 | { | ||
| 973 | const value: u3 = 0b101; | ||
| 974 | try testFmt("u3: 5\n", "u3: {}\n", value); | ||
| 975 | } | ||
| 976 | { | ||
| 977 | const value: u8 = 'a'; | ||
| 978 | try testFmt("u8: a\n", "u8: {c}\n", value); | ||
| 979 | } | ||
| 980 | { | ||
| 981 | const value: u8 = 0b1100; | ||
| 982 | try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value); | ||
| 983 | } | ||
| 984 | { | ||
| 985 | var buf1: [32]u8 = undefined; | ||
| 986 | var context = BufPrintContext{ .remaining = buf1[0..] }; | ||
| 987 | try formatType(1234, "", &context, error{BufferTooSmall}, bufPrintWrite); | ||
| 988 | var res = buf1[0 .. buf1.len - context.remaining.len]; | ||
| 989 | testing.expect(mem.eql(u8, res, "1234")); | ||
| 990 | |||
| 991 | context = BufPrintContext{ .remaining = buf1[0..] }; | ||
| 992 | try formatType('a', "c", &context, error{BufferTooSmall}, bufPrintWrite); | ||
| 993 | res = buf1[0 .. buf1.len - context.remaining.len]; | ||
| 994 | testing.expect(mem.eql(u8, res, "a")); | ||
| 995 | |||
| 996 | context = BufPrintContext{ .remaining = buf1[0..] }; | ||
| 997 | try formatType(0b1100, "b", &context, error{BufferTooSmall}, bufPrintWrite); | ||
| 998 | res = buf1[0 .. buf1.len - context.remaining.len]; | ||
| 999 | testing.expect(mem.eql(u8, res, "1100")); | ||
| 1000 | } | ||
| 1001 | { | ||
| 1002 | const value: [3]u8 = "abc"; | ||
| 1003 | try testFmt("array: abc\n", "array: {}\n", value); | ||
| 1004 | try testFmt("array: abc\n", "array: {}\n", &value); | ||
| 1005 | |||
| 1006 | var buf: [100]u8 = undefined; | ||
| 1007 | try testFmt( | ||
| 1008 | try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)), | ||
| 1009 | "array: {*}\n", | ||
| 1010 | &value, | ||
| 1011 | ); | ||
| 1012 | } | ||
| 1013 | { | ||
| 1014 | const value: []const u8 = "abc"; | ||
| 1015 | try testFmt("slice: abc\n", "slice: {}\n", value); | ||
| 1016 | } | ||
| 1017 | { | ||
| 1018 | const value = @intToPtr(*i32, 0xdeadbeef); | ||
| 1019 | try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value); | ||
| 1020 | try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value); | ||
| 1021 | } | ||
| 1022 | { | ||
| 1023 | const value = @intToPtr(fn () void, 0xdeadbeef); | ||
| 1024 | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); | ||
| 1025 | } | ||
| 1026 | { | ||
| 1027 | const value = @intToPtr(fn () void, 0xdeadbeef); | ||
| 1028 | try testFmt("pointer: fn() void@deadbeef\n", "pointer: {}\n", value); | ||
| 1029 | } | ||
| 1030 | try testFmt("buf: Test \n", "buf: {s5}\n", "Test"); | ||
| 1031 | try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test"); | ||
| 1032 | try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C"); | ||
| 1033 | try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C"); | ||
| 1034 | try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); | ||
| 1035 | try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); | ||
| 1036 | { | ||
| 1037 | const Struct = struct { | ||
| 1038 | field: u8, | ||
| 1039 | }; | ||
| 1040 | const value = Struct{ .field = 42 }; | ||
| 1041 | try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", value); | ||
| 1042 | try testFmt("struct: Struct{ .field = 42 }\n", "struct: {}\n", &value); | ||
| 1043 | } | ||
| 1044 | { | ||
| 1045 | const Struct = struct { | ||
| 1046 | a: u0, | ||
| 1047 | b: u1, | ||
| 1048 | }; | ||
| 1049 | const value = Struct{ .a = 0, .b = 1 }; | ||
| 1050 | try testFmt("struct: Struct{ .a = 0, .b = 1 }\n", "struct: {}\n", value); | ||
| 1051 | } | ||
| 1052 | { | ||
| 1053 | const Enum = enum { | ||
| 1054 | One, | ||
| 1055 | Two, | ||
| 1056 | }; | ||
| 1057 | const value = Enum.Two; | ||
| 1058 | try testFmt("enum: Enum.Two\n", "enum: {}\n", value); | ||
| 1059 | try testFmt("enum: Enum.Two\n", "enum: {}\n", &value); | ||
| 1060 | } | ||
| 1061 | { | ||
| 1062 | var buf1: [32]u8 = undefined; | ||
| 1063 | const value: f32 = 1.34; | ||
| 1064 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | ||
| 1065 | testing.expect(mem.eql(u8, result, "f32: 1.34000003e+00\n")); | ||
| 1066 | } | ||
| 1067 | { | ||
| 1068 | var buf1: [32]u8 = undefined; | ||
| 1069 | const value: f32 = 12.34; | ||
| 1070 | const result = try bufPrint(buf1[0..], "f32: {e}\n", value); | ||
| 1071 | testing.expect(mem.eql(u8, result, "f32: 1.23400001e+01\n")); | ||
| 1072 | } | ||
| 1073 | { | ||
| 1074 | var buf1: [32]u8 = undefined; | ||
| 1075 | const value: f64 = -12.34e10; | ||
| 1076 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | ||
| 1077 | testing.expect(mem.eql(u8, result, "f64: -1.234e+11\n")); | ||
| 1078 | } | ||
| 1079 | { | ||
| 1080 | // This fails on release due to a minor rounding difference. | ||
| 1081 | // --release-fast outputs 9.999960000000001e-40 vs. the expected. | ||
| 1082 | // TODO fix this, it should be the same in Debug and ReleaseFast | ||
| 1083 | if (builtin.mode == builtin.Mode.Debug) { | ||
| 1084 | var buf1: [32]u8 = undefined; | ||
| 1085 | const value: f64 = 9.999960e-40; | ||
| 1086 | const result = try bufPrint(buf1[0..], "f64: {e}\n", value); | ||
| 1087 | testing.expect(mem.eql(u8, result, "f64: 9.99996e-40\n")); | ||
| 1088 | } | ||
| 1089 | } | ||
| 1090 | { | ||
| 1091 | var buf1: [32]u8 = undefined; | ||
| 1092 | const value: f64 = 1.409706e-42; | ||
| 1093 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1094 | testing.expect(mem.eql(u8, result, "f64: 1.40971e-42\n")); | ||
| 1095 | } | ||
| 1096 | { | ||
| 1097 | var buf1: [32]u8 = undefined; | ||
| 1098 | const value: f64 = @bitCast(f32, u32(814313563)); | ||
| 1099 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1100 | testing.expect(mem.eql(u8, result, "f64: 1.00000e-09\n")); | ||
| 1101 | } | ||
| 1102 | { | ||
| 1103 | var buf1: [32]u8 = undefined; | ||
| 1104 | const value: f64 = @bitCast(f32, u32(1006632960)); | ||
| 1105 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1106 | testing.expect(mem.eql(u8, result, "f64: 7.81250e-03\n")); | ||
| 1107 | } | ||
| 1108 | { | ||
| 1109 | // libc rounds 1.000005e+05 to 1.00000e+05 but zig does 1.00001e+05. | ||
| 1110 | // In fact, libc doesn't round a lot of 5 cases up when one past the precision point. | ||
| 1111 | var buf1: [32]u8 = undefined; | ||
| 1112 | const value: f64 = @bitCast(f32, u32(1203982400)); | ||
| 1113 | const result = try bufPrint(buf1[0..], "f64: {e5}\n", value); | ||
| 1114 | testing.expect(mem.eql(u8, result, "f64: 1.00001e+05\n")); | ||
| 1115 | } | ||
| 1116 | { | ||
| 1117 | var buf1: [32]u8 = undefined; | ||
| 1118 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.nan_f64); | ||
| 1119 | testing.expect(mem.eql(u8, result, "f64: nan\n")); | ||
| 1120 | } | ||
| 1121 | if (builtin.arch != builtin.Arch.arm) { | ||
| 1122 | // negative nan is not defined by IEE 754, | ||
| 1123 | // and ARM thus normalizes it to positive nan | ||
| 1124 | var buf1: [32]u8 = undefined; | ||
| 1125 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.nan_f64); | ||
| 1126 | testing.expect(mem.eql(u8, result, "f64: -nan\n")); | ||
| 1127 | } | ||
| 1128 | { | ||
| 1129 | var buf1: [32]u8 = undefined; | ||
| 1130 | const result = try bufPrint(buf1[0..], "f64: {}\n", math.inf_f64); | ||
| 1131 | testing.expect(mem.eql(u8, result, "f64: inf\n")); | ||
| 1132 | } | ||
| 1133 | { | ||
| 1134 | var buf1: [32]u8 = undefined; | ||
| 1135 | const result = try bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64); | ||
| 1136 | testing.expect(mem.eql(u8, result, "f64: -inf\n")); | ||
| 1137 | } | ||
| 1138 | { | ||
| 1139 | var buf1: [64]u8 = undefined; | ||
| 1140 | const value: f64 = 1.52314e+29; | ||
| 1141 | const result = try bufPrint(buf1[0..], "f64: {.}\n", value); | ||
| 1142 | testing.expect(mem.eql(u8, result, "f64: 152314000000000000000000000000\n")); | ||
| 1143 | } | ||
| 1144 | { | ||
| 1145 | var buf1: [32]u8 = undefined; | ||
| 1146 | const value: f32 = 1.1234; | ||
| 1147 | const result = try bufPrint(buf1[0..], "f32: {.1}\n", value); | ||
| 1148 | testing.expect(mem.eql(u8, result, "f32: 1.1\n")); | ||
| 1149 | } | ||
| 1150 | { | ||
| 1151 | var buf1: [32]u8 = undefined; | ||
| 1152 | const value: f32 = 1234.567; | ||
| 1153 | const result = try bufPrint(buf1[0..], "f32: {.2}\n", value); | ||
| 1154 | testing.expect(mem.eql(u8, result, "f32: 1234.57\n")); | ||
| 1155 | } | ||
| 1156 | { | ||
| 1157 | var buf1: [32]u8 = undefined; | ||
| 1158 | const value: f32 = -11.1234; | ||
| 1159 | const result = try bufPrint(buf1[0..], "f32: {.4}\n", value); | ||
| 1160 | // -11.1234 is converted to f64 -11.12339... internally (errol3() function takes f64). | ||
| 1161 | // -11.12339... is rounded back up to -11.1234 | ||
| 1162 | testing.expect(mem.eql(u8, result, "f32: -11.1234\n")); | ||
| 1163 | } | ||
| 1164 | { | ||
| 1165 | var buf1: [32]u8 = undefined; | ||
| 1166 | const value: f32 = 91.12345; | ||
| 1167 | const result = try bufPrint(buf1[0..], "f32: {.5}\n", value); | ||
| 1168 | testing.expect(mem.eql(u8, result, "f32: 91.12345\n")); | ||
| 1169 | } | ||
| 1170 | { | ||
| 1171 | var buf1: [32]u8 = undefined; | ||
| 1172 | const value: f64 = 91.12345678901235; | ||
| 1173 | const result = try bufPrint(buf1[0..], "f64: {.10}\n", value); | ||
| 1174 | testing.expect(mem.eql(u8, result, "f64: 91.1234567890\n")); | ||
| 1175 | } | ||
| 1176 | { | ||
| 1177 | var buf1: [32]u8 = undefined; | ||
| 1178 | const value: f64 = 0.0; | ||
| 1179 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1180 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | ||
| 1181 | } | ||
| 1182 | { | ||
| 1183 | var buf1: [32]u8 = undefined; | ||
| 1184 | const value: f64 = 5.700; | ||
| 1185 | const result = try bufPrint(buf1[0..], "f64: {.0}\n", value); | ||
| 1186 | testing.expect(mem.eql(u8, result, "f64: 6\n")); | ||
| 1187 | } | ||
| 1188 | { | ||
| 1189 | var buf1: [32]u8 = undefined; | ||
| 1190 | const value: f64 = 9.999; | ||
| 1191 | const result = try bufPrint(buf1[0..], "f64: {.1}\n", value); | ||
| 1192 | testing.expect(mem.eql(u8, result, "f64: 10.0\n")); | ||
| 1193 | } | ||
| 1194 | { | ||
| 1195 | var buf1: [32]u8 = undefined; | ||
| 1196 | const value: f64 = 1.0; | ||
| 1197 | const result = try bufPrint(buf1[0..], "f64: {.3}\n", value); | ||
| 1198 | testing.expect(mem.eql(u8, result, "f64: 1.000\n")); | ||
| 1199 | } | ||
| 1200 | { | ||
| 1201 | var buf1: [32]u8 = undefined; | ||
| 1202 | const value: f64 = 0.0003; | ||
| 1203 | const result = try bufPrint(buf1[0..], "f64: {.8}\n", value); | ||
| 1204 | testing.expect(mem.eql(u8, result, "f64: 0.00030000\n")); | ||
| 1205 | } | ||
| 1206 | { | ||
| 1207 | var buf1: [32]u8 = undefined; | ||
| 1208 | const value: f64 = 1.40130e-45; | ||
| 1209 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1210 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | ||
| 1211 | } | ||
| 1212 | { | ||
| 1213 | var buf1: [32]u8 = undefined; | ||
| 1214 | const value: f64 = 9.999960e-40; | ||
| 1215 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1216 | testing.expect(mem.eql(u8, result, "f64: 0.00000\n")); | ||
| 1217 | } | ||
| 1218 | // libc checks | ||
| 1219 | { | ||
| 1220 | var buf1: [32]u8 = undefined; | ||
| 1221 | const value: f64 = f64(@bitCast(f32, u32(916964781))); | ||
| 1222 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1223 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | ||
| 1224 | } | ||
| 1225 | { | ||
| 1226 | var buf1: [32]u8 = undefined; | ||
| 1227 | const value: f64 = f64(@bitCast(f32, u32(925353389))); | ||
| 1228 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1229 | testing.expect(mem.eql(u8, result, "f64: 0.00001\n")); | ||
| 1230 | } | ||
| 1231 | { | ||
| 1232 | var buf1: [32]u8 = undefined; | ||
| 1233 | const value: f64 = f64(@bitCast(f32, u32(1036831278))); | ||
| 1234 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1235 | testing.expect(mem.eql(u8, result, "f64: 0.10000\n")); | ||
| 1236 | } | ||
| 1237 | { | ||
| 1238 | var buf1: [32]u8 = undefined; | ||
| 1239 | const value: f64 = f64(@bitCast(f32, u32(1065353133))); | ||
| 1240 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1241 | testing.expect(mem.eql(u8, result, "f64: 1.00000\n")); | ||
| 1242 | } | ||
| 1243 | { | ||
| 1244 | var buf1: [32]u8 = undefined; | ||
| 1245 | const value: f64 = f64(@bitCast(f32, u32(1092616192))); | ||
| 1246 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1247 | testing.expect(mem.eql(u8, result, "f64: 10.00000\n")); | ||
| 1248 | } | ||
| 1249 | // libc differences | ||
| 1250 | { | ||
| 1251 | var buf1: [32]u8 = undefined; | ||
| 1252 | // This is 0.015625 exactly according to gdb. We thus round down, | ||
| 1253 | // however glibc rounds up for some reason. This occurs for all | ||
| 1254 | // floats of the form x.yyyy25 on a precision point. | ||
| 1255 | const value: f64 = f64(@bitCast(f32, u32(1015021568))); | ||
| 1256 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1257 | testing.expect(mem.eql(u8, result, "f64: 0.01563\n")); | ||
| 1258 | } | ||
| 1259 | // std-windows-x86_64-Debug-bare test case fails | ||
| 1260 | { | ||
| 1261 | // errol3 rounds to ... 630 but libc rounds to ...632. Grisu3 | ||
| 1262 | // also rounds to 630 so I'm inclined to believe libc is not | ||
| 1263 | // optimal here. | ||
| 1264 | var buf1: [32]u8 = undefined; | ||
| 1265 | const value: f64 = f64(@bitCast(f32, u32(1518338049))); | ||
| 1266 | const result = try bufPrint(buf1[0..], "f64: {.5}\n", value); | ||
| 1267 | testing.expect(mem.eql(u8, result, "f64: 18014400656965630.00000\n")); | ||
| 1268 | } | ||
| 1269 | //custom type format | ||
| 1270 | { | ||
| 1271 | const Vec2 = struct { | ||
| 1272 | const SelfType = @This(); | ||
| 1273 | x: f32, | ||
| 1274 | y: f32, | ||
| 1275 | |||
| 1276 | pub fn format( | ||
| 1277 | self: SelfType, | ||
| 1278 | comptime fmt: []const u8, | ||
| 1279 | context: var, | ||
| 1280 | comptime Errors: type, | ||
| 1281 | output: fn (@typeOf(context), []const u8) Errors!void, | ||
| 1282 | ) Errors!void { | ||
| 1283 | switch (fmt.len) { | ||
| 1284 | 0 => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | ||
| 1285 | 1 => switch (fmt[0]) { | ||
| 1286 | //point format | ||
| 1287 | 'p' => return std.fmt.format(context, Errors, output, "({.3},{.3})", self.x, self.y), | ||
| 1288 | //dimension format | ||
| 1289 | 'd' => return std.fmt.format(context, Errors, output, "{.3}x{.3}", self.x, self.y), | ||
| 1290 | else => unreachable, | ||
| 1291 | }, | ||
| 1292 | else => unreachable, | ||
| 1293 | } | ||
| 1294 | } | ||
| 1295 | }; | ||
| 1296 | |||
| 1297 | var buf1: [32]u8 = undefined; | ||
| 1298 | var value = Vec2{ | ||
| 1299 | .x = 10.2, | ||
| 1300 | .y = 2.22, | ||
| 1301 | }; | ||
| 1302 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", &value); | ||
| 1303 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", &value); | ||
| 1304 | |||
| 1305 | // same thing but not passing a pointer | ||
| 1306 | try testFmt("point: (10.200,2.220)\n", "point: {}\n", value); | ||
| 1307 | try testFmt("dim: 10.200x2.220\n", "dim: {d}\n", value); | ||
| 1308 | } | ||
| 1309 | //struct format | ||
| 1310 | { | ||
| 1311 | const S = struct { | ||
| 1312 | a: u32, | ||
| 1313 | b: anyerror, | ||
| 1314 | }; | ||
| 1315 | |||
| 1316 | const inst = S{ | ||
| 1317 | .a = 456, | ||
| 1318 | .b = error.Unused, | ||
| 1319 | }; | ||
| 1320 | |||
| 1321 | try testFmt("S{ .a = 456, .b = error.Unused }", "{}", inst); | ||
| 1322 | } | ||
| 1323 | //union format | ||
| 1324 | { | ||
| 1325 | const TU = union(enum) { | ||
| 1326 | float: f32, | ||
| 1327 | int: u32, | ||
| 1328 | }; | ||
| 1329 | |||
| 1330 | const UU = union { | ||
| 1331 | float: f32, | ||
| 1332 | int: u32, | ||
| 1333 | }; | ||
| 1334 | |||
| 1335 | const EU = extern union { | ||
| 1336 | float: f32, | ||
| 1337 | int: u32, | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | const tu_inst = TU{ .int = 123 }; | ||
| 1341 | const uu_inst = UU{ .int = 456 }; | ||
| 1342 | const eu_inst = EU{ .float = 321.123 }; | ||
| 1343 | |||
| 1344 | try testFmt("TU{ .int = 123 }", "{}", tu_inst); | ||
| 1345 | |||
| 1346 | var buf: [100]u8 = undefined; | ||
| 1347 | const uu_result = try bufPrint(buf[0..], "{}", uu_inst); | ||
| 1348 | testing.expect(mem.eql(u8, uu_result[0..3], "UU@")); | ||
| 1349 | |||
| 1350 | const eu_result = try bufPrint(buf[0..], "{}", eu_inst); | ||
| 1351 | testing.expect(mem.eql(u8, uu_result[0..3], "EU@")); | ||
| 1352 | } | ||
| 1353 | //enum format | ||
| 1354 | { | ||
| 1355 | const E = enum { | ||
| 1356 | One, | ||
| 1357 | Two, | ||
| 1358 | Three, | ||
| 1359 | }; | ||
| 1360 | |||
| 1361 | const inst = E.Two; | ||
| 1362 | |||
| 1363 | try testFmt("E.Two", "{}", inst); | ||
| 1364 | } | ||
| 1365 | //print bytes as hex | ||
| 1366 | { | ||
| 1367 | const some_bytes = "\xCA\xFE\xBA\xBE"; | ||
| 1368 | try testFmt("lowercase: cafebabe\n", "lowercase: {x}\n", some_bytes); | ||
| 1369 | try testFmt("uppercase: CAFEBABE\n", "uppercase: {X}\n", some_bytes); | ||
| 1370 | //Test Slices | ||
| 1371 | try testFmt("uppercase: CAFE\n", "uppercase: {X}\n", some_bytes[0..2]); | ||
| 1372 | try testFmt("lowercase: babe\n", "lowercase: {x}\n", some_bytes[2..]); | ||
| 1373 | const bytes_with_zeros = "\x00\x0E\xBA\xBE"; | ||
| 1374 | try testFmt("lowercase: 000ebabe\n", "lowercase: {x}\n", bytes_with_zeros); | ||
| 1375 | } | ||
| 1376 | } | ||
| 1377 | |||
| 1378 | fn testFmt(expected: []const u8, comptime template: []const u8, args: ...) !void { | ||
| 1379 | var buf: [100]u8 = undefined; | ||
| 1380 | const result = try bufPrint(buf[0..], template, args); | ||
| 1381 | if (mem.eql(u8, result, expected)) return; | ||
| 1382 | |||
| 1383 | std.debug.warn("\n====== expected this output: =========\n"); | ||
| 1384 | std.debug.warn("{}", expected); | ||
| 1385 | std.debug.warn("\n======== instead found this: =========\n"); | ||
| 1386 | std.debug.warn("{}", result); | ||
| 1387 | std.debug.warn("\n======================================\n"); | ||
| 1388 | return error.TestFailed; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | pub fn trim(buf: []const u8) []const u8 { | ||
| 1392 | var start: usize = 0; | ||
| 1393 | while (start < buf.len and isWhiteSpace(buf[start])) : (start += 1) {} | ||
| 1394 | |||
| 1395 | var end: usize = buf.len; | ||
| 1396 | while (true) { | ||
| 1397 | if (end > start) { | ||
| 1398 | const new_end = end - 1; | ||
| 1399 | if (isWhiteSpace(buf[new_end])) { | ||
| 1400 | end = new_end; | ||
| 1401 | continue; | ||
| 1402 | } | ||
| 1403 | } | ||
| 1404 | break; | ||
| 1405 | } | ||
| 1406 | return buf[start..end]; | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | test "fmt.trim" { | ||
| 1410 | testing.expect(mem.eql(u8, "abc", trim("\n abc \t"))); | ||
| 1411 | testing.expect(mem.eql(u8, "", trim(" "))); | ||
| 1412 | testing.expect(mem.eql(u8, "", trim(""))); | ||
| 1413 | testing.expect(mem.eql(u8, "abc", trim(" abc"))); | ||
| 1414 | testing.expect(mem.eql(u8, "abc", trim("abc "))); | ||
| 1415 | } | ||
| 1416 | |||
| 1417 | pub fn isWhiteSpace(byte: u8) bool { | ||
| 1418 | return switch (byte) { | ||
| 1419 | ' ', '\t', '\n', '\r' => true, | ||
| 1420 | else => false, | ||
| 1421 | }; | ||
| 1422 | } | ||
| 1423 | |||
| 1424 | pub fn hexToBytes(out: []u8, input: []const u8) !void { | ||
| 1425 | if (out.len * 2 < input.len) | ||
| 1426 | return error.InvalidLength; | ||
| 1427 | |||
| 1428 | var in_i: usize = 0; | ||
| 1429 | while (in_i != input.len) : (in_i += 2) { | ||
| 1430 | const hi = try charToDigit(input[in_i], 16); | ||
| 1431 | const lo = try charToDigit(input[in_i + 1], 16); | ||
| 1432 | out[in_i / 2] = (hi << 4) | lo; | ||
| 1433 | } | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | test "fmt.hexToBytes" { | ||
| 1437 | const test_hex_str = "909A312BB12ED1F819B3521AC4C1E896F2160507FFC1C8381E3B07BB16BD1706"; | ||
| 1438 | var pb: [32]u8 = undefined; | ||
| 1439 | try hexToBytes(pb[0..], test_hex_str); | ||
| 1440 | try testFmt(test_hex_str, "{X}", pb); | ||
| 1441 | } | ||
std/fmt/parse_float.zig+1-1| ... | @@ -29,7 +29,7 @@ | ... | @@ -29,7 +29,7 @@ |
| 29 | // - Only supports round-to-zero | 29 | // - Only supports round-to-zero |
| 30 | // - Does not handle denormals | 30 | // - Does not handle denormals |
| 31 | 31 | ||
| 32 | const std = @import("../index.zig"); | 32 | const std = @import("../std.zig"); |
| 33 | 33 | ||
| 34 | const max_digits = 25; | 34 | const max_digits = 25; |
| 35 | 35 |
std/hash.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | const adler = @import("hash/adler.zig"); | ||
| 2 | pub const Adler32 = adler.Adler32; | ||
| 3 | |||
| 4 | // pub for polynomials + generic crc32 construction | ||
| 5 | pub const crc = @import("hash/crc.zig"); | ||
| 6 | pub const Crc32 = crc.Crc32; | ||
| 7 | |||
| 8 | const fnv = @import("hash/fnv.zig"); | ||
| 9 | pub const Fnv1a_32 = fnv.Fnv1a_32; | ||
| 10 | pub const Fnv1a_64 = fnv.Fnv1a_64; | ||
| 11 | pub const Fnv1a_128 = fnv.Fnv1a_128; | ||
| 12 | |||
| 13 | const siphash = @import("hash/siphash.zig"); | ||
| 14 | pub const SipHash64 = siphash.SipHash64; | ||
| 15 | pub const SipHash128 = siphash.SipHash128; | ||
| 16 | |||
| 17 | test "hash" { | ||
| 18 | _ = @import("hash/adler.zig"); | ||
| 19 | _ = @import("hash/crc.zig"); | ||
| 20 | _ = @import("hash/fnv.zig"); | ||
| 21 | _ = @import("hash/siphash.zig"); | ||
| 22 | } | ||
std/hash/adler.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // https://tools.ietf.org/html/rfc1950#section-9 | 3 | // https://tools.ietf.org/html/rfc1950#section-9 |
| 4 | // https://github.com/madler/zlib/blob/master/adler32.c | 4 | // https://github.com/madler/zlib/blob/master/adler32.c |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const testing = std.testing; | 7 | const testing = std.testing; |
| 8 | 8 | ||
| 9 | pub const Adler32 = struct { | 9 | pub const Adler32 = struct { |
std/hash/crc.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is | 5 | // - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is |
| 6 | // still moderately fast just slow relative to the slicing approach. | 6 | // still moderately fast just slow relative to the slicing approach. |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const debug = std.debug; | 9 | const debug = std.debug; |
| 10 | const testing = std.testing; | 10 | const testing = std.testing; |
| 11 | 11 |
std/hash/fnv.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // | 4 | // |
| 5 | // https://tools.ietf.org/html/draft-eastlake-fnv-14 | 5 | // https://tools.ietf.org/html/draft-eastlake-fnv-14 |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const testing = std.testing; | 8 | const testing = std.testing; |
| 9 | 9 | ||
| 10 | pub const Fnv1a_32 = Fnv1a(u32, 0x01000193, 0x811c9dc5); | 10 | pub const Fnv1a_32 = Fnv1a(u32, 0x01000193, 0x811c9dc5); |
std/hash/index.zig deleted-22| ... | @@ -1,22 +0,0 @@ | ||
| 1 | const adler = @import("adler.zig"); | ||
| 2 | pub const Adler32 = adler.Adler32; | ||
| 3 | |||
| 4 | // pub for polynomials + generic crc32 construction | ||
| 5 | pub const crc = @import("crc.zig"); | ||
| 6 | pub const Crc32 = crc.Crc32; | ||
| 7 | |||
| 8 | const fnv = @import("fnv.zig"); | ||
| 9 | pub const Fnv1a_32 = fnv.Fnv1a_32; | ||
| 10 | pub const Fnv1a_64 = fnv.Fnv1a_64; | ||
| 11 | pub const Fnv1a_128 = fnv.Fnv1a_128; | ||
| 12 | |||
| 13 | const siphash = @import("siphash.zig"); | ||
| 14 | pub const SipHash64 = siphash.SipHash64; | ||
| 15 | pub const SipHash128 = siphash.SipHash128; | ||
| 16 | |||
| 17 | test "hash" { | ||
| 18 | _ = @import("adler.zig"); | ||
| 19 | _ = @import("crc.zig"); | ||
| 20 | _ = @import("fnv.zig"); | ||
| 21 | _ = @import("siphash.zig"); | ||
| 22 | } | ||
std/hash/siphash.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // | 5 | // |
| 6 | // https://131002.net/siphash/ | 6 | // https://131002.net/siphash/ |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const assert = std.debug.assert; | 9 | const assert = std.debug.assert; |
| 10 | const testing = std.testing; | 10 | const testing = std.testing; |
| 11 | const math = std.math; | 11 | const math = std.math; |
std/hash_map.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const assert = debug.assert; | 3 | const assert = debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/heap.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const assert = debug.assert; | 3 | const assert = debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/index.zig deleted-95| ... | @@ -1,95 +0,0 @@ | ||
| 1 | pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList; | ||
| 2 | pub const ArrayList = @import("array_list.zig").ArrayList; | ||
| 3 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; | ||
| 4 | pub const BufMap = @import("buf_map.zig").BufMap; | ||
| 5 | pub const BufSet = @import("buf_set.zig").BufSet; | ||
| 6 | pub const Buffer = @import("buffer.zig").Buffer; | ||
| 7 | pub const BufferOutStream = @import("io.zig").BufferOutStream; | ||
| 8 | pub const DynLib = @import("dynamic_library.zig").DynLib; | ||
| 9 | pub const HashMap = @import("hash_map.zig").HashMap; | ||
| 10 | pub const LinkedList = @import("linked_list.zig").LinkedList; | ||
| 11 | pub const Mutex = @import("mutex.zig").Mutex; | ||
| 12 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; | ||
| 13 | pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; | ||
| 14 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | ||
| 15 | pub const SpinLock = @import("spinlock.zig").SpinLock; | ||
| 16 | |||
| 17 | pub const atomic = @import("atomic/index.zig"); | ||
| 18 | pub const base64 = @import("base64.zig"); | ||
| 19 | pub const build = @import("build.zig"); | ||
| 20 | pub const c = @import("c/index.zig"); | ||
| 21 | pub const coff = @import("coff.zig"); | ||
| 22 | pub const crypto = @import("crypto/index.zig"); | ||
| 23 | pub const cstr = @import("cstr.zig"); | ||
| 24 | pub const debug = @import("debug/index.zig"); | ||
| 25 | pub const dwarf = @import("dwarf.zig"); | ||
| 26 | pub const elf = @import("elf.zig"); | ||
| 27 | pub const empty_import = @import("empty.zig"); | ||
| 28 | pub const event = @import("event.zig"); | ||
| 29 | pub const fmt = @import("fmt/index.zig"); | ||
| 30 | pub const hash = @import("hash/index.zig"); | ||
| 31 | pub const hash_map = @import("hash_map.zig"); | ||
| 32 | pub const heap = @import("heap.zig"); | ||
| 33 | pub const io = @import("io.zig"); | ||
| 34 | pub const json = @import("json.zig"); | ||
| 35 | pub const lazyInit = @import("lazy_init.zig").lazyInit; | ||
| 36 | pub const macho = @import("macho.zig"); | ||
| 37 | pub const math = @import("math/index.zig"); | ||
| 38 | pub const mem = @import("mem.zig"); | ||
| 39 | pub const meta = @import("meta/index.zig"); | ||
| 40 | pub const net = @import("net.zig"); | ||
| 41 | pub const os = @import("os/index.zig"); | ||
| 42 | pub const pdb = @import("pdb.zig"); | ||
| 43 | pub const rand = @import("rand/index.zig"); | ||
| 44 | pub const rb = @import("rb.zig"); | ||
| 45 | pub const sort = @import("sort.zig"); | ||
| 46 | pub const testing = @import("testing.zig"); | ||
| 47 | pub const unicode = @import("unicode.zig"); | ||
| 48 | pub const zig = @import("zig/index.zig"); | ||
| 49 | |||
| 50 | test "std" { | ||
| 51 | // run tests from these | ||
| 52 | _ = @import("array_list.zig"); | ||
| 53 | _ = @import("atomic/index.zig"); | ||
| 54 | _ = @import("buf_map.zig"); | ||
| 55 | _ = @import("buf_set.zig"); | ||
| 56 | _ = @import("buffer.zig"); | ||
| 57 | _ = @import("hash_map.zig"); | ||
| 58 | _ = @import("linked_list.zig"); | ||
| 59 | _ = @import("mutex.zig"); | ||
| 60 | _ = @import("statically_initialized_mutex.zig"); | ||
| 61 | _ = @import("segmented_list.zig"); | ||
| 62 | _ = @import("spinlock.zig"); | ||
| 63 | |||
| 64 | _ = @import("base64.zig"); | ||
| 65 | _ = @import("build.zig"); | ||
| 66 | _ = @import("c/index.zig"); | ||
| 67 | _ = @import("coff.zig"); | ||
| 68 | _ = @import("crypto/index.zig"); | ||
| 69 | _ = @import("cstr.zig"); | ||
| 70 | _ = @import("debug/index.zig"); | ||
| 71 | _ = @import("dwarf.zig"); | ||
| 72 | _ = @import("dynamic_library.zig"); | ||
| 73 | _ = @import("elf.zig"); | ||
| 74 | _ = @import("empty.zig"); | ||
| 75 | _ = @import("event.zig"); | ||
| 76 | _ = @import("fmt/index.zig"); | ||
| 77 | _ = @import("hash/index.zig"); | ||
| 78 | _ = @import("heap.zig"); | ||
| 79 | _ = @import("io.zig"); | ||
| 80 | _ = @import("json.zig"); | ||
| 81 | _ = @import("lazy_init.zig"); | ||
| 82 | _ = @import("macho.zig"); | ||
| 83 | _ = @import("math/index.zig"); | ||
| 84 | _ = @import("mem.zig"); | ||
| 85 | _ = @import("meta/index.zig"); | ||
| 86 | _ = @import("net.zig"); | ||
| 87 | _ = @import("os/index.zig"); | ||
| 88 | _ = @import("pdb.zig"); | ||
| 89 | _ = @import("priority_queue.zig"); | ||
| 90 | _ = @import("rand/index.zig"); | ||
| 91 | _ = @import("sort.zig"); | ||
| 92 | _ = @import("testing.zig"); | ||
| 93 | _ = @import("unicode.zig"); | ||
| 94 | _ = @import("zig/index.zig"); | ||
| 95 | } | ||
std/io.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Os = builtin.Os; | 3 | const Os = builtin.Os; |
| 4 | const c = std.c; | 4 | const c = std.c; |
std/io/seekable_stream.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const InStream = std.io.InStream; | 2 | const InStream = std.io.InStream; |
| 3 | 3 | ||
| 4 | pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { | 4 | pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { |
std/io_test.zig+15-17| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const io = std.io; | 2 | const io = std.io; |
| 3 | const meta = std.meta; | 3 | const meta = std.meta; |
| 4 | const trait = std.trait; | 4 | const trait = std.trait; |
| ... | @@ -34,8 +34,7 @@ test "write a file, read it, then delete it" { | ... | @@ -34,8 +34,7 @@ test "write a file, read it, then delete it" { |
| 34 | // make sure openWriteNoClobber doesn't harm the file | 34 | // make sure openWriteNoClobber doesn't harm the file |
| 35 | if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { | 35 | if (os.File.openWriteNoClobber(tmp_file_name, os.File.default_mode)) |file| { |
| 36 | unreachable; | 36 | unreachable; |
| 37 | } | 37 | } else |err| { |
| 38 | else |err| { | ||
| 39 | std.debug.assert(err == os.File.OpenError.PathAlreadyExists); | 38 | std.debug.assert(err == os.File.OpenError.PathAlreadyExists); |
| 40 | } | 39 | } |
| 41 | } | 40 | } |
| ... | @@ -180,7 +179,7 @@ test "BitInStream" { | ... | @@ -180,7 +179,7 @@ test "BitInStream" { |
| 180 | expect(out_bits == 16); | 179 | expect(out_bits == 16); |
| 181 | 180 | ||
| 182 | _ = try bit_stream_be.readBits(u0, 0, &out_bits); | 181 | _ = try bit_stream_be.readBits(u0, 0, &out_bits); |
| 183 | 182 | ||
| 184 | expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); | 183 | expect(0 == try bit_stream_be.readBits(u1, 1, &out_bits)); |
| 185 | expect(out_bits == 0); | 184 | expect(out_bits == 0); |
| 186 | expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); | 185 | expectError(error.EndOfStream, bit_stream_be.readBitsNoEof(u1, 1)); |
| ... | @@ -212,7 +211,7 @@ test "BitInStream" { | ... | @@ -212,7 +211,7 @@ test "BitInStream" { |
| 212 | expect(out_bits == 16); | 211 | expect(out_bits == 16); |
| 213 | 212 | ||
| 214 | _ = try bit_stream_le.readBits(u0, 0, &out_bits); | 213 | _ = try bit_stream_le.readBits(u0, 0, &out_bits); |
| 215 | 214 | ||
| 216 | expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); | 215 | expect(0 == try bit_stream_le.readBits(u1, 1, &out_bits)); |
| 217 | expect(out_bits == 0); | 216 | expect(out_bits == 0); |
| 218 | expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); | 217 | expectError(error.EndOfStream, bit_stream_le.readBitsNoEof(u1, 1)); |
| ... | @@ -281,7 +280,7 @@ test "BitStreams with File Stream" { | ... | @@ -281,7 +280,7 @@ test "BitStreams with File Stream" { |
| 281 | var file_out_stream = &file_out.stream; | 280 | var file_out_stream = &file_out.stream; |
| 282 | const OutError = os.File.WriteError; | 281 | const OutError = os.File.WriteError; |
| 283 | var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); | 282 | var bit_stream = io.BitOutStream(builtin.endian, OutError).init(file_out_stream); |
| 284 | 283 | ||
| 285 | try bit_stream.writeBits(u2(1), 1); | 284 | try bit_stream.writeBits(u2(1), 1); |
| 286 | try bit_stream.writeBits(u5(2), 2); | 285 | try bit_stream.writeBits(u5(2), 2); |
| 287 | try bit_stream.writeBits(u128(3), 3); | 286 | try bit_stream.writeBits(u128(3), 3); |
| ... | @@ -298,7 +297,7 @@ test "BitStreams with File Stream" { | ... | @@ -298,7 +297,7 @@ test "BitStreams with File Stream" { |
| 298 | var file_in_stream = &file_in.stream; | 297 | var file_in_stream = &file_in.stream; |
| 299 | const InError = os.File.ReadError; | 298 | const InError = os.File.ReadError; |
| 300 | var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); | 299 | var bit_stream = io.BitInStream(builtin.endian, InError).init(file_in_stream); |
| 301 | 300 | ||
| 302 | var out_bits: usize = undefined; | 301 | var out_bits: usize = undefined; |
| 303 | 302 | ||
| 304 | expect(1 == try bit_stream.readBits(u2, 1, &out_bits)); | 303 | expect(1 == try bit_stream.readBits(u2, 1, &out_bits)); |
| ... | @@ -313,7 +312,7 @@ test "BitStreams with File Stream" { | ... | @@ -313,7 +312,7 @@ test "BitStreams with File Stream" { |
| 313 | expect(out_bits == 5); | 312 | expect(out_bits == 5); |
| 314 | expect(1 == try bit_stream.readBits(u1, 1, &out_bits)); | 313 | expect(1 == try bit_stream.readBits(u1, 1, &out_bits)); |
| 315 | expect(out_bits == 1); | 314 | expect(out_bits == 1); |
| 316 | 315 | ||
| 317 | expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); | 316 | expectError(error.EndOfStream, bit_stream.readBitsNoEof(u1, 1)); |
| 318 | } | 317 | } |
| 319 | try os.deleteFile(tmp_file_name); | 318 | try os.deleteFile(tmp_file_name); |
| ... | @@ -322,14 +321,14 @@ test "BitStreams with File Stream" { | ... | @@ -322,14 +321,14 @@ test "BitStreams with File Stream" { |
| 322 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { | 321 | fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packed: bool) !void { |
| 323 | //@NOTE: if this test is taking too long, reduce the maximum tested bitsize | 322 | //@NOTE: if this test is taking too long, reduce the maximum tested bitsize |
| 324 | const max_test_bitsize = 128; | 323 | const max_test_bitsize = 128; |
| 325 | 324 | ||
| 326 | const total_bytes = comptime blk: { | 325 | const total_bytes = comptime blk: { |
| 327 | var bytes = 0; | 326 | var bytes = 0; |
| 328 | comptime var i = 0; | 327 | comptime var i = 0; |
| 329 | while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); | 328 | while (i <= max_test_bitsize) : (i += 1) bytes += (i / 8) + @boolToInt(i % 8 > 0); |
| 330 | break :blk bytes * 2; | 329 | break :blk bytes * 2; |
| 331 | }; | 330 | }; |
| 332 | 331 | ||
| 333 | var data_mem: [total_bytes]u8 = undefined; | 332 | var data_mem: [total_bytes]u8 = undefined; |
| 334 | var out = io.SliceOutStream.init(data_mem[0..]); | 333 | var out = io.SliceOutStream.init(data_mem[0..]); |
| 335 | const OutError = io.SliceOutStream.Error; | 334 | const OutError = io.SliceOutStream.Error; |
| ... | @@ -386,12 +385,13 @@ test "Serializer/Deserializer Int" { | ... | @@ -386,12 +385,13 @@ test "Serializer/Deserializer Int" { |
| 386 | try testIntSerializerDeserializer(builtin.Endian.Little, true); | 385 | try testIntSerializerDeserializer(builtin.Endian.Little, true); |
| 387 | } | 386 | } |
| 388 | 387 | ||
| 389 | fn testIntSerializerDeserializerInfNaN(comptime endian: builtin.Endian, | 388 | fn testIntSerializerDeserializerInfNaN( |
| 390 | comptime is_packed: bool) !void | 389 | comptime endian: builtin.Endian, |
| 391 | { | 390 | comptime is_packed: bool, |
| 392 | const mem_size = (16*2 + 32*2 + 64*2 + 128*2) / comptime meta.bitCount(u8); | 391 | ) !void { |
| 392 | const mem_size = (16 * 2 + 32 * 2 + 64 * 2 + 128 * 2) / comptime meta.bitCount(u8); | ||
| 393 | var data_mem: [mem_size]u8 = undefined; | 393 | var data_mem: [mem_size]u8 = undefined; |
| 394 | 394 | ||
| 395 | var out = io.SliceOutStream.init(data_mem[0..]); | 395 | var out = io.SliceOutStream.init(data_mem[0..]); |
| 396 | const OutError = io.SliceOutStream.Error; | 396 | const OutError = io.SliceOutStream.Error; |
| 397 | var out_stream = &out.stream; | 397 | var out_stream = &out.stream; |
| ... | @@ -472,8 +472,6 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe | ... | @@ -472,8 +472,6 @@ fn testSerializerDeserializer(comptime endian: builtin.Endian, comptime is_packe |
| 472 | f_u2: u2, | 472 | f_u2: u2, |
| 473 | }; | 473 | }; |
| 474 | 474 | ||
| 475 | |||
| 476 | |||
| 477 | //to test custom serialization | 475 | //to test custom serialization |
| 478 | const Custom = struct { | 476 | const Custom = struct { |
| 479 | f_f16: f16, | 477 | f_f16: f16, |
std/json.zig+1-1| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | // | 2 | // |
| 3 | // https://tools.ietf.org/html/rfc8259 | 3 | // https://tools.ietf.org/html/rfc8259 |
| 4 | 4 | ||
| 5 | const std = @import("index.zig"); | 5 | const std = @import("std.zig"); |
| 6 | const debug = std.debug; | 6 | const debug = std.debug; |
| 7 | const testing = std.testing; | 7 | const testing = std.testing; |
| 8 | const mem = std.mem; | 8 | const mem = std.mem; |
std/json_test.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // Tests are taken from https://github.com/nst/JSONTestSuite | 3 | // Tests are taken from https://github.com/nst/JSONTestSuite |
| 4 | // Read also http://seriot.ch/parsing_json.php for a good overview. | 4 | // Read also http://seriot.ch/parsing_json.php for a good overview. |
| 5 | 5 | ||
| 6 | const std = @import("index.zig"); | 6 | const std = @import("std.zig"); |
| 7 | 7 | ||
| 8 | fn ok(comptime s: []const u8) void { | 8 | fn ok(comptime s: []const u8) void { |
| 9 | std.testing.expect(std.json.validate(s)); | 9 | std.testing.expect(std.json.validate(s)); |
std/lazy_init.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/linked_list.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const assert = debug.assert; | 3 | const assert = debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/math.zig created+797| ... | @@ -0,0 +1,797 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std.zig"); | ||
| 3 | const TypeId = builtin.TypeId; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const testing = std.testing; | ||
| 6 | |||
| 7 | pub const e = 2.71828182845904523536028747135266249775724709369995; | ||
| 8 | pub const pi = 3.14159265358979323846264338327950288419716939937510; | ||
| 9 | |||
| 10 | // From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) | ||
| 11 | pub const f128_true_min = @bitCast(f128, u128(0x00000000000000000000000000000001)); | ||
| 12 | pub const f128_min = @bitCast(f128, u128(0x00010000000000000000000000000000)); | ||
| 13 | pub const f128_max = @bitCast(f128, u128(0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); | ||
| 14 | pub const f128_epsilon = @bitCast(f128, u128(0x3F8F0000000000000000000000000000)); | ||
| 15 | pub const f128_toint = 1.0 / f128_epsilon; | ||
| 16 | |||
| 17 | // float.h details | ||
| 18 | pub const f64_true_min = 4.94065645841246544177e-324; | ||
| 19 | pub const f64_min = 2.2250738585072014e-308; | ||
| 20 | pub const f64_max = 1.79769313486231570815e+308; | ||
| 21 | pub const f64_epsilon = 2.22044604925031308085e-16; | ||
| 22 | pub const f64_toint = 1.0 / f64_epsilon; | ||
| 23 | |||
| 24 | pub const f32_true_min = 1.40129846432481707092e-45; | ||
| 25 | pub const f32_min = 1.17549435082228750797e-38; | ||
| 26 | pub const f32_max = 3.40282346638528859812e+38; | ||
| 27 | pub const f32_epsilon = 1.1920928955078125e-07; | ||
| 28 | pub const f32_toint = 1.0 / f32_epsilon; | ||
| 29 | |||
| 30 | pub const f16_true_min = 0.000000059604644775390625; // 2**-24 | ||
| 31 | pub const f16_min = 0.00006103515625; // 2**-14 | ||
| 32 | pub const f16_max = 65504; | ||
| 33 | pub const f16_epsilon = 0.0009765625; // 2**-10 | ||
| 34 | pub const f16_toint = 1.0 / f16_epsilon; | ||
| 35 | |||
| 36 | pub const nan_u16 = u16(0x7C01); | ||
| 37 | pub const nan_f16 = @bitCast(f16, nan_u16); | ||
| 38 | |||
| 39 | pub const inf_u16 = u16(0x7C00); | ||
| 40 | pub const inf_f16 = @bitCast(f16, inf_u16); | ||
| 41 | |||
| 42 | pub const nan_u32 = u32(0x7F800001); | ||
| 43 | pub const nan_f32 = @bitCast(f32, nan_u32); | ||
| 44 | |||
| 45 | pub const inf_u32 = u32(0x7F800000); | ||
| 46 | pub const inf_f32 = @bitCast(f32, inf_u32); | ||
| 47 | |||
| 48 | pub const nan_u64 = u64(0x7FF << 52) | 1; | ||
| 49 | pub const nan_f64 = @bitCast(f64, nan_u64); | ||
| 50 | |||
| 51 | pub const inf_u64 = u64(0x7FF << 52); | ||
| 52 | pub const inf_f64 = @bitCast(f64, inf_u64); | ||
| 53 | |||
| 54 | pub const nan_u128 = u128(0x7fff0000000000000000000000000001); | ||
| 55 | pub const nan_f128 = @bitCast(f128, nan_u128); | ||
| 56 | |||
| 57 | pub const inf_u128 = u128(0x7fff0000000000000000000000000000); | ||
| 58 | pub const inf_f128 = @bitCast(f128, inf_u128); | ||
| 59 | |||
| 60 | pub const nan = @import("math/nan.zig").nan; | ||
| 61 | pub const snan = @import("math/nan.zig").snan; | ||
| 62 | pub const inf = @import("math/inf.zig").inf; | ||
| 63 | |||
| 64 | pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { | ||
| 65 | assert(@typeId(T) == TypeId.Float); | ||
| 66 | return fabs(x - y) < epsilon; | ||
| 67 | } | ||
| 68 | |||
| 69 | // TODO: Hide the following in an internal module. | ||
| 70 | pub fn forceEval(value: var) void { | ||
| 71 | const T = @typeOf(value); | ||
| 72 | switch (T) { | ||
| 73 | f16 => { | ||
| 74 | var x: f16 = undefined; | ||
| 75 | const p = @ptrCast(*volatile f16, &x); | ||
| 76 | p.* = x; | ||
| 77 | }, | ||
| 78 | f32 => { | ||
| 79 | var x: f32 = undefined; | ||
| 80 | const p = @ptrCast(*volatile f32, &x); | ||
| 81 | p.* = x; | ||
| 82 | }, | ||
| 83 | f64 => { | ||
| 84 | var x: f64 = undefined; | ||
| 85 | const p = @ptrCast(*volatile f64, &x); | ||
| 86 | p.* = x; | ||
| 87 | }, | ||
| 88 | else => { | ||
| 89 | @compileError("forceEval not implemented for " ++ @typeName(T)); | ||
| 90 | }, | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn raiseInvalid() void { | ||
| 95 | // Raise INVALID fpu exception | ||
| 96 | } | ||
| 97 | |||
| 98 | pub fn raiseUnderflow() void { | ||
| 99 | // Raise UNDERFLOW fpu exception | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn raiseOverflow() void { | ||
| 103 | // Raise OVERFLOW fpu exception | ||
| 104 | } | ||
| 105 | |||
| 106 | pub fn raiseInexact() void { | ||
| 107 | // Raise INEXACT fpu exception | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn raiseDivByZero() void { | ||
| 111 | // Raise INEXACT fpu exception | ||
| 112 | } | ||
| 113 | |||
| 114 | pub const isNan = @import("math/isnan.zig").isNan; | ||
| 115 | pub const isSignalNan = @import("math/isnan.zig").isSignalNan; | ||
| 116 | pub const fabs = @import("math/fabs.zig").fabs; | ||
| 117 | pub const ceil = @import("math/ceil.zig").ceil; | ||
| 118 | pub const floor = @import("math/floor.zig").floor; | ||
| 119 | pub const trunc = @import("math/trunc.zig").trunc; | ||
| 120 | pub const round = @import("math/round.zig").round; | ||
| 121 | pub const frexp = @import("math/frexp.zig").frexp; | ||
| 122 | pub const frexp32_result = @import("math/frexp.zig").frexp32_result; | ||
| 123 | pub const frexp64_result = @import("math/frexp.zig").frexp64_result; | ||
| 124 | pub const modf = @import("math/modf.zig").modf; | ||
| 125 | pub const modf32_result = @import("math/modf.zig").modf32_result; | ||
| 126 | pub const modf64_result = @import("math/modf.zig").modf64_result; | ||
| 127 | pub const copysign = @import("math/copysign.zig").copysign; | ||
| 128 | pub const isFinite = @import("math/isfinite.zig").isFinite; | ||
| 129 | pub const isInf = @import("math/isinf.zig").isInf; | ||
| 130 | pub const isPositiveInf = @import("math/isinf.zig").isPositiveInf; | ||
| 131 | pub const isNegativeInf = @import("math/isinf.zig").isNegativeInf; | ||
| 132 | pub const isNormal = @import("math/isnormal.zig").isNormal; | ||
| 133 | pub const signbit = @import("math/signbit.zig").signbit; | ||
| 134 | pub const scalbn = @import("math/scalbn.zig").scalbn; | ||
| 135 | pub const pow = @import("math/pow.zig").pow; | ||
| 136 | pub const powi = @import("math/powi.zig").powi; | ||
| 137 | pub const sqrt = @import("math/sqrt.zig").sqrt; | ||
| 138 | pub const cbrt = @import("math/cbrt.zig").cbrt; | ||
| 139 | pub const acos = @import("math/acos.zig").acos; | ||
| 140 | pub const asin = @import("math/asin.zig").asin; | ||
| 141 | pub const atan = @import("math/atan.zig").atan; | ||
| 142 | pub const atan2 = @import("math/atan2.zig").atan2; | ||
| 143 | pub const hypot = @import("math/hypot.zig").hypot; | ||
| 144 | pub const exp = @import("math/exp.zig").exp; | ||
| 145 | pub const exp2 = @import("math/exp2.zig").exp2; | ||
| 146 | pub const expm1 = @import("math/expm1.zig").expm1; | ||
| 147 | pub const ilogb = @import("math/ilogb.zig").ilogb; | ||
| 148 | pub const ln = @import("math/ln.zig").ln; | ||
| 149 | pub const log = @import("math/log.zig").log; | ||
| 150 | pub const log2 = @import("math/log2.zig").log2; | ||
| 151 | pub const log10 = @import("math/log10.zig").log10; | ||
| 152 | pub const log1p = @import("math/log1p.zig").log1p; | ||
| 153 | pub const fma = @import("math/fma.zig").fma; | ||
| 154 | pub const asinh = @import("math/asinh.zig").asinh; | ||
| 155 | pub const acosh = @import("math/acosh.zig").acosh; | ||
| 156 | pub const atanh = @import("math/atanh.zig").atanh; | ||
| 157 | pub const sinh = @import("math/sinh.zig").sinh; | ||
| 158 | pub const cosh = @import("math/cosh.zig").cosh; | ||
| 159 | pub const tanh = @import("math/tanh.zig").tanh; | ||
| 160 | pub const cos = @import("math/cos.zig").cos; | ||
| 161 | pub const sin = @import("math/sin.zig").sin; | ||
| 162 | pub const tan = @import("math/tan.zig").tan; | ||
| 163 | |||
| 164 | pub const complex = @import("math/complex.zig"); | ||
| 165 | pub const Complex = complex.Complex; | ||
| 166 | |||
| 167 | pub const big = @import("math/big.zig"); | ||
| 168 | |||
| 169 | test "math" { | ||
| 170 | _ = @import("math/nan.zig"); | ||
| 171 | _ = @import("math/isnan.zig"); | ||
| 172 | _ = @import("math/fabs.zig"); | ||
| 173 | _ = @import("math/ceil.zig"); | ||
| 174 | _ = @import("math/floor.zig"); | ||
| 175 | _ = @import("math/trunc.zig"); | ||
| 176 | _ = @import("math/round.zig"); | ||
| 177 | _ = @import("math/frexp.zig"); | ||
| 178 | _ = @import("math/modf.zig"); | ||
| 179 | _ = @import("math/copysign.zig"); | ||
| 180 | _ = @import("math/isfinite.zig"); | ||
| 181 | _ = @import("math/isinf.zig"); | ||
| 182 | _ = @import("math/isnormal.zig"); | ||
| 183 | _ = @import("math/signbit.zig"); | ||
| 184 | _ = @import("math/scalbn.zig"); | ||
| 185 | _ = @import("math/pow.zig"); | ||
| 186 | _ = @import("math/powi.zig"); | ||
| 187 | _ = @import("math/sqrt.zig"); | ||
| 188 | _ = @import("math/cbrt.zig"); | ||
| 189 | _ = @import("math/acos.zig"); | ||
| 190 | _ = @import("math/asin.zig"); | ||
| 191 | _ = @import("math/atan.zig"); | ||
| 192 | _ = @import("math/atan2.zig"); | ||
| 193 | _ = @import("math/hypot.zig"); | ||
| 194 | _ = @import("math/exp.zig"); | ||
| 195 | _ = @import("math/exp2.zig"); | ||
| 196 | _ = @import("math/expm1.zig"); | ||
| 197 | _ = @import("math/ilogb.zig"); | ||
| 198 | _ = @import("math/ln.zig"); | ||
| 199 | _ = @import("math/log.zig"); | ||
| 200 | _ = @import("math/log2.zig"); | ||
| 201 | _ = @import("math/log10.zig"); | ||
| 202 | _ = @import("math/log1p.zig"); | ||
| 203 | _ = @import("math/fma.zig"); | ||
| 204 | _ = @import("math/asinh.zig"); | ||
| 205 | _ = @import("math/acosh.zig"); | ||
| 206 | _ = @import("math/atanh.zig"); | ||
| 207 | _ = @import("math/sinh.zig"); | ||
| 208 | _ = @import("math/cosh.zig"); | ||
| 209 | _ = @import("math/tanh.zig"); | ||
| 210 | _ = @import("math/sin.zig"); | ||
| 211 | _ = @import("math/cos.zig"); | ||
| 212 | _ = @import("math/tan.zig"); | ||
| 213 | |||
| 214 | _ = @import("math/complex.zig"); | ||
| 215 | |||
| 216 | _ = @import("math/big.zig"); | ||
| 217 | } | ||
| 218 | |||
| 219 | pub fn floatMantissaBits(comptime T: type) comptime_int { | ||
| 220 | assert(@typeId(T) == builtin.TypeId.Float); | ||
| 221 | |||
| 222 | return switch (T.bit_count) { | ||
| 223 | 16 => 10, | ||
| 224 | 32 => 23, | ||
| 225 | 64 => 52, | ||
| 226 | 80 => 64, | ||
| 227 | 128 => 112, | ||
| 228 | else => @compileError("unknown floating point type " ++ @typeName(T)), | ||
| 229 | }; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn floatExponentBits(comptime T: type) comptime_int { | ||
| 233 | assert(@typeId(T) == builtin.TypeId.Float); | ||
| 234 | |||
| 235 | return switch (T.bit_count) { | ||
| 236 | 16 => 5, | ||
| 237 | 32 => 8, | ||
| 238 | 64 => 11, | ||
| 239 | 80 => 15, | ||
| 240 | 128 => 15, | ||
| 241 | else => @compileError("unknown floating point type " ++ @typeName(T)), | ||
| 242 | }; | ||
| 243 | } | ||
| 244 | |||
| 245 | pub fn min(x: var, y: var) @typeOf(x + y) { | ||
| 246 | return if (x < y) x else y; | ||
| 247 | } | ||
| 248 | |||
| 249 | test "math.min" { | ||
| 250 | testing.expect(min(i32(-1), i32(2)) == -1); | ||
| 251 | } | ||
| 252 | |||
| 253 | pub fn max(x: var, y: var) @typeOf(x + y) { | ||
| 254 | return if (x > y) x else y; | ||
| 255 | } | ||
| 256 | |||
| 257 | test "math.max" { | ||
| 258 | testing.expect(max(i32(-1), i32(2)) == 2); | ||
| 259 | } | ||
| 260 | |||
| 261 | pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { | ||
| 262 | var answer: T = undefined; | ||
| 263 | return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; | ||
| 264 | } | ||
| 265 | |||
| 266 | pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) { | ||
| 267 | var answer: T = undefined; | ||
| 268 | return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; | ||
| 269 | } | ||
| 270 | |||
| 271 | pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { | ||
| 272 | var answer: T = undefined; | ||
| 273 | return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn negate(x: var) !@typeOf(x) { | ||
| 277 | return sub(@typeOf(x), 0, x); | ||
| 278 | } | ||
| 279 | |||
| 280 | pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { | ||
| 281 | var answer: T = undefined; | ||
| 282 | return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer; | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Shifts left. Overflowed bits are truncated. | ||
| 286 | /// A negative shift amount results in a right shift. | ||
| 287 | pub fn shl(comptime T: type, a: T, shift_amt: var) T { | ||
| 288 | const abs_shift_amt = absCast(shift_amt); | ||
| 289 | const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); | ||
| 290 | |||
| 291 | if (@typeOf(shift_amt).is_signed) { | ||
| 292 | if (shift_amt >= 0) { | ||
| 293 | return a << casted_shift_amt; | ||
| 294 | } else { | ||
| 295 | return a >> casted_shift_amt; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | return a << casted_shift_amt; | ||
| 300 | } | ||
| 301 | |||
| 302 | test "math.shl" { | ||
| 303 | testing.expect(shl(u8, 0b11111111, usize(3)) == 0b11111000); | ||
| 304 | testing.expect(shl(u8, 0b11111111, usize(8)) == 0); | ||
| 305 | testing.expect(shl(u8, 0b11111111, usize(9)) == 0); | ||
| 306 | testing.expect(shl(u8, 0b11111111, isize(-2)) == 0b00111111); | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Shifts right. Overflowed bits are truncated. | ||
| 310 | /// A negative shift amount results in a lefft shift. | ||
| 311 | pub fn shr(comptime T: type, a: T, shift_amt: var) T { | ||
| 312 | const abs_shift_amt = absCast(shift_amt); | ||
| 313 | const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); | ||
| 314 | |||
| 315 | if (@typeOf(shift_amt).is_signed) { | ||
| 316 | if (shift_amt >= 0) { | ||
| 317 | return a >> casted_shift_amt; | ||
| 318 | } else { | ||
| 319 | return a << casted_shift_amt; | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | return a >> casted_shift_amt; | ||
| 324 | } | ||
| 325 | |||
| 326 | test "math.shr" { | ||
| 327 | testing.expect(shr(u8, 0b11111111, usize(3)) == 0b00011111); | ||
| 328 | testing.expect(shr(u8, 0b11111111, usize(8)) == 0); | ||
| 329 | testing.expect(shr(u8, 0b11111111, usize(9)) == 0); | ||
| 330 | testing.expect(shr(u8, 0b11111111, isize(-2)) == 0b11111100); | ||
| 331 | } | ||
| 332 | |||
| 333 | /// Rotates right. Only unsigned values can be rotated. | ||
| 334 | /// Negative shift values results in shift modulo the bit count. | ||
| 335 | pub fn rotr(comptime T: type, x: T, r: var) T { | ||
| 336 | if (T.is_signed) { | ||
| 337 | @compileError("cannot rotate signed integer"); | ||
| 338 | } else { | ||
| 339 | const ar = @mod(r, T.bit_count); | ||
| 340 | return shr(T, x, ar) | shl(T, x, T.bit_count - ar); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | test "math.rotr" { | ||
| 345 | testing.expect(rotr(u8, 0b00000001, usize(0)) == 0b00000001); | ||
| 346 | testing.expect(rotr(u8, 0b00000001, usize(9)) == 0b10000000); | ||
| 347 | testing.expect(rotr(u8, 0b00000001, usize(8)) == 0b00000001); | ||
| 348 | testing.expect(rotr(u8, 0b00000001, usize(4)) == 0b00010000); | ||
| 349 | testing.expect(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); | ||
| 350 | } | ||
| 351 | |||
| 352 | /// Rotates left. Only unsigned values can be rotated. | ||
| 353 | /// Negative shift values results in shift modulo the bit count. | ||
| 354 | pub fn rotl(comptime T: type, x: T, r: var) T { | ||
| 355 | if (T.is_signed) { | ||
| 356 | @compileError("cannot rotate signed integer"); | ||
| 357 | } else { | ||
| 358 | const ar = @mod(r, T.bit_count); | ||
| 359 | return shl(T, x, ar) | shr(T, x, T.bit_count - ar); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | test "math.rotl" { | ||
| 364 | testing.expect(rotl(u8, 0b00000001, usize(0)) == 0b00000001); | ||
| 365 | testing.expect(rotl(u8, 0b00000001, usize(9)) == 0b00000010); | ||
| 366 | testing.expect(rotl(u8, 0b00000001, usize(8)) == 0b00000001); | ||
| 367 | testing.expect(rotl(u8, 0b00000001, usize(4)) == 0b00010000); | ||
| 368 | testing.expect(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); | ||
| 369 | } | ||
| 370 | |||
| 371 | pub fn Log2Int(comptime T: type) type { | ||
| 372 | // comptime ceil log2 | ||
| 373 | comptime var count = 0; | ||
| 374 | comptime var s = T.bit_count - 1; | ||
| 375 | inline while (s != 0) : (s >>= 1) { | ||
| 376 | count += 1; | ||
| 377 | } | ||
| 378 | |||
| 379 | return @IntType(false, count); | ||
| 380 | } | ||
| 381 | |||
| 382 | pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type { | ||
| 383 | assert(from <= to); | ||
| 384 | if (from == 0 and to == 0) { | ||
| 385 | return u0; | ||
| 386 | } | ||
| 387 | const is_signed = from < 0; | ||
| 388 | const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement | ||
| 389 | const base = log2(largest_positive_integer); | ||
| 390 | const upper = (1 << base) - 1; | ||
| 391 | var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; | ||
| 392 | if (is_signed) { | ||
| 393 | magnitude_bits += 1; | ||
| 394 | } | ||
| 395 | return @IntType(is_signed, magnitude_bits); | ||
| 396 | } | ||
| 397 | |||
| 398 | test "math.IntFittingRange" { | ||
| 399 | testing.expect(IntFittingRange(0, 0) == u0); | ||
| 400 | testing.expect(IntFittingRange(0, 1) == u1); | ||
| 401 | testing.expect(IntFittingRange(0, 2) == u2); | ||
| 402 | testing.expect(IntFittingRange(0, 3) == u2); | ||
| 403 | testing.expect(IntFittingRange(0, 4) == u3); | ||
| 404 | testing.expect(IntFittingRange(0, 7) == u3); | ||
| 405 | testing.expect(IntFittingRange(0, 8) == u4); | ||
| 406 | testing.expect(IntFittingRange(0, 9) == u4); | ||
| 407 | testing.expect(IntFittingRange(0, 15) == u4); | ||
| 408 | testing.expect(IntFittingRange(0, 16) == u5); | ||
| 409 | testing.expect(IntFittingRange(0, 17) == u5); | ||
| 410 | testing.expect(IntFittingRange(0, 4095) == u12); | ||
| 411 | testing.expect(IntFittingRange(2000, 4095) == u12); | ||
| 412 | testing.expect(IntFittingRange(0, 4096) == u13); | ||
| 413 | testing.expect(IntFittingRange(2000, 4096) == u13); | ||
| 414 | testing.expect(IntFittingRange(0, 4097) == u13); | ||
| 415 | testing.expect(IntFittingRange(2000, 4097) == u13); | ||
| 416 | testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87); | ||
| 417 | testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177); | ||
| 418 | |||
| 419 | testing.expect(IntFittingRange(-1, -1) == i1); | ||
| 420 | testing.expect(IntFittingRange(-1, 0) == i1); | ||
| 421 | testing.expect(IntFittingRange(-1, 1) == i2); | ||
| 422 | testing.expect(IntFittingRange(-2, -2) == i2); | ||
| 423 | testing.expect(IntFittingRange(-2, -1) == i2); | ||
| 424 | testing.expect(IntFittingRange(-2, 0) == i2); | ||
| 425 | testing.expect(IntFittingRange(-2, 1) == i2); | ||
| 426 | testing.expect(IntFittingRange(-2, 2) == i3); | ||
| 427 | testing.expect(IntFittingRange(-1, 2) == i3); | ||
| 428 | testing.expect(IntFittingRange(-1, 3) == i3); | ||
| 429 | testing.expect(IntFittingRange(-1, 4) == i4); | ||
| 430 | testing.expect(IntFittingRange(-1, 7) == i4); | ||
| 431 | testing.expect(IntFittingRange(-1, 8) == i5); | ||
| 432 | testing.expect(IntFittingRange(-1, 9) == i5); | ||
| 433 | testing.expect(IntFittingRange(-1, 15) == i5); | ||
| 434 | testing.expect(IntFittingRange(-1, 16) == i6); | ||
| 435 | testing.expect(IntFittingRange(-1, 17) == i6); | ||
| 436 | testing.expect(IntFittingRange(-1, 4095) == i13); | ||
| 437 | testing.expect(IntFittingRange(-4096, 4095) == i13); | ||
| 438 | testing.expect(IntFittingRange(-1, 4096) == i14); | ||
| 439 | testing.expect(IntFittingRange(-4097, 4095) == i14); | ||
| 440 | testing.expect(IntFittingRange(-1, 4097) == i14); | ||
| 441 | testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88); | ||
| 442 | testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178); | ||
| 443 | } | ||
| 444 | |||
| 445 | test "math overflow functions" { | ||
| 446 | testOverflow(); | ||
| 447 | comptime testOverflow(); | ||
| 448 | } | ||
| 449 | |||
| 450 | fn testOverflow() void { | ||
| 451 | testing.expect((mul(i32, 3, 4) catch unreachable) == 12); | ||
| 452 | testing.expect((add(i32, 3, 4) catch unreachable) == 7); | ||
| 453 | testing.expect((sub(i32, 3, 4) catch unreachable) == -1); | ||
| 454 | testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); | ||
| 455 | } | ||
| 456 | |||
| 457 | pub fn absInt(x: var) !@typeOf(x) { | ||
| 458 | const T = @typeOf(x); | ||
| 459 | comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt | ||
| 460 | comptime assert(T.is_signed); // must pass a signed integer to absInt | ||
| 461 | |||
| 462 | if (x == minInt(@typeOf(x))) { | ||
| 463 | return error.Overflow; | ||
| 464 | } else { | ||
| 465 | @setRuntimeSafety(false); | ||
| 466 | return if (x < 0) -x else x; | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | test "math.absInt" { | ||
| 471 | testAbsInt(); | ||
| 472 | comptime testAbsInt(); | ||
| 473 | } | ||
| 474 | fn testAbsInt() void { | ||
| 475 | testing.expect((absInt(i32(-10)) catch unreachable) == 10); | ||
| 476 | testing.expect((absInt(i32(10)) catch unreachable) == 10); | ||
| 477 | } | ||
| 478 | |||
| 479 | pub const absFloat = @import("fabs.zig").fabs; | ||
| 480 | |||
| 481 | pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { | ||
| 482 | @setRuntimeSafety(false); | ||
| 483 | if (denominator == 0) return error.DivisionByZero; | ||
| 484 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; | ||
| 485 | return @divTrunc(numerator, denominator); | ||
| 486 | } | ||
| 487 | |||
| 488 | test "math.divTrunc" { | ||
| 489 | testDivTrunc(); | ||
| 490 | comptime testDivTrunc(); | ||
| 491 | } | ||
| 492 | fn testDivTrunc() void { | ||
| 493 | testing.expect((divTrunc(i32, 5, 3) catch unreachable) == 1); | ||
| 494 | testing.expect((divTrunc(i32, -5, 3) catch unreachable) == -1); | ||
| 495 | testing.expectError(error.DivisionByZero, divTrunc(i8, -5, 0)); | ||
| 496 | testing.expectError(error.Overflow, divTrunc(i8, -128, -1)); | ||
| 497 | |||
| 498 | testing.expect((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0); | ||
| 499 | testing.expect((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); | ||
| 500 | } | ||
| 501 | |||
| 502 | pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { | ||
| 503 | @setRuntimeSafety(false); | ||
| 504 | if (denominator == 0) return error.DivisionByZero; | ||
| 505 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; | ||
| 506 | return @divFloor(numerator, denominator); | ||
| 507 | } | ||
| 508 | |||
| 509 | test "math.divFloor" { | ||
| 510 | testDivFloor(); | ||
| 511 | comptime testDivFloor(); | ||
| 512 | } | ||
| 513 | fn testDivFloor() void { | ||
| 514 | testing.expect((divFloor(i32, 5, 3) catch unreachable) == 1); | ||
| 515 | testing.expect((divFloor(i32, -5, 3) catch unreachable) == -2); | ||
| 516 | testing.expectError(error.DivisionByZero, divFloor(i8, -5, 0)); | ||
| 517 | testing.expectError(error.Overflow, divFloor(i8, -128, -1)); | ||
| 518 | |||
| 519 | testing.expect((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0); | ||
| 520 | testing.expect((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); | ||
| 521 | } | ||
| 522 | |||
| 523 | pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { | ||
| 524 | @setRuntimeSafety(false); | ||
| 525 | if (denominator == 0) return error.DivisionByZero; | ||
| 526 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; | ||
| 527 | const result = @divTrunc(numerator, denominator); | ||
| 528 | if (result * denominator != numerator) return error.UnexpectedRemainder; | ||
| 529 | return result; | ||
| 530 | } | ||
| 531 | |||
| 532 | test "math.divExact" { | ||
| 533 | testDivExact(); | ||
| 534 | comptime testDivExact(); | ||
| 535 | } | ||
| 536 | fn testDivExact() void { | ||
| 537 | testing.expect((divExact(i32, 10, 5) catch unreachable) == 2); | ||
| 538 | testing.expect((divExact(i32, -10, 5) catch unreachable) == -2); | ||
| 539 | testing.expectError(error.DivisionByZero, divExact(i8, -5, 0)); | ||
| 540 | testing.expectError(error.Overflow, divExact(i8, -128, -1)); | ||
| 541 | testing.expectError(error.UnexpectedRemainder, divExact(i32, 5, 2)); | ||
| 542 | |||
| 543 | testing.expect((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0); | ||
| 544 | testing.expect((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0); | ||
| 545 | testing.expectError(error.UnexpectedRemainder, divExact(f32, 5.0, 2.0)); | ||
| 546 | } | ||
| 547 | |||
| 548 | pub fn mod(comptime T: type, numerator: T, denominator: T) !T { | ||
| 549 | @setRuntimeSafety(false); | ||
| 550 | if (denominator == 0) return error.DivisionByZero; | ||
| 551 | if (denominator < 0) return error.NegativeDenominator; | ||
| 552 | return @mod(numerator, denominator); | ||
| 553 | } | ||
| 554 | |||
| 555 | test "math.mod" { | ||
| 556 | testMod(); | ||
| 557 | comptime testMod(); | ||
| 558 | } | ||
| 559 | fn testMod() void { | ||
| 560 | testing.expect((mod(i32, -5, 3) catch unreachable) == 1); | ||
| 561 | testing.expect((mod(i32, 5, 3) catch unreachable) == 2); | ||
| 562 | testing.expectError(error.NegativeDenominator, mod(i32, 10, -1)); | ||
| 563 | testing.expectError(error.DivisionByZero, mod(i32, 10, 0)); | ||
| 564 | |||
| 565 | testing.expect((mod(f32, -5, 3) catch unreachable) == 1); | ||
| 566 | testing.expect((mod(f32, 5, 3) catch unreachable) == 2); | ||
| 567 | testing.expectError(error.NegativeDenominator, mod(f32, 10, -1)); | ||
| 568 | testing.expectError(error.DivisionByZero, mod(f32, 10, 0)); | ||
| 569 | } | ||
| 570 | |||
| 571 | pub fn rem(comptime T: type, numerator: T, denominator: T) !T { | ||
| 572 | @setRuntimeSafety(false); | ||
| 573 | if (denominator == 0) return error.DivisionByZero; | ||
| 574 | if (denominator < 0) return error.NegativeDenominator; | ||
| 575 | return @rem(numerator, denominator); | ||
| 576 | } | ||
| 577 | |||
| 578 | test "math.rem" { | ||
| 579 | testRem(); | ||
| 580 | comptime testRem(); | ||
| 581 | } | ||
| 582 | fn testRem() void { | ||
| 583 | testing.expect((rem(i32, -5, 3) catch unreachable) == -2); | ||
| 584 | testing.expect((rem(i32, 5, 3) catch unreachable) == 2); | ||
| 585 | testing.expectError(error.NegativeDenominator, rem(i32, 10, -1)); | ||
| 586 | testing.expectError(error.DivisionByZero, rem(i32, 10, 0)); | ||
| 587 | |||
| 588 | testing.expect((rem(f32, -5, 3) catch unreachable) == -2); | ||
| 589 | testing.expect((rem(f32, 5, 3) catch unreachable) == 2); | ||
| 590 | testing.expectError(error.NegativeDenominator, rem(f32, 10, -1)); | ||
| 591 | testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); | ||
| 592 | } | ||
| 593 | |||
| 594 | /// Returns the absolute value of the integer parameter. | ||
| 595 | /// Result is an unsigned integer. | ||
| 596 | pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { | ||
| 597 | const uint = @IntType(false, @typeOf(x).bit_count); | ||
| 598 | if (x >= 0) return @intCast(uint, x); | ||
| 599 | |||
| 600 | return @intCast(uint, -(x + 1)) + 1; | ||
| 601 | } | ||
| 602 | |||
| 603 | test "math.absCast" { | ||
| 604 | testing.expect(absCast(i32(-999)) == 999); | ||
| 605 | testing.expect(@typeOf(absCast(i32(-999))) == u32); | ||
| 606 | |||
| 607 | testing.expect(absCast(i32(999)) == 999); | ||
| 608 | testing.expect(@typeOf(absCast(i32(999))) == u32); | ||
| 609 | |||
| 610 | testing.expect(absCast(i32(minInt(i32))) == -minInt(i32)); | ||
| 611 | testing.expect(@typeOf(absCast(i32(minInt(i32)))) == u32); | ||
| 612 | } | ||
| 613 | |||
| 614 | /// Returns the negation of the integer parameter. | ||
| 615 | /// Result is a signed integer. | ||
| 616 | pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { | ||
| 617 | if (@typeOf(x).is_signed) return negate(x); | ||
| 618 | |||
| 619 | const int = @IntType(true, @typeOf(x).bit_count); | ||
| 620 | if (x > -minInt(int)) return error.Overflow; | ||
| 621 | |||
| 622 | if (x == -minInt(int)) return minInt(int); | ||
| 623 | |||
| 624 | return -@intCast(int, x); | ||
| 625 | } | ||
| 626 | |||
| 627 | test "math.negateCast" { | ||
| 628 | testing.expect((negateCast(u32(999)) catch unreachable) == -999); | ||
| 629 | testing.expect(@typeOf(negateCast(u32(999)) catch unreachable) == i32); | ||
| 630 | |||
| 631 | testing.expect((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); | ||
| 632 | testing.expect(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); | ||
| 633 | |||
| 634 | testing.expectError(error.Overflow, negateCast(u32(maxInt(i32) + 10))); | ||
| 635 | } | ||
| 636 | |||
| 637 | /// Cast an integer to a different integer type. If the value doesn't fit, | ||
| 638 | /// return an error. | ||
| 639 | pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { | ||
| 640 | comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer | ||
| 641 | comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer | ||
| 642 | if (maxInt(@typeOf(x)) > maxInt(T) and x > maxInt(T)) { | ||
| 643 | return error.Overflow; | ||
| 644 | } else if (minInt(@typeOf(x)) < minInt(T) and x < minInt(T)) { | ||
| 645 | return error.Overflow; | ||
| 646 | } else { | ||
| 647 | return @intCast(T, x); | ||
| 648 | } | ||
| 649 | } | ||
| 650 | |||
| 651 | test "math.cast" { | ||
| 652 | testing.expectError(error.Overflow, cast(u8, u32(300))); | ||
| 653 | testing.expectError(error.Overflow, cast(i8, i32(-200))); | ||
| 654 | testing.expectError(error.Overflow, cast(u8, i8(-1))); | ||
| 655 | testing.expectError(error.Overflow, cast(u64, i8(-1))); | ||
| 656 | |||
| 657 | testing.expect((try cast(u8, u32(255))) == u8(255)); | ||
| 658 | testing.expect(@typeOf(try cast(u8, u32(255))) == u8); | ||
| 659 | } | ||
| 660 | |||
| 661 | pub const AlignCastError = error{UnalignedMemory}; | ||
| 662 | |||
| 663 | /// Align cast a pointer but return an error if it's the wrong alignment | ||
| 664 | pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { | ||
| 665 | const addr = @ptrToInt(ptr); | ||
| 666 | if (addr % alignment != 0) { | ||
| 667 | return error.UnalignedMemory; | ||
| 668 | } | ||
| 669 | return @alignCast(alignment, ptr); | ||
| 670 | } | ||
| 671 | |||
| 672 | pub fn floorPowerOfTwo(comptime T: type, value: T) T { | ||
| 673 | var x = value; | ||
| 674 | |||
| 675 | comptime var i = 1; | ||
| 676 | inline while (T.bit_count > i) : (i *= 2) { | ||
| 677 | x |= (x >> i); | ||
| 678 | } | ||
| 679 | |||
| 680 | return x - (x >> 1); | ||
| 681 | } | ||
| 682 | |||
| 683 | test "math.floorPowerOfTwo" { | ||
| 684 | testFloorPowerOfTwo(); | ||
| 685 | comptime testFloorPowerOfTwo(); | ||
| 686 | } | ||
| 687 | |||
| 688 | pub fn log2_int(comptime T: type, x: T) Log2Int(T) { | ||
| 689 | assert(x != 0); | ||
| 690 | return @intCast(Log2Int(T), T.bit_count - 1 - @clz(x)); | ||
| 691 | } | ||
| 692 | |||
| 693 | pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { | ||
| 694 | assert(x != 0); | ||
| 695 | const log2_val = log2_int(T, x); | ||
| 696 | if (T(1) << log2_val == x) | ||
| 697 | return log2_val; | ||
| 698 | return log2_val + 1; | ||
| 699 | } | ||
| 700 | |||
| 701 | test "std.math.log2_int_ceil" { | ||
| 702 | testing.expect(log2_int_ceil(u32, 1) == 0); | ||
| 703 | testing.expect(log2_int_ceil(u32, 2) == 1); | ||
| 704 | testing.expect(log2_int_ceil(u32, 3) == 2); | ||
| 705 | testing.expect(log2_int_ceil(u32, 4) == 2); | ||
| 706 | testing.expect(log2_int_ceil(u32, 5) == 3); | ||
| 707 | testing.expect(log2_int_ceil(u32, 6) == 3); | ||
| 708 | testing.expect(log2_int_ceil(u32, 7) == 3); | ||
| 709 | testing.expect(log2_int_ceil(u32, 8) == 3); | ||
| 710 | testing.expect(log2_int_ceil(u32, 9) == 4); | ||
| 711 | testing.expect(log2_int_ceil(u32, 10) == 4); | ||
| 712 | } | ||
| 713 | |||
| 714 | fn testFloorPowerOfTwo() void { | ||
| 715 | testing.expect(floorPowerOfTwo(u32, 63) == 32); | ||
| 716 | testing.expect(floorPowerOfTwo(u32, 64) == 64); | ||
| 717 | testing.expect(floorPowerOfTwo(u32, 65) == 64); | ||
| 718 | testing.expect(floorPowerOfTwo(u4, 7) == 4); | ||
| 719 | testing.expect(floorPowerOfTwo(u4, 8) == 8); | ||
| 720 | testing.expect(floorPowerOfTwo(u4, 9) == 8); | ||
| 721 | } | ||
| 722 | |||
| 723 | pub fn lossyCast(comptime T: type, value: var) T { | ||
| 724 | switch (@typeInfo(@typeOf(value))) { | ||
| 725 | builtin.TypeId.Int => return @intToFloat(T, value), | ||
| 726 | builtin.TypeId.Float => return @floatCast(T, value), | ||
| 727 | builtin.TypeId.ComptimeInt => return T(value), | ||
| 728 | builtin.TypeId.ComptimeFloat => return T(value), | ||
| 729 | else => @compileError("bad type"), | ||
| 730 | } | ||
| 731 | } | ||
| 732 | |||
| 733 | test "math.f64_min" { | ||
| 734 | const f64_min_u64 = 0x0010000000000000; | ||
| 735 | const fmin: f64 = f64_min; | ||
| 736 | testing.expect(@bitCast(u64, fmin) == f64_min_u64); | ||
| 737 | } | ||
| 738 | |||
| 739 | pub fn maxInt(comptime T: type) comptime_int { | ||
| 740 | const info = @typeInfo(T); | ||
| 741 | const bit_count = info.Int.bits; | ||
| 742 | if (bit_count == 0) return 0; | ||
| 743 | return (1 << (bit_count - @boolToInt(info.Int.is_signed))) - 1; | ||
| 744 | } | ||
| 745 | |||
| 746 | pub fn minInt(comptime T: type) comptime_int { | ||
| 747 | const info = @typeInfo(T); | ||
| 748 | const bit_count = info.Int.bits; | ||
| 749 | if (!info.Int.is_signed) return 0; | ||
| 750 | if (bit_count == 0) return 0; | ||
| 751 | return -(1 << (bit_count - 1)); | ||
| 752 | } | ||
| 753 | |||
| 754 | test "minInt and maxInt" { | ||
| 755 | testing.expect(maxInt(u0) == 0); | ||
| 756 | testing.expect(maxInt(u1) == 1); | ||
| 757 | testing.expect(maxInt(u8) == 255); | ||
| 758 | testing.expect(maxInt(u16) == 65535); | ||
| 759 | testing.expect(maxInt(u32) == 4294967295); | ||
| 760 | testing.expect(maxInt(u64) == 18446744073709551615); | ||
| 761 | testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); | ||
| 762 | |||
| 763 | testing.expect(maxInt(i0) == 0); | ||
| 764 | testing.expect(maxInt(i1) == 0); | ||
| 765 | testing.expect(maxInt(i8) == 127); | ||
| 766 | testing.expect(maxInt(i16) == 32767); | ||
| 767 | testing.expect(maxInt(i32) == 2147483647); | ||
| 768 | testing.expect(maxInt(i63) == 4611686018427387903); | ||
| 769 | testing.expect(maxInt(i64) == 9223372036854775807); | ||
| 770 | testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); | ||
| 771 | |||
| 772 | testing.expect(minInt(u0) == 0); | ||
| 773 | testing.expect(minInt(u1) == 0); | ||
| 774 | testing.expect(minInt(u8) == 0); | ||
| 775 | testing.expect(minInt(u16) == 0); | ||
| 776 | testing.expect(minInt(u32) == 0); | ||
| 777 | testing.expect(minInt(u63) == 0); | ||
| 778 | testing.expect(minInt(u64) == 0); | ||
| 779 | testing.expect(minInt(u128) == 0); | ||
| 780 | |||
| 781 | testing.expect(minInt(i0) == 0); | ||
| 782 | testing.expect(minInt(i1) == -1); | ||
| 783 | testing.expect(minInt(i8) == -128); | ||
| 784 | testing.expect(minInt(i16) == -32768); | ||
| 785 | testing.expect(minInt(i32) == -2147483648); | ||
| 786 | testing.expect(minInt(i63) == -4611686018427387904); | ||
| 787 | testing.expect(minInt(i64) == -9223372036854775808); | ||
| 788 | testing.expect(minInt(i128) == -170141183460469231731687303715884105728); | ||
| 789 | } | ||
| 790 | |||
| 791 | test "max value type" { | ||
| 792 | // If the type of maxInt(i32) was i32 then this implicit cast to | ||
| 793 | // u32 would not work. But since the value is a number literal, | ||
| 794 | // it works fine. | ||
| 795 | const x: u32 = maxInt(i32); | ||
| 796 | testing.expect(x == 2147483647); | ||
| 797 | } | ||
std/math/acos.zig+1-1| ... | @@ -2,7 +2,7 @@ | ... | @@ -2,7 +2,7 @@ |
| 2 | // | 2 | // |
| 3 | // - acos(x) = nan if x < -1 or x > 1 | 3 | // - acos(x) = nan if x < -1 or x > 1 |
| 4 | 4 | ||
| 5 | const std = @import("../index.zig"); | 5 | const std = @import("../std.zig"); |
| 6 | const math = std.math; | 6 | const math = std.math; |
| 7 | const expect = std.testing.expect; | 7 | const expect = std.testing.expect; |
| 8 | 8 |
std/math/acosh.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - acosh(nan) = nan | 4 | // - acosh(nan) = nan |
| 5 | 5 | ||
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/asin.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // - asin(+-0) = +-0 | 3 | // - asin(+-0) = +-0 |
| 4 | // - asin(x) = nan if x < -1 or x > 1 | 4 | // - asin(x) = nan if x < -1 or x > 1 |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | 9 |
std/math/asinh.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - asinh(+-inf) = +-inf | 4 | // - asinh(+-inf) = +-inf |
| 5 | // - asinh(nan) = nan | 5 | // - asinh(nan) = nan |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
std/math/atan.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // - atan(+-0) = +-0 | 3 | // - atan(+-0) = +-0 |
| 4 | // - atan(+-inf) = +-pi/2 | 4 | // - atan(+-inf) = +-pi/2 |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | 9 |
std/math/atan2.zig+1-1| ... | @@ -18,7 +18,7 @@ | ... | @@ -18,7 +18,7 @@ |
| 18 | // atan2(+inf, x) = +pi/2 | 18 | // atan2(+inf, x) = +pi/2 |
| 19 | // atan2(-inf, x) = -pi/2 | 19 | // atan2(-inf, x) = -pi/2 |
| 20 | 20 | ||
| 21 | const std = @import("../index.zig"); | 21 | const std = @import("../std.zig"); |
| 22 | const math = std.math; | 22 | const math = std.math; |
| 23 | const expect = std.testing.expect; | 23 | const expect = std.testing.expect; |
| 24 | 24 |
std/math/atanh.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - atanh(x) = nan if |x| > 1 with signal | 4 | // - atanh(x) = nan if |x| > 1 with signal |
| 5 | // - atanh(nan) = nan | 5 | // - atanh(nan) = nan |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
std/math/big.zig created+5| ... | @@ -0,0 +1,5 @@ | ||
| 1 | pub use @import("big/int.zig"); | ||
| 2 | |||
| 3 | test "math.big" { | ||
| 4 | _ = @import("big/int.zig"); | ||
| 5 | } | ||
std/math/big/index.zig deleted-5| ... | @@ -1,5 +0,0 @@ | ||
| 1 | pub use @import("int.zig"); | ||
| 2 | |||
| 3 | test "math.big" { | ||
| 4 | _ = @import("int.zig"); | ||
| 5 | } | ||
std/math/big/int.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/math/cbrt.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - cbrt(+-inf) = +-inf | 4 | // - cbrt(+-inf) = +-inf |
| 5 | // - cbrt(nan) = nan | 5 | // - cbrt(nan) = nan |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/ceil.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - ceil(nan) = nan | 5 | // - ceil(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/complex.zig created+171| ... | @@ -0,0 +1,171 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const math = std.math; | ||
| 4 | |||
| 5 | pub const abs = @import("complex/abs.zig").abs; | ||
| 6 | pub const acosh = @import("complex/acosh.zig").acosh; | ||
| 7 | pub const acos = @import("complex/acos.zig").acos; | ||
| 8 | pub const arg = @import("complex/arg.zig").arg; | ||
| 9 | pub const asinh = @import("complex/asinh.zig").asinh; | ||
| 10 | pub const asin = @import("complex/asin.zig").asin; | ||
| 11 | pub const atanh = @import("complex/atanh.zig").atanh; | ||
| 12 | pub const atan = @import("complex/atan.zig").atan; | ||
| 13 | pub const conj = @import("complex/conj.zig").conj; | ||
| 14 | pub const cosh = @import("complex/cosh.zig").cosh; | ||
| 15 | pub const cos = @import("complex/cos.zig").cos; | ||
| 16 | pub const exp = @import("complex/exp.zig").exp; | ||
| 17 | pub const log = @import("complex/log.zig").log; | ||
| 18 | pub const pow = @import("complex/pow.zig").pow; | ||
| 19 | pub const proj = @import("complex/proj.zig").proj; | ||
| 20 | pub const sinh = @import("complex/sinh.zig").sinh; | ||
| 21 | pub const sin = @import("complex/sin.zig").sin; | ||
| 22 | pub const sqrt = @import("complex/sqrt.zig").sqrt; | ||
| 23 | pub const tanh = @import("complex/tanh.zig").tanh; | ||
| 24 | pub const tan = @import("complex/tan.zig").tan; | ||
| 25 | |||
| 26 | pub fn Complex(comptime T: type) type { | ||
| 27 | return struct { | ||
| 28 | const Self = @This(); | ||
| 29 | |||
| 30 | re: T, | ||
| 31 | im: T, | ||
| 32 | |||
| 33 | pub fn new(re: T, im: T) Self { | ||
| 34 | return Self{ | ||
| 35 | .re = re, | ||
| 36 | .im = im, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn add(self: Self, other: Self) Self { | ||
| 41 | return Self{ | ||
| 42 | .re = self.re + other.re, | ||
| 43 | .im = self.im + other.im, | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn sub(self: Self, other: Self) Self { | ||
| 48 | return Self{ | ||
| 49 | .re = self.re - other.re, | ||
| 50 | .im = self.im - other.im, | ||
| 51 | }; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn mul(self: Self, other: Self) Self { | ||
| 55 | return Self{ | ||
| 56 | .re = self.re * other.re - self.im * other.im, | ||
| 57 | .im = self.im * other.re + self.re * other.im, | ||
| 58 | }; | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn div(self: Self, other: Self) Self { | ||
| 62 | const re_num = self.re * other.re + self.im * other.im; | ||
| 63 | const im_num = self.im * other.re - self.re * other.im; | ||
| 64 | const den = other.re * other.re + other.im * other.im; | ||
| 65 | |||
| 66 | return Self{ | ||
| 67 | .re = re_num / den, | ||
| 68 | .im = im_num / den, | ||
| 69 | }; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn conjugate(self: Self) Self { | ||
| 73 | return Self{ | ||
| 74 | .re = self.re, | ||
| 75 | .im = -self.im, | ||
| 76 | }; | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn reciprocal(self: Self) Self { | ||
| 80 | const m = self.re * self.re + self.im * self.im; | ||
| 81 | return Self{ | ||
| 82 | .re = self.re / m, | ||
| 83 | .im = -self.im / m, | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn magnitude(self: Self) T { | ||
| 88 | return math.sqrt(self.re * self.re + self.im * self.im); | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | const epsilon = 0.0001; | ||
| 94 | |||
| 95 | test "complex.add" { | ||
| 96 | const a = Complex(f32).new(5, 3); | ||
| 97 | const b = Complex(f32).new(2, 7); | ||
| 98 | const c = a.add(b); | ||
| 99 | |||
| 100 | testing.expect(c.re == 7 and c.im == 10); | ||
| 101 | } | ||
| 102 | |||
| 103 | test "complex.sub" { | ||
| 104 | const a = Complex(f32).new(5, 3); | ||
| 105 | const b = Complex(f32).new(2, 7); | ||
| 106 | const c = a.sub(b); | ||
| 107 | |||
| 108 | testing.expect(c.re == 3 and c.im == -4); | ||
| 109 | } | ||
| 110 | |||
| 111 | test "complex.mul" { | ||
| 112 | const a = Complex(f32).new(5, 3); | ||
| 113 | const b = Complex(f32).new(2, 7); | ||
| 114 | const c = a.mul(b); | ||
| 115 | |||
| 116 | testing.expect(c.re == -11 and c.im == 41); | ||
| 117 | } | ||
| 118 | |||
| 119 | test "complex.div" { | ||
| 120 | const a = Complex(f32).new(5, 3); | ||
| 121 | const b = Complex(f32).new(2, 7); | ||
| 122 | const c = a.div(b); | ||
| 123 | |||
| 124 | testing.expect(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and | ||
| 125 | math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); | ||
| 126 | } | ||
| 127 | |||
| 128 | test "complex.conjugate" { | ||
| 129 | const a = Complex(f32).new(5, 3); | ||
| 130 | const c = a.conjugate(); | ||
| 131 | |||
| 132 | testing.expect(c.re == 5 and c.im == -3); | ||
| 133 | } | ||
| 134 | |||
| 135 | test "complex.reciprocal" { | ||
| 136 | const a = Complex(f32).new(5, 3); | ||
| 137 | const c = a.reciprocal(); | ||
| 138 | |||
| 139 | testing.expect(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and | ||
| 140 | math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); | ||
| 141 | } | ||
| 142 | |||
| 143 | test "complex.magnitude" { | ||
| 144 | const a = Complex(f32).new(5, 3); | ||
| 145 | const c = a.magnitude(); | ||
| 146 | |||
| 147 | testing.expect(math.approxEq(f32, c, 5.83095, epsilon)); | ||
| 148 | } | ||
| 149 | |||
| 150 | test "complex.cmath" { | ||
| 151 | _ = @import("complex/abs.zig"); | ||
| 152 | _ = @import("complex/acosh.zig"); | ||
| 153 | _ = @import("complex/acos.zig"); | ||
| 154 | _ = @import("complex/arg.zig"); | ||
| 155 | _ = @import("complex/asinh.zig"); | ||
| 156 | _ = @import("complex/asin.zig"); | ||
| 157 | _ = @import("complex/atanh.zig"); | ||
| 158 | _ = @import("complex/atan.zig"); | ||
| 159 | _ = @import("complex/conj.zig"); | ||
| 160 | _ = @import("complex/cosh.zig"); | ||
| 161 | _ = @import("complex/cos.zig"); | ||
| 162 | _ = @import("complex/exp.zig"); | ||
| 163 | _ = @import("complex/log.zig"); | ||
| 164 | _ = @import("complex/pow.zig"); | ||
| 165 | _ = @import("complex/proj.zig"); | ||
| 166 | _ = @import("complex/sinh.zig"); | ||
| 167 | _ = @import("complex/sin.zig"); | ||
| 168 | _ = @import("complex/sqrt.zig"); | ||
| 169 | _ = @import("complex/tanh.zig"); | ||
| 170 | _ = @import("complex/tan.zig"); | ||
| 171 | } | ||
std/math/complex/abs.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/acos.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/acosh.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/arg.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/asin.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/asinh.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/atan.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/atanh.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/conj.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/cos.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/cosh.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/exp.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/index.zig deleted-171| ... | @@ -1,171 +0,0 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const testing = std.testing; | ||
| 3 | const math = std.math; | ||
| 4 | |||
| 5 | pub const abs = @import("abs.zig").abs; | ||
| 6 | pub const acosh = @import("acosh.zig").acosh; | ||
| 7 | pub const acos = @import("acos.zig").acos; | ||
| 8 | pub const arg = @import("arg.zig").arg; | ||
| 9 | pub const asinh = @import("asinh.zig").asinh; | ||
| 10 | pub const asin = @import("asin.zig").asin; | ||
| 11 | pub const atanh = @import("atanh.zig").atanh; | ||
| 12 | pub const atan = @import("atan.zig").atan; | ||
| 13 | pub const conj = @import("conj.zig").conj; | ||
| 14 | pub const cosh = @import("cosh.zig").cosh; | ||
| 15 | pub const cos = @import("cos.zig").cos; | ||
| 16 | pub const exp = @import("exp.zig").exp; | ||
| 17 | pub const log = @import("log.zig").log; | ||
| 18 | pub const pow = @import("pow.zig").pow; | ||
| 19 | pub const proj = @import("proj.zig").proj; | ||
| 20 | pub const sinh = @import("sinh.zig").sinh; | ||
| 21 | pub const sin = @import("sin.zig").sin; | ||
| 22 | pub const sqrt = @import("sqrt.zig").sqrt; | ||
| 23 | pub const tanh = @import("tanh.zig").tanh; | ||
| 24 | pub const tan = @import("tan.zig").tan; | ||
| 25 | |||
| 26 | pub fn Complex(comptime T: type) type { | ||
| 27 | return struct { | ||
| 28 | const Self = @This(); | ||
| 29 | |||
| 30 | re: T, | ||
| 31 | im: T, | ||
| 32 | |||
| 33 | pub fn new(re: T, im: T) Self { | ||
| 34 | return Self{ | ||
| 35 | .re = re, | ||
| 36 | .im = im, | ||
| 37 | }; | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn add(self: Self, other: Self) Self { | ||
| 41 | return Self{ | ||
| 42 | .re = self.re + other.re, | ||
| 43 | .im = self.im + other.im, | ||
| 44 | }; | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn sub(self: Self, other: Self) Self { | ||
| 48 | return Self{ | ||
| 49 | .re = self.re - other.re, | ||
| 50 | .im = self.im - other.im, | ||
| 51 | }; | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn mul(self: Self, other: Self) Self { | ||
| 55 | return Self{ | ||
| 56 | .re = self.re * other.re - self.im * other.im, | ||
| 57 | .im = self.im * other.re + self.re * other.im, | ||
| 58 | }; | ||
| 59 | } | ||
| 60 | |||
| 61 | pub fn div(self: Self, other: Self) Self { | ||
| 62 | const re_num = self.re * other.re + self.im * other.im; | ||
| 63 | const im_num = self.im * other.re - self.re * other.im; | ||
| 64 | const den = other.re * other.re + other.im * other.im; | ||
| 65 | |||
| 66 | return Self{ | ||
| 67 | .re = re_num / den, | ||
| 68 | .im = im_num / den, | ||
| 69 | }; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn conjugate(self: Self) Self { | ||
| 73 | return Self{ | ||
| 74 | .re = self.re, | ||
| 75 | .im = -self.im, | ||
| 76 | }; | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn reciprocal(self: Self) Self { | ||
| 80 | const m = self.re * self.re + self.im * self.im; | ||
| 81 | return Self{ | ||
| 82 | .re = self.re / m, | ||
| 83 | .im = -self.im / m, | ||
| 84 | }; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn magnitude(self: Self) T { | ||
| 88 | return math.sqrt(self.re * self.re + self.im * self.im); | ||
| 89 | } | ||
| 90 | }; | ||
| 91 | } | ||
| 92 | |||
| 93 | const epsilon = 0.0001; | ||
| 94 | |||
| 95 | test "complex.add" { | ||
| 96 | const a = Complex(f32).new(5, 3); | ||
| 97 | const b = Complex(f32).new(2, 7); | ||
| 98 | const c = a.add(b); | ||
| 99 | |||
| 100 | testing.expect(c.re == 7 and c.im == 10); | ||
| 101 | } | ||
| 102 | |||
| 103 | test "complex.sub" { | ||
| 104 | const a = Complex(f32).new(5, 3); | ||
| 105 | const b = Complex(f32).new(2, 7); | ||
| 106 | const c = a.sub(b); | ||
| 107 | |||
| 108 | testing.expect(c.re == 3 and c.im == -4); | ||
| 109 | } | ||
| 110 | |||
| 111 | test "complex.mul" { | ||
| 112 | const a = Complex(f32).new(5, 3); | ||
| 113 | const b = Complex(f32).new(2, 7); | ||
| 114 | const c = a.mul(b); | ||
| 115 | |||
| 116 | testing.expect(c.re == -11 and c.im == 41); | ||
| 117 | } | ||
| 118 | |||
| 119 | test "complex.div" { | ||
| 120 | const a = Complex(f32).new(5, 3); | ||
| 121 | const b = Complex(f32).new(2, 7); | ||
| 122 | const c = a.div(b); | ||
| 123 | |||
| 124 | testing.expect(math.approxEq(f32, c.re, f32(31) / 53, epsilon) and | ||
| 125 | math.approxEq(f32, c.im, f32(-29) / 53, epsilon)); | ||
| 126 | } | ||
| 127 | |||
| 128 | test "complex.conjugate" { | ||
| 129 | const a = Complex(f32).new(5, 3); | ||
| 130 | const c = a.conjugate(); | ||
| 131 | |||
| 132 | testing.expect(c.re == 5 and c.im == -3); | ||
| 133 | } | ||
| 134 | |||
| 135 | test "complex.reciprocal" { | ||
| 136 | const a = Complex(f32).new(5, 3); | ||
| 137 | const c = a.reciprocal(); | ||
| 138 | |||
| 139 | testing.expect(math.approxEq(f32, c.re, f32(5) / 34, epsilon) and | ||
| 140 | math.approxEq(f32, c.im, f32(-3) / 34, epsilon)); | ||
| 141 | } | ||
| 142 | |||
| 143 | test "complex.magnitude" { | ||
| 144 | const a = Complex(f32).new(5, 3); | ||
| 145 | const c = a.magnitude(); | ||
| 146 | |||
| 147 | testing.expect(math.approxEq(f32, c, 5.83095, epsilon)); | ||
| 148 | } | ||
| 149 | |||
| 150 | test "complex.cmath" { | ||
| 151 | _ = @import("abs.zig"); | ||
| 152 | _ = @import("acosh.zig"); | ||
| 153 | _ = @import("acos.zig"); | ||
| 154 | _ = @import("arg.zig"); | ||
| 155 | _ = @import("asinh.zig"); | ||
| 156 | _ = @import("asin.zig"); | ||
| 157 | _ = @import("atanh.zig"); | ||
| 158 | _ = @import("atan.zig"); | ||
| 159 | _ = @import("conj.zig"); | ||
| 160 | _ = @import("cosh.zig"); | ||
| 161 | _ = @import("cos.zig"); | ||
| 162 | _ = @import("exp.zig"); | ||
| 163 | _ = @import("log.zig"); | ||
| 164 | _ = @import("pow.zig"); | ||
| 165 | _ = @import("proj.zig"); | ||
| 166 | _ = @import("sinh.zig"); | ||
| 167 | _ = @import("sin.zig"); | ||
| 168 | _ = @import("sqrt.zig"); | ||
| 169 | _ = @import("tanh.zig"); | ||
| 170 | _ = @import("tan.zig"); | ||
| 171 | } | ||
std/math/complex/ldexp.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/log.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/pow.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/proj.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/sin.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/sinh.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/sqrt.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/tan.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/complex/tanh.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const testing = std.testing; | 2 | const testing = std.testing; |
| 3 | const math = std.math; | 3 | const math = std.math; |
| 4 | const cmath = math.complex; | 4 | const cmath = math.complex; |
std/math/copysign.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
std/math/cos.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - cos(nan) = nan | 4 | // - cos(nan) = nan |
| 5 | 5 | ||
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/cosh.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - cosh(nan) = nan | 5 | // - cosh(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expo2 = @import("expo2.zig").expo2; | 10 | const expo2 = @import("expo2.zig").expo2; |
| 11 | const expect = std.testing.expect; | 11 | const expect = std.testing.expect; |
std/math/exp.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // - exp(+inf) = +inf | 3 | // - exp(+inf) = +inf |
| 4 | // - exp(nan) = nan | 4 | // - exp(nan) = nan |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const assert = std.debug.assert; | 8 | const assert = std.debug.assert; |
| 9 | const builtin = @import("builtin"); | 9 | const builtin = @import("builtin"); |
std/math/exp2.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // - exp2(+inf) = +inf | 3 | // - exp2(+inf) = +inf |
| 4 | // - exp2(nan) = nan | 4 | // - exp2(nan) = nan |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | 9 |
std/math/expm1.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - expm1(nan) = nan | 5 | // - expm1(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/expo2.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const math = @import("index.zig"); | 1 | const math = @import("../math.zig"); |
| 2 | 2 | ||
| 3 | pub fn expo2(x: var) @typeOf(x) { | 3 | pub fn expo2(x: var) @typeOf(x) { |
| 4 | const T = @typeOf(x); | 4 | const T = @typeOf(x); |
std/math/fabs.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // - fabs(+-inf) = +inf | 3 | // - fabs(+-inf) = +inf |
| 4 | // - fabs(nan) = nan | 4 | // - fabs(nan) = nan |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | const maxInt = std.math.maxInt; | 9 | const maxInt = std.math.maxInt; |
std/math/floor.zig+1-1| ... | @@ -6,7 +6,7 @@ | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | const std = @import("../index.zig"); | 9 | const std = @import("../std.zig"); |
| 10 | const math = std.math; | 10 | const math = std.math; |
| 11 | 11 | ||
| 12 | pub fn floor(x: var) @typeOf(x) { | 12 | pub fn floor(x: var) @typeOf(x) { |
std/math/fma.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | 4 |
std/math/frexp.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - frexp(+-inf) = +-inf, 0 | 4 | // - frexp(+-inf) = +-inf, 0 |
| 5 | // - frexp(nan) = nan, undefined | 5 | // - frexp(nan) = nan, undefined |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/hypot.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - hypot(nan, y) = nan | 5 | // - hypot(nan, y) = nan |
| 6 | // - hypot(x, nan) = nan | 6 | // - hypot(x, nan) = nan |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const maxInt = std.math.maxInt; | 11 | const maxInt = std.math.maxInt; |
std/math/ilogb.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - ilogb(0) = maxInt(i32) | 4 | // - ilogb(0) = maxInt(i32) |
| 5 | // - ilogb(nan) = maxInt(i32) | 5 | // - ilogb(nan) = maxInt(i32) |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
std/math/index.zig deleted-797| ... | @@ -1,797 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("../index.zig"); | ||
| 3 | const TypeId = builtin.TypeId; | ||
| 4 | const assert = std.debug.assert; | ||
| 5 | const testing = std.testing; | ||
| 6 | |||
| 7 | pub const e = 2.71828182845904523536028747135266249775724709369995; | ||
| 8 | pub const pi = 3.14159265358979323846264338327950288419716939937510; | ||
| 9 | |||
| 10 | // From a small c++ [program using boost float128](https://github.com/winksaville/cpp_boost_float128) | ||
| 11 | pub const f128_true_min = @bitCast(f128, u128(0x00000000000000000000000000000001)); | ||
| 12 | pub const f128_min = @bitCast(f128, u128(0x00010000000000000000000000000000)); | ||
| 13 | pub const f128_max = @bitCast(f128, u128(0x7FFEFFFFFFFFFFFFFFFFFFFFFFFFFFFF)); | ||
| 14 | pub const f128_epsilon = @bitCast(f128, u128(0x3F8F0000000000000000000000000000)); | ||
| 15 | pub const f128_toint = 1.0 / f128_epsilon; | ||
| 16 | |||
| 17 | // float.h details | ||
| 18 | pub const f64_true_min = 4.94065645841246544177e-324; | ||
| 19 | pub const f64_min = 2.2250738585072014e-308; | ||
| 20 | pub const f64_max = 1.79769313486231570815e+308; | ||
| 21 | pub const f64_epsilon = 2.22044604925031308085e-16; | ||
| 22 | pub const f64_toint = 1.0 / f64_epsilon; | ||
| 23 | |||
| 24 | pub const f32_true_min = 1.40129846432481707092e-45; | ||
| 25 | pub const f32_min = 1.17549435082228750797e-38; | ||
| 26 | pub const f32_max = 3.40282346638528859812e+38; | ||
| 27 | pub const f32_epsilon = 1.1920928955078125e-07; | ||
| 28 | pub const f32_toint = 1.0 / f32_epsilon; | ||
| 29 | |||
| 30 | pub const f16_true_min = 0.000000059604644775390625; // 2**-24 | ||
| 31 | pub const f16_min = 0.00006103515625; // 2**-14 | ||
| 32 | pub const f16_max = 65504; | ||
| 33 | pub const f16_epsilon = 0.0009765625; // 2**-10 | ||
| 34 | pub const f16_toint = 1.0 / f16_epsilon; | ||
| 35 | |||
| 36 | pub const nan_u16 = u16(0x7C01); | ||
| 37 | pub const nan_f16 = @bitCast(f16, nan_u16); | ||
| 38 | |||
| 39 | pub const inf_u16 = u16(0x7C00); | ||
| 40 | pub const inf_f16 = @bitCast(f16, inf_u16); | ||
| 41 | |||
| 42 | pub const nan_u32 = u32(0x7F800001); | ||
| 43 | pub const nan_f32 = @bitCast(f32, nan_u32); | ||
| 44 | |||
| 45 | pub const inf_u32 = u32(0x7F800000); | ||
| 46 | pub const inf_f32 = @bitCast(f32, inf_u32); | ||
| 47 | |||
| 48 | pub const nan_u64 = u64(0x7FF << 52) | 1; | ||
| 49 | pub const nan_f64 = @bitCast(f64, nan_u64); | ||
| 50 | |||
| 51 | pub const inf_u64 = u64(0x7FF << 52); | ||
| 52 | pub const inf_f64 = @bitCast(f64, inf_u64); | ||
| 53 | |||
| 54 | pub const nan_u128 = u128(0x7fff0000000000000000000000000001); | ||
| 55 | pub const nan_f128 = @bitCast(f128, nan_u128); | ||
| 56 | |||
| 57 | pub const inf_u128 = u128(0x7fff0000000000000000000000000000); | ||
| 58 | pub const inf_f128 = @bitCast(f128, inf_u128); | ||
| 59 | |||
| 60 | pub const nan = @import("nan.zig").nan; | ||
| 61 | pub const snan = @import("nan.zig").snan; | ||
| 62 | pub const inf = @import("inf.zig").inf; | ||
| 63 | |||
| 64 | pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) bool { | ||
| 65 | assert(@typeId(T) == TypeId.Float); | ||
| 66 | return fabs(x - y) < epsilon; | ||
| 67 | } | ||
| 68 | |||
| 69 | // TODO: Hide the following in an internal module. | ||
| 70 | pub fn forceEval(value: var) void { | ||
| 71 | const T = @typeOf(value); | ||
| 72 | switch (T) { | ||
| 73 | f16 => { | ||
| 74 | var x: f16 = undefined; | ||
| 75 | const p = @ptrCast(*volatile f16, &x); | ||
| 76 | p.* = x; | ||
| 77 | }, | ||
| 78 | f32 => { | ||
| 79 | var x: f32 = undefined; | ||
| 80 | const p = @ptrCast(*volatile f32, &x); | ||
| 81 | p.* = x; | ||
| 82 | }, | ||
| 83 | f64 => { | ||
| 84 | var x: f64 = undefined; | ||
| 85 | const p = @ptrCast(*volatile f64, &x); | ||
| 86 | p.* = x; | ||
| 87 | }, | ||
| 88 | else => { | ||
| 89 | @compileError("forceEval not implemented for " ++ @typeName(T)); | ||
| 90 | }, | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn raiseInvalid() void { | ||
| 95 | // Raise INVALID fpu exception | ||
| 96 | } | ||
| 97 | |||
| 98 | pub fn raiseUnderflow() void { | ||
| 99 | // Raise UNDERFLOW fpu exception | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn raiseOverflow() void { | ||
| 103 | // Raise OVERFLOW fpu exception | ||
| 104 | } | ||
| 105 | |||
| 106 | pub fn raiseInexact() void { | ||
| 107 | // Raise INEXACT fpu exception | ||
| 108 | } | ||
| 109 | |||
| 110 | pub fn raiseDivByZero() void { | ||
| 111 | // Raise INEXACT fpu exception | ||
| 112 | } | ||
| 113 | |||
| 114 | pub const isNan = @import("isnan.zig").isNan; | ||
| 115 | pub const isSignalNan = @import("isnan.zig").isSignalNan; | ||
| 116 | pub const fabs = @import("fabs.zig").fabs; | ||
| 117 | pub const ceil = @import("ceil.zig").ceil; | ||
| 118 | pub const floor = @import("floor.zig").floor; | ||
| 119 | pub const trunc = @import("trunc.zig").trunc; | ||
| 120 | pub const round = @import("round.zig").round; | ||
| 121 | pub const frexp = @import("frexp.zig").frexp; | ||
| 122 | pub const frexp32_result = @import("frexp.zig").frexp32_result; | ||
| 123 | pub const frexp64_result = @import("frexp.zig").frexp64_result; | ||
| 124 | pub const modf = @import("modf.zig").modf; | ||
| 125 | pub const modf32_result = @import("modf.zig").modf32_result; | ||
| 126 | pub const modf64_result = @import("modf.zig").modf64_result; | ||
| 127 | pub const copysign = @import("copysign.zig").copysign; | ||
| 128 | pub const isFinite = @import("isfinite.zig").isFinite; | ||
| 129 | pub const isInf = @import("isinf.zig").isInf; | ||
| 130 | pub const isPositiveInf = @import("isinf.zig").isPositiveInf; | ||
| 131 | pub const isNegativeInf = @import("isinf.zig").isNegativeInf; | ||
| 132 | pub const isNormal = @import("isnormal.zig").isNormal; | ||
| 133 | pub const signbit = @import("signbit.zig").signbit; | ||
| 134 | pub const scalbn = @import("scalbn.zig").scalbn; | ||
| 135 | pub const pow = @import("pow.zig").pow; | ||
| 136 | pub const powi = @import("powi.zig").powi; | ||
| 137 | pub const sqrt = @import("sqrt.zig").sqrt; | ||
| 138 | pub const cbrt = @import("cbrt.zig").cbrt; | ||
| 139 | pub const acos = @import("acos.zig").acos; | ||
| 140 | pub const asin = @import("asin.zig").asin; | ||
| 141 | pub const atan = @import("atan.zig").atan; | ||
| 142 | pub const atan2 = @import("atan2.zig").atan2; | ||
| 143 | pub const hypot = @import("hypot.zig").hypot; | ||
| 144 | pub const exp = @import("exp.zig").exp; | ||
| 145 | pub const exp2 = @import("exp2.zig").exp2; | ||
| 146 | pub const expm1 = @import("expm1.zig").expm1; | ||
| 147 | pub const ilogb = @import("ilogb.zig").ilogb; | ||
| 148 | pub const ln = @import("ln.zig").ln; | ||
| 149 | pub const log = @import("log.zig").log; | ||
| 150 | pub const log2 = @import("log2.zig").log2; | ||
| 151 | pub const log10 = @import("log10.zig").log10; | ||
| 152 | pub const log1p = @import("log1p.zig").log1p; | ||
| 153 | pub const fma = @import("fma.zig").fma; | ||
| 154 | pub const asinh = @import("asinh.zig").asinh; | ||
| 155 | pub const acosh = @import("acosh.zig").acosh; | ||
| 156 | pub const atanh = @import("atanh.zig").atanh; | ||
| 157 | pub const sinh = @import("sinh.zig").sinh; | ||
| 158 | pub const cosh = @import("cosh.zig").cosh; | ||
| 159 | pub const tanh = @import("tanh.zig").tanh; | ||
| 160 | pub const cos = @import("cos.zig").cos; | ||
| 161 | pub const sin = @import("sin.zig").sin; | ||
| 162 | pub const tan = @import("tan.zig").tan; | ||
| 163 | |||
| 164 | pub const complex = @import("complex/index.zig"); | ||
| 165 | pub const Complex = complex.Complex; | ||
| 166 | |||
| 167 | pub const big = @import("big/index.zig"); | ||
| 168 | |||
| 169 | test "math" { | ||
| 170 | _ = @import("nan.zig"); | ||
| 171 | _ = @import("isnan.zig"); | ||
| 172 | _ = @import("fabs.zig"); | ||
| 173 | _ = @import("ceil.zig"); | ||
| 174 | _ = @import("floor.zig"); | ||
| 175 | _ = @import("trunc.zig"); | ||
| 176 | _ = @import("round.zig"); | ||
| 177 | _ = @import("frexp.zig"); | ||
| 178 | _ = @import("modf.zig"); | ||
| 179 | _ = @import("copysign.zig"); | ||
| 180 | _ = @import("isfinite.zig"); | ||
| 181 | _ = @import("isinf.zig"); | ||
| 182 | _ = @import("isnormal.zig"); | ||
| 183 | _ = @import("signbit.zig"); | ||
| 184 | _ = @import("scalbn.zig"); | ||
| 185 | _ = @import("pow.zig"); | ||
| 186 | _ = @import("powi.zig"); | ||
| 187 | _ = @import("sqrt.zig"); | ||
| 188 | _ = @import("cbrt.zig"); | ||
| 189 | _ = @import("acos.zig"); | ||
| 190 | _ = @import("asin.zig"); | ||
| 191 | _ = @import("atan.zig"); | ||
| 192 | _ = @import("atan2.zig"); | ||
| 193 | _ = @import("hypot.zig"); | ||
| 194 | _ = @import("exp.zig"); | ||
| 195 | _ = @import("exp2.zig"); | ||
| 196 | _ = @import("expm1.zig"); | ||
| 197 | _ = @import("ilogb.zig"); | ||
| 198 | _ = @import("ln.zig"); | ||
| 199 | _ = @import("log.zig"); | ||
| 200 | _ = @import("log2.zig"); | ||
| 201 | _ = @import("log10.zig"); | ||
| 202 | _ = @import("log1p.zig"); | ||
| 203 | _ = @import("fma.zig"); | ||
| 204 | _ = @import("asinh.zig"); | ||
| 205 | _ = @import("acosh.zig"); | ||
| 206 | _ = @import("atanh.zig"); | ||
| 207 | _ = @import("sinh.zig"); | ||
| 208 | _ = @import("cosh.zig"); | ||
| 209 | _ = @import("tanh.zig"); | ||
| 210 | _ = @import("sin.zig"); | ||
| 211 | _ = @import("cos.zig"); | ||
| 212 | _ = @import("tan.zig"); | ||
| 213 | |||
| 214 | _ = @import("complex/index.zig"); | ||
| 215 | |||
| 216 | _ = @import("big/index.zig"); | ||
| 217 | } | ||
| 218 | |||
| 219 | pub fn floatMantissaBits(comptime T: type) comptime_int { | ||
| 220 | assert(@typeId(T) == builtin.TypeId.Float); | ||
| 221 | |||
| 222 | return switch (T.bit_count) { | ||
| 223 | 16 => 10, | ||
| 224 | 32 => 23, | ||
| 225 | 64 => 52, | ||
| 226 | 80 => 64, | ||
| 227 | 128 => 112, | ||
| 228 | else => @compileError("unknown floating point type " ++ @typeName(T)), | ||
| 229 | }; | ||
| 230 | } | ||
| 231 | |||
| 232 | pub fn floatExponentBits(comptime T: type) comptime_int { | ||
| 233 | assert(@typeId(T) == builtin.TypeId.Float); | ||
| 234 | |||
| 235 | return switch (T.bit_count) { | ||
| 236 | 16 => 5, | ||
| 237 | 32 => 8, | ||
| 238 | 64 => 11, | ||
| 239 | 80 => 15, | ||
| 240 | 128 => 15, | ||
| 241 | else => @compileError("unknown floating point type " ++ @typeName(T)), | ||
| 242 | }; | ||
| 243 | } | ||
| 244 | |||
| 245 | pub fn min(x: var, y: var) @typeOf(x + y) { | ||
| 246 | return if (x < y) x else y; | ||
| 247 | } | ||
| 248 | |||
| 249 | test "math.min" { | ||
| 250 | testing.expect(min(i32(-1), i32(2)) == -1); | ||
| 251 | } | ||
| 252 | |||
| 253 | pub fn max(x: var, y: var) @typeOf(x + y) { | ||
| 254 | return if (x > y) x else y; | ||
| 255 | } | ||
| 256 | |||
| 257 | test "math.max" { | ||
| 258 | testing.expect(max(i32(-1), i32(2)) == 2); | ||
| 259 | } | ||
| 260 | |||
| 261 | pub fn mul(comptime T: type, a: T, b: T) (error{Overflow}!T) { | ||
| 262 | var answer: T = undefined; | ||
| 263 | return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer; | ||
| 264 | } | ||
| 265 | |||
| 266 | pub fn add(comptime T: type, a: T, b: T) (error{Overflow}!T) { | ||
| 267 | var answer: T = undefined; | ||
| 268 | return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer; | ||
| 269 | } | ||
| 270 | |||
| 271 | pub fn sub(comptime T: type, a: T, b: T) (error{Overflow}!T) { | ||
| 272 | var answer: T = undefined; | ||
| 273 | return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer; | ||
| 274 | } | ||
| 275 | |||
| 276 | pub fn negate(x: var) !@typeOf(x) { | ||
| 277 | return sub(@typeOf(x), 0, x); | ||
| 278 | } | ||
| 279 | |||
| 280 | pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) !T { | ||
| 281 | var answer: T = undefined; | ||
| 282 | return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer; | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Shifts left. Overflowed bits are truncated. | ||
| 286 | /// A negative shift amount results in a right shift. | ||
| 287 | pub fn shl(comptime T: type, a: T, shift_amt: var) T { | ||
| 288 | const abs_shift_amt = absCast(shift_amt); | ||
| 289 | const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); | ||
| 290 | |||
| 291 | if (@typeOf(shift_amt).is_signed) { | ||
| 292 | if (shift_amt >= 0) { | ||
| 293 | return a << casted_shift_amt; | ||
| 294 | } else { | ||
| 295 | return a >> casted_shift_amt; | ||
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | return a << casted_shift_amt; | ||
| 300 | } | ||
| 301 | |||
| 302 | test "math.shl" { | ||
| 303 | testing.expect(shl(u8, 0b11111111, usize(3)) == 0b11111000); | ||
| 304 | testing.expect(shl(u8, 0b11111111, usize(8)) == 0); | ||
| 305 | testing.expect(shl(u8, 0b11111111, usize(9)) == 0); | ||
| 306 | testing.expect(shl(u8, 0b11111111, isize(-2)) == 0b00111111); | ||
| 307 | } | ||
| 308 | |||
| 309 | /// Shifts right. Overflowed bits are truncated. | ||
| 310 | /// A negative shift amount results in a lefft shift. | ||
| 311 | pub fn shr(comptime T: type, a: T, shift_amt: var) T { | ||
| 312 | const abs_shift_amt = absCast(shift_amt); | ||
| 313 | const casted_shift_amt = if (abs_shift_amt >= T.bit_count) return 0 else @intCast(Log2Int(T), abs_shift_amt); | ||
| 314 | |||
| 315 | if (@typeOf(shift_amt).is_signed) { | ||
| 316 | if (shift_amt >= 0) { | ||
| 317 | return a >> casted_shift_amt; | ||
| 318 | } else { | ||
| 319 | return a << casted_shift_amt; | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | return a >> casted_shift_amt; | ||
| 324 | } | ||
| 325 | |||
| 326 | test "math.shr" { | ||
| 327 | testing.expect(shr(u8, 0b11111111, usize(3)) == 0b00011111); | ||
| 328 | testing.expect(shr(u8, 0b11111111, usize(8)) == 0); | ||
| 329 | testing.expect(shr(u8, 0b11111111, usize(9)) == 0); | ||
| 330 | testing.expect(shr(u8, 0b11111111, isize(-2)) == 0b11111100); | ||
| 331 | } | ||
| 332 | |||
| 333 | /// Rotates right. Only unsigned values can be rotated. | ||
| 334 | /// Negative shift values results in shift modulo the bit count. | ||
| 335 | pub fn rotr(comptime T: type, x: T, r: var) T { | ||
| 336 | if (T.is_signed) { | ||
| 337 | @compileError("cannot rotate signed integer"); | ||
| 338 | } else { | ||
| 339 | const ar = @mod(r, T.bit_count); | ||
| 340 | return shr(T, x, ar) | shl(T, x, T.bit_count - ar); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | test "math.rotr" { | ||
| 345 | testing.expect(rotr(u8, 0b00000001, usize(0)) == 0b00000001); | ||
| 346 | testing.expect(rotr(u8, 0b00000001, usize(9)) == 0b10000000); | ||
| 347 | testing.expect(rotr(u8, 0b00000001, usize(8)) == 0b00000001); | ||
| 348 | testing.expect(rotr(u8, 0b00000001, usize(4)) == 0b00010000); | ||
| 349 | testing.expect(rotr(u8, 0b00000001, isize(-1)) == 0b00000010); | ||
| 350 | } | ||
| 351 | |||
| 352 | /// Rotates left. Only unsigned values can be rotated. | ||
| 353 | /// Negative shift values results in shift modulo the bit count. | ||
| 354 | pub fn rotl(comptime T: type, x: T, r: var) T { | ||
| 355 | if (T.is_signed) { | ||
| 356 | @compileError("cannot rotate signed integer"); | ||
| 357 | } else { | ||
| 358 | const ar = @mod(r, T.bit_count); | ||
| 359 | return shl(T, x, ar) | shr(T, x, T.bit_count - ar); | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | test "math.rotl" { | ||
| 364 | testing.expect(rotl(u8, 0b00000001, usize(0)) == 0b00000001); | ||
| 365 | testing.expect(rotl(u8, 0b00000001, usize(9)) == 0b00000010); | ||
| 366 | testing.expect(rotl(u8, 0b00000001, usize(8)) == 0b00000001); | ||
| 367 | testing.expect(rotl(u8, 0b00000001, usize(4)) == 0b00010000); | ||
| 368 | testing.expect(rotl(u8, 0b00000001, isize(-1)) == 0b10000000); | ||
| 369 | } | ||
| 370 | |||
| 371 | pub fn Log2Int(comptime T: type) type { | ||
| 372 | // comptime ceil log2 | ||
| 373 | comptime var count = 0; | ||
| 374 | comptime var s = T.bit_count - 1; | ||
| 375 | inline while (s != 0) : (s >>= 1) { | ||
| 376 | count += 1; | ||
| 377 | } | ||
| 378 | |||
| 379 | return @IntType(false, count); | ||
| 380 | } | ||
| 381 | |||
| 382 | pub fn IntFittingRange(comptime from: comptime_int, comptime to: comptime_int) type { | ||
| 383 | assert(from <= to); | ||
| 384 | if (from == 0 and to == 0) { | ||
| 385 | return u0; | ||
| 386 | } | ||
| 387 | const is_signed = from < 0; | ||
| 388 | const largest_positive_integer = max(if (from < 0) (-from) - 1 else from, to); // two's complement | ||
| 389 | const base = log2(largest_positive_integer); | ||
| 390 | const upper = (1 << base) - 1; | ||
| 391 | var magnitude_bits = if (upper >= largest_positive_integer) base else base + 1; | ||
| 392 | if (is_signed) { | ||
| 393 | magnitude_bits += 1; | ||
| 394 | } | ||
| 395 | return @IntType(is_signed, magnitude_bits); | ||
| 396 | } | ||
| 397 | |||
| 398 | test "math.IntFittingRange" { | ||
| 399 | testing.expect(IntFittingRange(0, 0) == u0); | ||
| 400 | testing.expect(IntFittingRange(0, 1) == u1); | ||
| 401 | testing.expect(IntFittingRange(0, 2) == u2); | ||
| 402 | testing.expect(IntFittingRange(0, 3) == u2); | ||
| 403 | testing.expect(IntFittingRange(0, 4) == u3); | ||
| 404 | testing.expect(IntFittingRange(0, 7) == u3); | ||
| 405 | testing.expect(IntFittingRange(0, 8) == u4); | ||
| 406 | testing.expect(IntFittingRange(0, 9) == u4); | ||
| 407 | testing.expect(IntFittingRange(0, 15) == u4); | ||
| 408 | testing.expect(IntFittingRange(0, 16) == u5); | ||
| 409 | testing.expect(IntFittingRange(0, 17) == u5); | ||
| 410 | testing.expect(IntFittingRange(0, 4095) == u12); | ||
| 411 | testing.expect(IntFittingRange(2000, 4095) == u12); | ||
| 412 | testing.expect(IntFittingRange(0, 4096) == u13); | ||
| 413 | testing.expect(IntFittingRange(2000, 4096) == u13); | ||
| 414 | testing.expect(IntFittingRange(0, 4097) == u13); | ||
| 415 | testing.expect(IntFittingRange(2000, 4097) == u13); | ||
| 416 | testing.expect(IntFittingRange(0, 123456789123456798123456789) == u87); | ||
| 417 | testing.expect(IntFittingRange(0, 123456789123456798123456789123456789123456798123456789) == u177); | ||
| 418 | |||
| 419 | testing.expect(IntFittingRange(-1, -1) == i1); | ||
| 420 | testing.expect(IntFittingRange(-1, 0) == i1); | ||
| 421 | testing.expect(IntFittingRange(-1, 1) == i2); | ||
| 422 | testing.expect(IntFittingRange(-2, -2) == i2); | ||
| 423 | testing.expect(IntFittingRange(-2, -1) == i2); | ||
| 424 | testing.expect(IntFittingRange(-2, 0) == i2); | ||
| 425 | testing.expect(IntFittingRange(-2, 1) == i2); | ||
| 426 | testing.expect(IntFittingRange(-2, 2) == i3); | ||
| 427 | testing.expect(IntFittingRange(-1, 2) == i3); | ||
| 428 | testing.expect(IntFittingRange(-1, 3) == i3); | ||
| 429 | testing.expect(IntFittingRange(-1, 4) == i4); | ||
| 430 | testing.expect(IntFittingRange(-1, 7) == i4); | ||
| 431 | testing.expect(IntFittingRange(-1, 8) == i5); | ||
| 432 | testing.expect(IntFittingRange(-1, 9) == i5); | ||
| 433 | testing.expect(IntFittingRange(-1, 15) == i5); | ||
| 434 | testing.expect(IntFittingRange(-1, 16) == i6); | ||
| 435 | testing.expect(IntFittingRange(-1, 17) == i6); | ||
| 436 | testing.expect(IntFittingRange(-1, 4095) == i13); | ||
| 437 | testing.expect(IntFittingRange(-4096, 4095) == i13); | ||
| 438 | testing.expect(IntFittingRange(-1, 4096) == i14); | ||
| 439 | testing.expect(IntFittingRange(-4097, 4095) == i14); | ||
| 440 | testing.expect(IntFittingRange(-1, 4097) == i14); | ||
| 441 | testing.expect(IntFittingRange(-1, 123456789123456798123456789) == i88); | ||
| 442 | testing.expect(IntFittingRange(-1, 123456789123456798123456789123456789123456798123456789) == i178); | ||
| 443 | } | ||
| 444 | |||
| 445 | test "math overflow functions" { | ||
| 446 | testOverflow(); | ||
| 447 | comptime testOverflow(); | ||
| 448 | } | ||
| 449 | |||
| 450 | fn testOverflow() void { | ||
| 451 | testing.expect((mul(i32, 3, 4) catch unreachable) == 12); | ||
| 452 | testing.expect((add(i32, 3, 4) catch unreachable) == 7); | ||
| 453 | testing.expect((sub(i32, 3, 4) catch unreachable) == -1); | ||
| 454 | testing.expect((shlExact(i32, 0b11, 4) catch unreachable) == 0b110000); | ||
| 455 | } | ||
| 456 | |||
| 457 | pub fn absInt(x: var) !@typeOf(x) { | ||
| 458 | const T = @typeOf(x); | ||
| 459 | comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer to absInt | ||
| 460 | comptime assert(T.is_signed); // must pass a signed integer to absInt | ||
| 461 | |||
| 462 | if (x == minInt(@typeOf(x))) { | ||
| 463 | return error.Overflow; | ||
| 464 | } else { | ||
| 465 | @setRuntimeSafety(false); | ||
| 466 | return if (x < 0) -x else x; | ||
| 467 | } | ||
| 468 | } | ||
| 469 | |||
| 470 | test "math.absInt" { | ||
| 471 | testAbsInt(); | ||
| 472 | comptime testAbsInt(); | ||
| 473 | } | ||
| 474 | fn testAbsInt() void { | ||
| 475 | testing.expect((absInt(i32(-10)) catch unreachable) == 10); | ||
| 476 | testing.expect((absInt(i32(10)) catch unreachable) == 10); | ||
| 477 | } | ||
| 478 | |||
| 479 | pub const absFloat = @import("fabs.zig").fabs; | ||
| 480 | |||
| 481 | pub fn divTrunc(comptime T: type, numerator: T, denominator: T) !T { | ||
| 482 | @setRuntimeSafety(false); | ||
| 483 | if (denominator == 0) return error.DivisionByZero; | ||
| 484 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; | ||
| 485 | return @divTrunc(numerator, denominator); | ||
| 486 | } | ||
| 487 | |||
| 488 | test "math.divTrunc" { | ||
| 489 | testDivTrunc(); | ||
| 490 | comptime testDivTrunc(); | ||
| 491 | } | ||
| 492 | fn testDivTrunc() void { | ||
| 493 | testing.expect((divTrunc(i32, 5, 3) catch unreachable) == 1); | ||
| 494 | testing.expect((divTrunc(i32, -5, 3) catch unreachable) == -1); | ||
| 495 | testing.expectError(error.DivisionByZero, divTrunc(i8, -5, 0)); | ||
| 496 | testing.expectError(error.Overflow, divTrunc(i8, -128, -1)); | ||
| 497 | |||
| 498 | testing.expect((divTrunc(f32, 5.0, 3.0) catch unreachable) == 1.0); | ||
| 499 | testing.expect((divTrunc(f32, -5.0, 3.0) catch unreachable) == -1.0); | ||
| 500 | } | ||
| 501 | |||
| 502 | pub fn divFloor(comptime T: type, numerator: T, denominator: T) !T { | ||
| 503 | @setRuntimeSafety(false); | ||
| 504 | if (denominator == 0) return error.DivisionByZero; | ||
| 505 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; | ||
| 506 | return @divFloor(numerator, denominator); | ||
| 507 | } | ||
| 508 | |||
| 509 | test "math.divFloor" { | ||
| 510 | testDivFloor(); | ||
| 511 | comptime testDivFloor(); | ||
| 512 | } | ||
| 513 | fn testDivFloor() void { | ||
| 514 | testing.expect((divFloor(i32, 5, 3) catch unreachable) == 1); | ||
| 515 | testing.expect((divFloor(i32, -5, 3) catch unreachable) == -2); | ||
| 516 | testing.expectError(error.DivisionByZero, divFloor(i8, -5, 0)); | ||
| 517 | testing.expectError(error.Overflow, divFloor(i8, -128, -1)); | ||
| 518 | |||
| 519 | testing.expect((divFloor(f32, 5.0, 3.0) catch unreachable) == 1.0); | ||
| 520 | testing.expect((divFloor(f32, -5.0, 3.0) catch unreachable) == -2.0); | ||
| 521 | } | ||
| 522 | |||
| 523 | pub fn divExact(comptime T: type, numerator: T, denominator: T) !T { | ||
| 524 | @setRuntimeSafety(false); | ||
| 525 | if (denominator == 0) return error.DivisionByZero; | ||
| 526 | if (@typeId(T) == builtin.TypeId.Int and T.is_signed and numerator == minInt(T) and denominator == -1) return error.Overflow; | ||
| 527 | const result = @divTrunc(numerator, denominator); | ||
| 528 | if (result * denominator != numerator) return error.UnexpectedRemainder; | ||
| 529 | return result; | ||
| 530 | } | ||
| 531 | |||
| 532 | test "math.divExact" { | ||
| 533 | testDivExact(); | ||
| 534 | comptime testDivExact(); | ||
| 535 | } | ||
| 536 | fn testDivExact() void { | ||
| 537 | testing.expect((divExact(i32, 10, 5) catch unreachable) == 2); | ||
| 538 | testing.expect((divExact(i32, -10, 5) catch unreachable) == -2); | ||
| 539 | testing.expectError(error.DivisionByZero, divExact(i8, -5, 0)); | ||
| 540 | testing.expectError(error.Overflow, divExact(i8, -128, -1)); | ||
| 541 | testing.expectError(error.UnexpectedRemainder, divExact(i32, 5, 2)); | ||
| 542 | |||
| 543 | testing.expect((divExact(f32, 10.0, 5.0) catch unreachable) == 2.0); | ||
| 544 | testing.expect((divExact(f32, -10.0, 5.0) catch unreachable) == -2.0); | ||
| 545 | testing.expectError(error.UnexpectedRemainder, divExact(f32, 5.0, 2.0)); | ||
| 546 | } | ||
| 547 | |||
| 548 | pub fn mod(comptime T: type, numerator: T, denominator: T) !T { | ||
| 549 | @setRuntimeSafety(false); | ||
| 550 | if (denominator == 0) return error.DivisionByZero; | ||
| 551 | if (denominator < 0) return error.NegativeDenominator; | ||
| 552 | return @mod(numerator, denominator); | ||
| 553 | } | ||
| 554 | |||
| 555 | test "math.mod" { | ||
| 556 | testMod(); | ||
| 557 | comptime testMod(); | ||
| 558 | } | ||
| 559 | fn testMod() void { | ||
| 560 | testing.expect((mod(i32, -5, 3) catch unreachable) == 1); | ||
| 561 | testing.expect((mod(i32, 5, 3) catch unreachable) == 2); | ||
| 562 | testing.expectError(error.NegativeDenominator, mod(i32, 10, -1)); | ||
| 563 | testing.expectError(error.DivisionByZero, mod(i32, 10, 0)); | ||
| 564 | |||
| 565 | testing.expect((mod(f32, -5, 3) catch unreachable) == 1); | ||
| 566 | testing.expect((mod(f32, 5, 3) catch unreachable) == 2); | ||
| 567 | testing.expectError(error.NegativeDenominator, mod(f32, 10, -1)); | ||
| 568 | testing.expectError(error.DivisionByZero, mod(f32, 10, 0)); | ||
| 569 | } | ||
| 570 | |||
| 571 | pub fn rem(comptime T: type, numerator: T, denominator: T) !T { | ||
| 572 | @setRuntimeSafety(false); | ||
| 573 | if (denominator == 0) return error.DivisionByZero; | ||
| 574 | if (denominator < 0) return error.NegativeDenominator; | ||
| 575 | return @rem(numerator, denominator); | ||
| 576 | } | ||
| 577 | |||
| 578 | test "math.rem" { | ||
| 579 | testRem(); | ||
| 580 | comptime testRem(); | ||
| 581 | } | ||
| 582 | fn testRem() void { | ||
| 583 | testing.expect((rem(i32, -5, 3) catch unreachable) == -2); | ||
| 584 | testing.expect((rem(i32, 5, 3) catch unreachable) == 2); | ||
| 585 | testing.expectError(error.NegativeDenominator, rem(i32, 10, -1)); | ||
| 586 | testing.expectError(error.DivisionByZero, rem(i32, 10, 0)); | ||
| 587 | |||
| 588 | testing.expect((rem(f32, -5, 3) catch unreachable) == -2); | ||
| 589 | testing.expect((rem(f32, 5, 3) catch unreachable) == 2); | ||
| 590 | testing.expectError(error.NegativeDenominator, rem(f32, 10, -1)); | ||
| 591 | testing.expectError(error.DivisionByZero, rem(f32, 10, 0)); | ||
| 592 | } | ||
| 593 | |||
| 594 | /// Returns the absolute value of the integer parameter. | ||
| 595 | /// Result is an unsigned integer. | ||
| 596 | pub fn absCast(x: var) @IntType(false, @typeOf(x).bit_count) { | ||
| 597 | const uint = @IntType(false, @typeOf(x).bit_count); | ||
| 598 | if (x >= 0) return @intCast(uint, x); | ||
| 599 | |||
| 600 | return @intCast(uint, -(x + 1)) + 1; | ||
| 601 | } | ||
| 602 | |||
| 603 | test "math.absCast" { | ||
| 604 | testing.expect(absCast(i32(-999)) == 999); | ||
| 605 | testing.expect(@typeOf(absCast(i32(-999))) == u32); | ||
| 606 | |||
| 607 | testing.expect(absCast(i32(999)) == 999); | ||
| 608 | testing.expect(@typeOf(absCast(i32(999))) == u32); | ||
| 609 | |||
| 610 | testing.expect(absCast(i32(minInt(i32))) == -minInt(i32)); | ||
| 611 | testing.expect(@typeOf(absCast(i32(minInt(i32)))) == u32); | ||
| 612 | } | ||
| 613 | |||
| 614 | /// Returns the negation of the integer parameter. | ||
| 615 | /// Result is a signed integer. | ||
| 616 | pub fn negateCast(x: var) !@IntType(true, @typeOf(x).bit_count) { | ||
| 617 | if (@typeOf(x).is_signed) return negate(x); | ||
| 618 | |||
| 619 | const int = @IntType(true, @typeOf(x).bit_count); | ||
| 620 | if (x > -minInt(int)) return error.Overflow; | ||
| 621 | |||
| 622 | if (x == -minInt(int)) return minInt(int); | ||
| 623 | |||
| 624 | return -@intCast(int, x); | ||
| 625 | } | ||
| 626 | |||
| 627 | test "math.negateCast" { | ||
| 628 | testing.expect((negateCast(u32(999)) catch unreachable) == -999); | ||
| 629 | testing.expect(@typeOf(negateCast(u32(999)) catch unreachable) == i32); | ||
| 630 | |||
| 631 | testing.expect((negateCast(u32(-minInt(i32))) catch unreachable) == minInt(i32)); | ||
| 632 | testing.expect(@typeOf(negateCast(u32(-minInt(i32))) catch unreachable) == i32); | ||
| 633 | |||
| 634 | testing.expectError(error.Overflow, negateCast(u32(maxInt(i32) + 10))); | ||
| 635 | } | ||
| 636 | |||
| 637 | /// Cast an integer to a different integer type. If the value doesn't fit, | ||
| 638 | /// return an error. | ||
| 639 | pub fn cast(comptime T: type, x: var) (error{Overflow}!T) { | ||
| 640 | comptime assert(@typeId(T) == builtin.TypeId.Int); // must pass an integer | ||
| 641 | comptime assert(@typeId(@typeOf(x)) == builtin.TypeId.Int); // must pass an integer | ||
| 642 | if (maxInt(@typeOf(x)) > maxInt(T) and x > maxInt(T)) { | ||
| 643 | return error.Overflow; | ||
| 644 | } else if (minInt(@typeOf(x)) < minInt(T) and x < minInt(T)) { | ||
| 645 | return error.Overflow; | ||
| 646 | } else { | ||
| 647 | return @intCast(T, x); | ||
| 648 | } | ||
| 649 | } | ||
| 650 | |||
| 651 | test "math.cast" { | ||
| 652 | testing.expectError(error.Overflow, cast(u8, u32(300))); | ||
| 653 | testing.expectError(error.Overflow, cast(i8, i32(-200))); | ||
| 654 | testing.expectError(error.Overflow, cast(u8, i8(-1))); | ||
| 655 | testing.expectError(error.Overflow, cast(u64, i8(-1))); | ||
| 656 | |||
| 657 | testing.expect((try cast(u8, u32(255))) == u8(255)); | ||
| 658 | testing.expect(@typeOf(try cast(u8, u32(255))) == u8); | ||
| 659 | } | ||
| 660 | |||
| 661 | pub const AlignCastError = error{UnalignedMemory}; | ||
| 662 | |||
| 663 | /// Align cast a pointer but return an error if it's the wrong alignment | ||
| 664 | pub fn alignCast(comptime alignment: u29, ptr: var) AlignCastError!@typeOf(@alignCast(alignment, ptr)) { | ||
| 665 | const addr = @ptrToInt(ptr); | ||
| 666 | if (addr % alignment != 0) { | ||
| 667 | return error.UnalignedMemory; | ||
| 668 | } | ||
| 669 | return @alignCast(alignment, ptr); | ||
| 670 | } | ||
| 671 | |||
| 672 | pub fn floorPowerOfTwo(comptime T: type, value: T) T { | ||
| 673 | var x = value; | ||
| 674 | |||
| 675 | comptime var i = 1; | ||
| 676 | inline while (T.bit_count > i) : (i *= 2) { | ||
| 677 | x |= (x >> i); | ||
| 678 | } | ||
| 679 | |||
| 680 | return x - (x >> 1); | ||
| 681 | } | ||
| 682 | |||
| 683 | test "math.floorPowerOfTwo" { | ||
| 684 | testFloorPowerOfTwo(); | ||
| 685 | comptime testFloorPowerOfTwo(); | ||
| 686 | } | ||
| 687 | |||
| 688 | pub fn log2_int(comptime T: type, x: T) Log2Int(T) { | ||
| 689 | assert(x != 0); | ||
| 690 | return @intCast(Log2Int(T), T.bit_count - 1 - @clz(x)); | ||
| 691 | } | ||
| 692 | |||
| 693 | pub fn log2_int_ceil(comptime T: type, x: T) Log2Int(T) { | ||
| 694 | assert(x != 0); | ||
| 695 | const log2_val = log2_int(T, x); | ||
| 696 | if (T(1) << log2_val == x) | ||
| 697 | return log2_val; | ||
| 698 | return log2_val + 1; | ||
| 699 | } | ||
| 700 | |||
| 701 | test "std.math.log2_int_ceil" { | ||
| 702 | testing.expect(log2_int_ceil(u32, 1) == 0); | ||
| 703 | testing.expect(log2_int_ceil(u32, 2) == 1); | ||
| 704 | testing.expect(log2_int_ceil(u32, 3) == 2); | ||
| 705 | testing.expect(log2_int_ceil(u32, 4) == 2); | ||
| 706 | testing.expect(log2_int_ceil(u32, 5) == 3); | ||
| 707 | testing.expect(log2_int_ceil(u32, 6) == 3); | ||
| 708 | testing.expect(log2_int_ceil(u32, 7) == 3); | ||
| 709 | testing.expect(log2_int_ceil(u32, 8) == 3); | ||
| 710 | testing.expect(log2_int_ceil(u32, 9) == 4); | ||
| 711 | testing.expect(log2_int_ceil(u32, 10) == 4); | ||
| 712 | } | ||
| 713 | |||
| 714 | fn testFloorPowerOfTwo() void { | ||
| 715 | testing.expect(floorPowerOfTwo(u32, 63) == 32); | ||
| 716 | testing.expect(floorPowerOfTwo(u32, 64) == 64); | ||
| 717 | testing.expect(floorPowerOfTwo(u32, 65) == 64); | ||
| 718 | testing.expect(floorPowerOfTwo(u4, 7) == 4); | ||
| 719 | testing.expect(floorPowerOfTwo(u4, 8) == 8); | ||
| 720 | testing.expect(floorPowerOfTwo(u4, 9) == 8); | ||
| 721 | } | ||
| 722 | |||
| 723 | pub fn lossyCast(comptime T: type, value: var) T { | ||
| 724 | switch (@typeInfo(@typeOf(value))) { | ||
| 725 | builtin.TypeId.Int => return @intToFloat(T, value), | ||
| 726 | builtin.TypeId.Float => return @floatCast(T, value), | ||
| 727 | builtin.TypeId.ComptimeInt => return T(value), | ||
| 728 | builtin.TypeId.ComptimeFloat => return T(value), | ||
| 729 | else => @compileError("bad type"), | ||
| 730 | } | ||
| 731 | } | ||
| 732 | |||
| 733 | test "math.f64_min" { | ||
| 734 | const f64_min_u64 = 0x0010000000000000; | ||
| 735 | const fmin: f64 = f64_min; | ||
| 736 | testing.expect(@bitCast(u64, fmin) == f64_min_u64); | ||
| 737 | } | ||
| 738 | |||
| 739 | pub fn maxInt(comptime T: type) comptime_int { | ||
| 740 | const info = @typeInfo(T); | ||
| 741 | const bit_count = info.Int.bits; | ||
| 742 | if (bit_count == 0) return 0; | ||
| 743 | return (1 << (bit_count - @boolToInt(info.Int.is_signed))) - 1; | ||
| 744 | } | ||
| 745 | |||
| 746 | pub fn minInt(comptime T: type) comptime_int { | ||
| 747 | const info = @typeInfo(T); | ||
| 748 | const bit_count = info.Int.bits; | ||
| 749 | if (!info.Int.is_signed) return 0; | ||
| 750 | if (bit_count == 0) return 0; | ||
| 751 | return -(1 << (bit_count - 1)); | ||
| 752 | } | ||
| 753 | |||
| 754 | test "minInt and maxInt" { | ||
| 755 | testing.expect(maxInt(u0) == 0); | ||
| 756 | testing.expect(maxInt(u1) == 1); | ||
| 757 | testing.expect(maxInt(u8) == 255); | ||
| 758 | testing.expect(maxInt(u16) == 65535); | ||
| 759 | testing.expect(maxInt(u32) == 4294967295); | ||
| 760 | testing.expect(maxInt(u64) == 18446744073709551615); | ||
| 761 | testing.expect(maxInt(u128) == 340282366920938463463374607431768211455); | ||
| 762 | |||
| 763 | testing.expect(maxInt(i0) == 0); | ||
| 764 | testing.expect(maxInt(i1) == 0); | ||
| 765 | testing.expect(maxInt(i8) == 127); | ||
| 766 | testing.expect(maxInt(i16) == 32767); | ||
| 767 | testing.expect(maxInt(i32) == 2147483647); | ||
| 768 | testing.expect(maxInt(i63) == 4611686018427387903); | ||
| 769 | testing.expect(maxInt(i64) == 9223372036854775807); | ||
| 770 | testing.expect(maxInt(i128) == 170141183460469231731687303715884105727); | ||
| 771 | |||
| 772 | testing.expect(minInt(u0) == 0); | ||
| 773 | testing.expect(minInt(u1) == 0); | ||
| 774 | testing.expect(minInt(u8) == 0); | ||
| 775 | testing.expect(minInt(u16) == 0); | ||
| 776 | testing.expect(minInt(u32) == 0); | ||
| 777 | testing.expect(minInt(u63) == 0); | ||
| 778 | testing.expect(minInt(u64) == 0); | ||
| 779 | testing.expect(minInt(u128) == 0); | ||
| 780 | |||
| 781 | testing.expect(minInt(i0) == 0); | ||
| 782 | testing.expect(minInt(i1) == -1); | ||
| 783 | testing.expect(minInt(i8) == -128); | ||
| 784 | testing.expect(minInt(i16) == -32768); | ||
| 785 | testing.expect(minInt(i32) == -2147483648); | ||
| 786 | testing.expect(minInt(i63) == -4611686018427387904); | ||
| 787 | testing.expect(minInt(i64) == -9223372036854775808); | ||
| 788 | testing.expect(minInt(i128) == -170141183460469231731687303715884105728); | ||
| 789 | } | ||
| 790 | |||
| 791 | test "max value type" { | ||
| 792 | // If the type of maxInt(i32) was i32 then this implicit cast to | ||
| 793 | // u32 would not work. But since the value is a number literal, | ||
| 794 | // it works fine. | ||
| 795 | const x: u32 = maxInt(i32); | ||
| 796 | testing.expect(x == 2147483647); | ||
| 797 | } | ||
std/math/inf.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | 3 | ||
| 4 | pub fn inf(comptime T: type) T { | 4 | pub fn inf(comptime T: type) T { |
std/math/isfinite.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
std/math/isinf.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
std/math/isnan.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
std/math/isnormal.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
std/math/ln.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - ln(x) = nan if x < 0 | 5 | // - ln(x) = nan if x < 0 |
| 6 | // - ln(nan) = nan | 6 | // - ln(nan) = nan |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
std/math/log.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const TypeId = builtin.TypeId; | 4 | const TypeId = builtin.TypeId; |
std/math/log10.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - log10(x) = nan if x < 0 | 5 | // - log10(x) = nan if x < 0 |
| 6 | // - log10(nan) = nan | 6 | // - log10(nan) = nan |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const testing = std.testing; | 10 | const testing = std.testing; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
std/math/log1p.zig+1-1| ... | @@ -7,7 +7,7 @@ | ... | @@ -7,7 +7,7 @@ |
| 7 | // - log1p(nan) = nan | 7 | // - log1p(nan) = nan |
| 8 | 8 | ||
| 9 | const builtin = @import("builtin"); | 9 | const builtin = @import("builtin"); |
| 10 | const std = @import("../index.zig"); | 10 | const std = @import("../std.zig"); |
| 11 | const math = std.math; | 11 | const math = std.math; |
| 12 | const expect = std.testing.expect; | 12 | const expect = std.testing.expect; |
| 13 | 13 |
std/math/log2.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - log2(x) = nan if x < 0 | 5 | // - log2(x) = nan if x < 0 |
| 6 | // - log2(nan) = nan | 6 | // - log2(nan) = nan |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
std/math/modf.zig+1-1| ... | @@ -3,7 +3,7 @@ | ... | @@ -3,7 +3,7 @@ |
| 3 | // - modf(+-inf) = +-inf, nan | 3 | // - modf(+-inf) = +-inf, nan |
| 4 | // - modf(nan) = nan, nan | 4 | // - modf(nan) = nan, nan |
| 5 | 5 | ||
| 6 | const std = @import("../index.zig"); | 6 | const std = @import("../std.zig"); |
| 7 | const math = std.math; | 7 | const math = std.math; |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | const maxInt = std.math.maxInt; | 9 | const maxInt = std.math.maxInt; |
std/math/nan.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const math = @import("index.zig"); | 1 | const math = @import("../math.zig"); |
| 2 | 2 | ||
| 3 | pub fn nan(comptime T: type) T { | 3 | pub fn nan(comptime T: type) T { |
| 4 | return switch (T) { | 4 | return switch (T) { |
std/math/pow.zig+1-1| ... | @@ -22,7 +22,7 @@ | ... | @@ -22,7 +22,7 @@ |
| 22 | // pow(x, y) = nan for finite x < 0 and finite non-integer y | 22 | // pow(x, y) = nan for finite x < 0 and finite non-integer y |
| 23 | 23 | ||
| 24 | const builtin = @import("builtin"); | 24 | const builtin = @import("builtin"); |
| 25 | const std = @import("../index.zig"); | 25 | const std = @import("../std.zig"); |
| 26 | const math = std.math; | 26 | const math = std.math; |
| 27 | const expect = std.testing.expect; | 27 | const expect = std.testing.expect; |
| 28 | 28 |
std/math/powi.zig+1-1| ... | @@ -9,7 +9,7 @@ | ... | @@ -9,7 +9,7 @@ |
| 9 | // powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0 | 9 | // powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0 |
| 10 | 10 | ||
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
| 12 | const std = @import("../index.zig"); | 12 | const std = @import("../std.zig"); |
| 13 | const math = std.math; | 13 | const math = std.math; |
| 14 | const assert = std.debug.assert; | 14 | const assert = std.debug.assert; |
| 15 | const testing = std.testing; | 15 | const testing = std.testing; |
std/math/round.zig+1-1| ... | @@ -6,7 +6,7 @@ | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const expect = std.testing.expect; | 8 | const expect = std.testing.expect; |
| 9 | const std = @import("../index.zig"); | 9 | const std = @import("../std.zig"); |
| 10 | const math = std.math; | 10 | const math = std.math; |
| 11 | 11 | ||
| 12 | pub fn round(x: var) @typeOf(x) { | 12 | pub fn round(x: var) @typeOf(x) { |
std/math/scalbn.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | 4 |
std/math/signbit.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; | 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | 4 |
std/math/sin.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - sin(nan) = nan | 5 | // - sin(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/sinh.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - sinh(nan) = nan | 5 | // - sinh(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const expo2 = @import("expo2.zig").expo2; | 11 | const expo2 = @import("expo2.zig").expo2; |
std/math/sqrt.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - sqrt(x) = nan if x < 0 | 5 | // - sqrt(x) = nan if x < 0 |
| 6 | // - sqrt(nan) = nan | 6 | // - sqrt(nan) = nan |
| 7 | 7 | ||
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
std/math/tan.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - tan(nan) = nan | 5 | // - tan(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/tanh.zig+1-1| ... | @@ -5,7 +5,7 @@ | ... | @@ -5,7 +5,7 @@ |
| 5 | // - sinh(nan) = nan | 5 | // - sinh(nan) = nan |
| 6 | 6 | ||
| 7 | const builtin = @import("builtin"); | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | 8 | const std = @import("../std.zig"); |
| 9 | const math = std.math; | 9 | const math = std.math; |
| 10 | const expect = std.testing.expect; | 10 | const expect = std.testing.expect; |
| 11 | const expo2 = @import("expo2.zig").expo2; | 11 | const expo2 = @import("expo2.zig").expo2; |
std/math/trunc.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | // - trunc(+-inf) = +-inf | 4 | // - trunc(+-inf) = +-inf |
| 5 | // - trunc(nan) = nan | 5 | // - trunc(nan) = nan |
| 6 | 6 | ||
| 7 | const std = @import("../index.zig"); | 7 | const std = @import("../std.zig"); |
| 8 | const math = std.math; | 8 | const math = std.math; |
| 9 | const expect = std.testing.expect; | 9 | const expect = std.testing.expect; |
| 10 | const maxInt = std.math.maxInt; | 10 | const maxInt = std.math.maxInt; |
std/mem.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const debug = std.debug; | 2 | const debug = std.debug; |
| 3 | const assert = debug.assert; | 3 | const assert = debug.assert; |
| 4 | const math = std.math; | 4 | const math = std.math; |
std/meta.zig created+569| ... | @@ -0,0 +1,569 @@ | ||
| 1 | const std = @import("std.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const debug = std.debug; | ||
| 4 | const mem = std.mem; | ||
| 5 | const math = std.math; | ||
| 6 | const testing = std.testing; | ||
| 7 | |||
| 8 | pub const trait = @import("meta/trait.zig"); | ||
| 9 | |||
| 10 | const TypeId = builtin.TypeId; | ||
| 11 | const TypeInfo = builtin.TypeInfo; | ||
| 12 | |||
| 13 | pub fn tagName(v: var) []const u8 { | ||
| 14 | const T = @typeOf(v); | ||
| 15 | switch (@typeInfo(T)) { | ||
| 16 | TypeId.Enum => |info| { | ||
| 17 | const Tag = info.tag_type; | ||
| 18 | inline for (info.fields) |field| { | ||
| 19 | if (field.value == @enumToInt(v)) return field.name; | ||
| 20 | } | ||
| 21 | |||
| 22 | unreachable; | ||
| 23 | }, | ||
| 24 | TypeId.Union => |info| { | ||
| 25 | const UnionTag = if (info.tag_type) |UT| UT else @compileError("union is untagged"); | ||
| 26 | const Tag = @typeInfo(UnionTag).Enum.tag_type; | ||
| 27 | inline for (info.fields) |field| { | ||
| 28 | if (field.enum_field.?.value == @enumToInt(UnionTag(v))) | ||
| 29 | return field.name; | ||
| 30 | } | ||
| 31 | |||
| 32 | unreachable; | ||
| 33 | }, | ||
| 34 | TypeId.ErrorSet => |info| { | ||
| 35 | inline for (info.errors) |err| { | ||
| 36 | if (err.value == @errorToInt(v)) return err.name; | ||
| 37 | } | ||
| 38 | |||
| 39 | unreachable; | ||
| 40 | }, | ||
| 41 | else => @compileError("expected enum, error set or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | test "std.meta.tagName" { | ||
| 46 | const E1 = enum { | ||
| 47 | A, | ||
| 48 | B, | ||
| 49 | }; | ||
| 50 | const E2 = enum(u8) { | ||
| 51 | C = 33, | ||
| 52 | D, | ||
| 53 | }; | ||
| 54 | const U1 = union(enum) { | ||
| 55 | G: u8, | ||
| 56 | H: u16, | ||
| 57 | }; | ||
| 58 | const U2 = union(E2) { | ||
| 59 | C: u8, | ||
| 60 | D: u16, | ||
| 61 | }; | ||
| 62 | |||
| 63 | var u1g = U1{ .G = 0 }; | ||
| 64 | var u1h = U1{ .H = 0 }; | ||
| 65 | var u2a = U2{ .C = 0 }; | ||
| 66 | var u2b = U2{ .D = 0 }; | ||
| 67 | |||
| 68 | testing.expect(mem.eql(u8, tagName(E1.A), "A")); | ||
| 69 | testing.expect(mem.eql(u8, tagName(E1.B), "B")); | ||
| 70 | testing.expect(mem.eql(u8, tagName(E2.C), "C")); | ||
| 71 | testing.expect(mem.eql(u8, tagName(E2.D), "D")); | ||
| 72 | testing.expect(mem.eql(u8, tagName(error.E), "E")); | ||
| 73 | testing.expect(mem.eql(u8, tagName(error.F), "F")); | ||
| 74 | testing.expect(mem.eql(u8, tagName(u1g), "G")); | ||
| 75 | testing.expect(mem.eql(u8, tagName(u1h), "H")); | ||
| 76 | testing.expect(mem.eql(u8, tagName(u2a), "C")); | ||
| 77 | testing.expect(mem.eql(u8, tagName(u2b), "D")); | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn stringToEnum(comptime T: type, str: []const u8) ?T { | ||
| 81 | inline for (@typeInfo(T).Enum.fields) |enumField| { | ||
| 82 | if (std.mem.eql(u8, str, enumField.name)) { | ||
| 83 | return @field(T, enumField.name); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | return null; | ||
| 87 | } | ||
| 88 | |||
| 89 | test "std.meta.stringToEnum" { | ||
| 90 | const E1 = enum { | ||
| 91 | A, | ||
| 92 | B, | ||
| 93 | }; | ||
| 94 | testing.expect(E1.A == stringToEnum(E1, "A").?); | ||
| 95 | testing.expect(E1.B == stringToEnum(E1, "B").?); | ||
| 96 | testing.expect(null == stringToEnum(E1, "C")); | ||
| 97 | } | ||
| 98 | |||
| 99 | pub fn bitCount(comptime T: type) comptime_int { | ||
| 100 | return switch (@typeInfo(T)) { | ||
| 101 | TypeId.Int => |info| info.bits, | ||
| 102 | TypeId.Float => |info| info.bits, | ||
| 103 | else => @compileError("Expected int or float type, found '" ++ @typeName(T) ++ "'"), | ||
| 104 | }; | ||
| 105 | } | ||
| 106 | |||
| 107 | test "std.meta.bitCount" { | ||
| 108 | testing.expect(bitCount(u8) == 8); | ||
| 109 | testing.expect(bitCount(f32) == 32); | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn alignment(comptime T: type) comptime_int { | ||
| 113 | //@alignOf works on non-pointer types | ||
| 114 | const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T; | ||
| 115 | return @typeInfo(P).Pointer.alignment; | ||
| 116 | } | ||
| 117 | |||
| 118 | test "std.meta.alignment" { | ||
| 119 | testing.expect(alignment(u8) == 1); | ||
| 120 | testing.expect(alignment(*align(1) u8) == 1); | ||
| 121 | testing.expect(alignment(*align(2) u8) == 2); | ||
| 122 | testing.expect(alignment([]align(1) u8) == 1); | ||
| 123 | testing.expect(alignment([]align(2) u8) == 2); | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn Child(comptime T: type) type { | ||
| 127 | return switch (@typeInfo(T)) { | ||
| 128 | TypeId.Array => |info| info.child, | ||
| 129 | TypeId.Pointer => |info| info.child, | ||
| 130 | TypeId.Optional => |info| info.child, | ||
| 131 | TypeId.Promise => |info| if (info.child) |child| child else null, | ||
| 132 | else => @compileError("Expected promise, pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), | ||
| 133 | }; | ||
| 134 | } | ||
| 135 | |||
| 136 | test "std.meta.Child" { | ||
| 137 | testing.expect(Child([1]u8) == u8); | ||
| 138 | testing.expect(Child(*u8) == u8); | ||
| 139 | testing.expect(Child([]u8) == u8); | ||
| 140 | testing.expect(Child(?u8) == u8); | ||
| 141 | testing.expect(Child(promise->u8) == u8); | ||
| 142 | } | ||
| 143 | |||
| 144 | pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { | ||
| 145 | return switch (@typeInfo(T)) { | ||
| 146 | TypeId.Struct => |info| info.layout, | ||
| 147 | TypeId.Enum => |info| info.layout, | ||
| 148 | TypeId.Union => |info| info.layout, | ||
| 149 | else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 150 | }; | ||
| 151 | } | ||
| 152 | |||
| 153 | test "std.meta.containerLayout" { | ||
| 154 | const E1 = enum { | ||
| 155 | A, | ||
| 156 | }; | ||
| 157 | const E2 = packed enum { | ||
| 158 | A, | ||
| 159 | }; | ||
| 160 | const E3 = extern enum { | ||
| 161 | A, | ||
| 162 | }; | ||
| 163 | const S1 = struct {}; | ||
| 164 | const S2 = packed struct {}; | ||
| 165 | const S3 = extern struct {}; | ||
| 166 | const U1 = union { | ||
| 167 | a: u8, | ||
| 168 | }; | ||
| 169 | const U2 = packed union { | ||
| 170 | a: u8, | ||
| 171 | }; | ||
| 172 | const U3 = extern union { | ||
| 173 | a: u8, | ||
| 174 | }; | ||
| 175 | |||
| 176 | testing.expect(containerLayout(E1) == TypeInfo.ContainerLayout.Auto); | ||
| 177 | testing.expect(containerLayout(E2) == TypeInfo.ContainerLayout.Packed); | ||
| 178 | testing.expect(containerLayout(E3) == TypeInfo.ContainerLayout.Extern); | ||
| 179 | testing.expect(containerLayout(S1) == TypeInfo.ContainerLayout.Auto); | ||
| 180 | testing.expect(containerLayout(S2) == TypeInfo.ContainerLayout.Packed); | ||
| 181 | testing.expect(containerLayout(S3) == TypeInfo.ContainerLayout.Extern); | ||
| 182 | testing.expect(containerLayout(U1) == TypeInfo.ContainerLayout.Auto); | ||
| 183 | testing.expect(containerLayout(U2) == TypeInfo.ContainerLayout.Packed); | ||
| 184 | testing.expect(containerLayout(U3) == TypeInfo.ContainerLayout.Extern); | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn definitions(comptime T: type) []TypeInfo.Definition { | ||
| 188 | return switch (@typeInfo(T)) { | ||
| 189 | TypeId.Struct => |info| info.defs, | ||
| 190 | TypeId.Enum => |info| info.defs, | ||
| 191 | TypeId.Union => |info| info.defs, | ||
| 192 | else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 193 | }; | ||
| 194 | } | ||
| 195 | |||
| 196 | test "std.meta.definitions" { | ||
| 197 | const E1 = enum { | ||
| 198 | A, | ||
| 199 | |||
| 200 | fn a() void {} | ||
| 201 | }; | ||
| 202 | const S1 = struct { | ||
| 203 | fn a() void {} | ||
| 204 | }; | ||
| 205 | const U1 = union { | ||
| 206 | a: u8, | ||
| 207 | |||
| 208 | fn a() void {} | ||
| 209 | }; | ||
| 210 | |||
| 211 | const defs = comptime [][]TypeInfo.Definition{ | ||
| 212 | definitions(E1), | ||
| 213 | definitions(S1), | ||
| 214 | definitions(U1), | ||
| 215 | }; | ||
| 216 | |||
| 217 | inline for (defs) |def| { | ||
| 218 | testing.expect(def.len == 1); | ||
| 219 | testing.expect(comptime mem.eql(u8, def[0].name, "a")); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | pub fn definitionInfo(comptime T: type, comptime def_name: []const u8) TypeInfo.Definition { | ||
| 224 | inline for (comptime definitions(T)) |def| { | ||
| 225 | if (comptime mem.eql(u8, def.name, def_name)) | ||
| 226 | return def; | ||
| 227 | } | ||
| 228 | |||
| 229 | @compileError("'" ++ @typeName(T) ++ "' has no definition '" ++ def_name ++ "'"); | ||
| 230 | } | ||
| 231 | |||
| 232 | test "std.meta.definitionInfo" { | ||
| 233 | const E1 = enum { | ||
| 234 | A, | ||
| 235 | |||
| 236 | fn a() void {} | ||
| 237 | }; | ||
| 238 | const S1 = struct { | ||
| 239 | fn a() void {} | ||
| 240 | }; | ||
| 241 | const U1 = union { | ||
| 242 | a: u8, | ||
| 243 | |||
| 244 | fn a() void {} | ||
| 245 | }; | ||
| 246 | |||
| 247 | const infos = comptime []TypeInfo.Definition{ | ||
| 248 | definitionInfo(E1, "a"), | ||
| 249 | definitionInfo(S1, "a"), | ||
| 250 | definitionInfo(U1, "a"), | ||
| 251 | }; | ||
| 252 | |||
| 253 | inline for (infos) |info| { | ||
| 254 | testing.expect(comptime mem.eql(u8, info.name, "a")); | ||
| 255 | testing.expect(!info.is_pub); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | pub fn fields(comptime T: type) switch (@typeInfo(T)) { | ||
| 260 | TypeId.Struct => []TypeInfo.StructField, | ||
| 261 | TypeId.Union => []TypeInfo.UnionField, | ||
| 262 | TypeId.ErrorSet => []TypeInfo.Error, | ||
| 263 | TypeId.Enum => []TypeInfo.EnumField, | ||
| 264 | else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), | ||
| 265 | } { | ||
| 266 | return switch (@typeInfo(T)) { | ||
| 267 | TypeId.Struct => |info| info.fields, | ||
| 268 | TypeId.Union => |info| info.fields, | ||
| 269 | TypeId.Enum => |info| info.fields, | ||
| 270 | TypeId.ErrorSet => |info| info.errors, | ||
| 271 | else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), | ||
| 272 | }; | ||
| 273 | } | ||
| 274 | |||
| 275 | test "std.meta.fields" { | ||
| 276 | const E1 = enum { | ||
| 277 | A, | ||
| 278 | }; | ||
| 279 | const E2 = error{A}; | ||
| 280 | const S1 = struct { | ||
| 281 | a: u8, | ||
| 282 | }; | ||
| 283 | const U1 = union { | ||
| 284 | a: u8, | ||
| 285 | }; | ||
| 286 | |||
| 287 | const e1f = comptime fields(E1); | ||
| 288 | const e2f = comptime fields(E2); | ||
| 289 | const sf = comptime fields(S1); | ||
| 290 | const uf = comptime fields(U1); | ||
| 291 | |||
| 292 | testing.expect(e1f.len == 1); | ||
| 293 | testing.expect(e2f.len == 1); | ||
| 294 | testing.expect(sf.len == 1); | ||
| 295 | testing.expect(uf.len == 1); | ||
| 296 | testing.expect(mem.eql(u8, e1f[0].name, "A")); | ||
| 297 | testing.expect(mem.eql(u8, e2f[0].name, "A")); | ||
| 298 | testing.expect(mem.eql(u8, sf[0].name, "a")); | ||
| 299 | testing.expect(mem.eql(u8, uf[0].name, "a")); | ||
| 300 | testing.expect(comptime sf[0].field_type == u8); | ||
| 301 | testing.expect(comptime uf[0].field_type == u8); | ||
| 302 | } | ||
| 303 | |||
| 304 | pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) { | ||
| 305 | TypeId.Struct => TypeInfo.StructField, | ||
| 306 | TypeId.Union => TypeInfo.UnionField, | ||
| 307 | TypeId.ErrorSet => TypeInfo.Error, | ||
| 308 | TypeId.Enum => TypeInfo.EnumField, | ||
| 309 | else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), | ||
| 310 | } { | ||
| 311 | inline for (comptime fields(T)) |field| { | ||
| 312 | if (comptime mem.eql(u8, field.name, field_name)) | ||
| 313 | return field; | ||
| 314 | } | ||
| 315 | |||
| 316 | @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'"); | ||
| 317 | } | ||
| 318 | |||
| 319 | test "std.meta.fieldInfo" { | ||
| 320 | const E1 = enum { | ||
| 321 | A, | ||
| 322 | }; | ||
| 323 | const E2 = error{A}; | ||
| 324 | const S1 = struct { | ||
| 325 | a: u8, | ||
| 326 | }; | ||
| 327 | const U1 = union { | ||
| 328 | a: u8, | ||
| 329 | }; | ||
| 330 | |||
| 331 | const e1f = comptime fieldInfo(E1, "A"); | ||
| 332 | const e2f = comptime fieldInfo(E2, "A"); | ||
| 333 | const sf = comptime fieldInfo(S1, "a"); | ||
| 334 | const uf = comptime fieldInfo(U1, "a"); | ||
| 335 | |||
| 336 | testing.expect(mem.eql(u8, e1f.name, "A")); | ||
| 337 | testing.expect(mem.eql(u8, e2f.name, "A")); | ||
| 338 | testing.expect(mem.eql(u8, sf.name, "a")); | ||
| 339 | testing.expect(mem.eql(u8, uf.name, "a")); | ||
| 340 | testing.expect(comptime sf.field_type == u8); | ||
| 341 | testing.expect(comptime uf.field_type == u8); | ||
| 342 | } | ||
| 343 | |||
| 344 | pub fn TagType(comptime T: type) type { | ||
| 345 | return switch (@typeInfo(T)) { | ||
| 346 | TypeId.Enum => |info| info.tag_type, | ||
| 347 | TypeId.Union => |info| if (info.tag_type) |Tag| Tag else null, | ||
| 348 | else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 349 | }; | ||
| 350 | } | ||
| 351 | |||
| 352 | test "std.meta.TagType" { | ||
| 353 | const E = enum(u8) { | ||
| 354 | C = 33, | ||
| 355 | D, | ||
| 356 | }; | ||
| 357 | const U = union(E) { | ||
| 358 | C: u8, | ||
| 359 | D: u16, | ||
| 360 | }; | ||
| 361 | |||
| 362 | testing.expect(TagType(E) == u8); | ||
| 363 | testing.expect(TagType(U) == E); | ||
| 364 | } | ||
| 365 | |||
| 366 | ///Returns the active tag of a tagged union | ||
| 367 | pub fn activeTag(u: var) @TagType(@typeOf(u)) { | ||
| 368 | const T = @typeOf(u); | ||
| 369 | return @TagType(T)(u); | ||
| 370 | } | ||
| 371 | |||
| 372 | test "std.meta.activeTag" { | ||
| 373 | const UE = enum { | ||
| 374 | Int, | ||
| 375 | Float, | ||
| 376 | }; | ||
| 377 | |||
| 378 | const U = union(UE) { | ||
| 379 | Int: u32, | ||
| 380 | Float: f32, | ||
| 381 | }; | ||
| 382 | |||
| 383 | var u = U{ .Int = 32 }; | ||
| 384 | testing.expect(activeTag(u) == UE.Int); | ||
| 385 | |||
| 386 | u = U{ .Float = 112.9876 }; | ||
| 387 | testing.expect(activeTag(u) == UE.Float); | ||
| 388 | } | ||
| 389 | |||
| 390 | ///Given a tagged union type, and an enum, return the type of the union | ||
| 391 | /// field corresponding to the enum tag. | ||
| 392 | pub fn TagPayloadType(comptime U: type, tag: var) type { | ||
| 393 | const Tag = @typeOf(tag); | ||
| 394 | testing.expect(trait.is(builtin.TypeId.Union)(U)); | ||
| 395 | testing.expect(trait.is(builtin.TypeId.Enum)(Tag)); | ||
| 396 | |||
| 397 | const info = @typeInfo(U).Union; | ||
| 398 | |||
| 399 | inline for (info.fields) |field_info| { | ||
| 400 | if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type; | ||
| 401 | } | ||
| 402 | unreachable; | ||
| 403 | } | ||
| 404 | |||
| 405 | test "std.meta.TagPayloadType" { | ||
| 406 | const Event = union(enum) { | ||
| 407 | Moved: struct { | ||
| 408 | from: i32, | ||
| 409 | to: i32, | ||
| 410 | }, | ||
| 411 | }; | ||
| 412 | const MovedEvent = TagPayloadType(Event, Event.Moved); | ||
| 413 | var e: Event = undefined; | ||
| 414 | testing.expect(MovedEvent == @typeOf(e.Moved)); | ||
| 415 | } | ||
| 416 | |||
| 417 | ///Compares two of any type for equality. Containers are compared on a field-by-field basis, | ||
| 418 | /// where possible. Pointers are not followed. | ||
| 419 | pub fn eql(a: var, b: @typeOf(a)) bool { | ||
| 420 | const T = @typeOf(a); | ||
| 421 | |||
| 422 | switch (@typeId(T)) { | ||
| 423 | builtin.TypeId.Struct => { | ||
| 424 | const info = @typeInfo(T).Struct; | ||
| 425 | |||
| 426 | inline for (info.fields) |field_info| { | ||
| 427 | if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false; | ||
| 428 | } | ||
| 429 | return true; | ||
| 430 | }, | ||
| 431 | builtin.TypeId.ErrorUnion => { | ||
| 432 | if (a) |a_p| { | ||
| 433 | if (b) |b_p| return eql(a_p, b_p) else |_| return false; | ||
| 434 | } else |a_e| { | ||
| 435 | if (b) |_| return false else |b_e| return a_e == b_e; | ||
| 436 | } | ||
| 437 | }, | ||
| 438 | builtin.TypeId.Union => { | ||
| 439 | const info = @typeInfo(T).Union; | ||
| 440 | |||
| 441 | if (info.tag_type) |_| { | ||
| 442 | const tag_a = activeTag(a); | ||
| 443 | const tag_b = activeTag(b); | ||
| 444 | if (tag_a != tag_b) return false; | ||
| 445 | |||
| 446 | inline for (info.fields) |field_info| { | ||
| 447 | const enum_field = field_info.enum_field.?; | ||
| 448 | if (enum_field.value == @enumToInt(tag_a)) { | ||
| 449 | return eql(@field(a, enum_field.name), @field(b, enum_field.name)); | ||
| 450 | } | ||
| 451 | } | ||
| 452 | return false; | ||
| 453 | } | ||
| 454 | |||
| 455 | @compileError("cannot compare untagged union type " ++ @typeName(T)); | ||
| 456 | }, | ||
| 457 | builtin.TypeId.Array => { | ||
| 458 | if (a.len != b.len) return false; | ||
| 459 | for (a) |e, i| | ||
| 460 | if (!eql(e, b[i])) return false; | ||
| 461 | return true; | ||
| 462 | }, | ||
| 463 | builtin.TypeId.Pointer => { | ||
| 464 | const info = @typeInfo(T).Pointer; | ||
| 465 | switch (info.size) { | ||
| 466 | builtin.TypeInfo.Pointer.Size.One, | ||
| 467 | builtin.TypeInfo.Pointer.Size.Many, | ||
| 468 | builtin.TypeInfo.Pointer.Size.C, | ||
| 469 | => return a == b, | ||
| 470 | builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len, | ||
| 471 | } | ||
| 472 | }, | ||
| 473 | builtin.TypeId.Optional => { | ||
| 474 | if (a == null and b == null) return true; | ||
| 475 | if (a == null or b == null) return false; | ||
| 476 | return eql(a.?, b.?); | ||
| 477 | }, | ||
| 478 | else => return a == b, | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | test "std.meta.eql" { | ||
| 483 | const S = struct { | ||
| 484 | a: u32, | ||
| 485 | b: f64, | ||
| 486 | c: [5]u8, | ||
| 487 | }; | ||
| 488 | |||
| 489 | const U = union(enum) { | ||
| 490 | s: S, | ||
| 491 | f: ?f32, | ||
| 492 | }; | ||
| 493 | |||
| 494 | const s_1 = S{ | ||
| 495 | .a = 134, | ||
| 496 | .b = 123.3, | ||
| 497 | .c = "12345", | ||
| 498 | }; | ||
| 499 | |||
| 500 | const s_2 = S{ | ||
| 501 | .a = 1, | ||
| 502 | .b = 123.3, | ||
| 503 | .c = "54321", | ||
| 504 | }; | ||
| 505 | |||
| 506 | const s_3 = S{ | ||
| 507 | .a = 134, | ||
| 508 | .b = 123.3, | ||
| 509 | .c = "12345", | ||
| 510 | }; | ||
| 511 | |||
| 512 | const u_1 = U{ .f = 24 }; | ||
| 513 | const u_2 = U{ .s = s_1 }; | ||
| 514 | const u_3 = U{ .f = 24 }; | ||
| 515 | |||
| 516 | testing.expect(eql(s_1, s_3)); | ||
| 517 | testing.expect(eql(&s_1, &s_1)); | ||
| 518 | testing.expect(!eql(&s_1, &s_3)); | ||
| 519 | testing.expect(eql(u_1, u_3)); | ||
| 520 | testing.expect(!eql(u_1, u_2)); | ||
| 521 | |||
| 522 | var a1 = "abcdef"; | ||
| 523 | var a2 = "abcdef"; | ||
| 524 | var a3 = "ghijkl"; | ||
| 525 | |||
| 526 | testing.expect(eql(a1, a2)); | ||
| 527 | testing.expect(!eql(a1, a3)); | ||
| 528 | testing.expect(!eql(a1[0..], a2[0..])); | ||
| 529 | |||
| 530 | const EU = struct { | ||
| 531 | fn tst(err: bool) !u8 { | ||
| 532 | if (err) return error.Error; | ||
| 533 | return u8(5); | ||
| 534 | } | ||
| 535 | }; | ||
| 536 | |||
| 537 | testing.expect(eql(EU.tst(true), EU.tst(true))); | ||
| 538 | testing.expect(eql(EU.tst(false), EU.tst(false))); | ||
| 539 | testing.expect(!eql(EU.tst(false), EU.tst(true))); | ||
| 540 | } | ||
| 541 | |||
| 542 | test "intToEnum with error return" { | ||
| 543 | const E1 = enum { | ||
| 544 | A, | ||
| 545 | }; | ||
| 546 | const E2 = enum { | ||
| 547 | A, | ||
| 548 | B, | ||
| 549 | }; | ||
| 550 | |||
| 551 | var zero: u8 = 0; | ||
| 552 | var one: u16 = 1; | ||
| 553 | testing.expect(intToEnum(E1, zero) catch unreachable == E1.A); | ||
| 554 | testing.expect(intToEnum(E2, one) catch unreachable == E2.B); | ||
| 555 | testing.expectError(error.InvalidEnumTag, intToEnum(E1, one)); | ||
| 556 | } | ||
| 557 | |||
| 558 | pub const IntToEnumError = error{InvalidEnumTag}; | ||
| 559 | |||
| 560 | pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { | ||
| 561 | comptime var i = 0; | ||
| 562 | inline while (i != @memberCount(Tag)) : (i += 1) { | ||
| 563 | const this_tag_value = @field(Tag, @memberName(Tag, i)); | ||
| 564 | if (tag_int == @enumToInt(this_tag_value)) { | ||
| 565 | return this_tag_value; | ||
| 566 | } | ||
| 567 | } | ||
| 568 | return error.InvalidEnumTag; | ||
| 569 | } | ||
std/meta/index.zig deleted-569| ... | @@ -1,569 +0,0 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const debug = std.debug; | ||
| 4 | const mem = std.mem; | ||
| 5 | const math = std.math; | ||
| 6 | const testing = std.testing; | ||
| 7 | |||
| 8 | pub const trait = @import("trait.zig"); | ||
| 9 | |||
| 10 | const TypeId = builtin.TypeId; | ||
| 11 | const TypeInfo = builtin.TypeInfo; | ||
| 12 | |||
| 13 | pub fn tagName(v: var) []const u8 { | ||
| 14 | const T = @typeOf(v); | ||
| 15 | switch (@typeInfo(T)) { | ||
| 16 | TypeId.Enum => |info| { | ||
| 17 | const Tag = info.tag_type; | ||
| 18 | inline for (info.fields) |field| { | ||
| 19 | if (field.value == @enumToInt(v)) return field.name; | ||
| 20 | } | ||
| 21 | |||
| 22 | unreachable; | ||
| 23 | }, | ||
| 24 | TypeId.Union => |info| { | ||
| 25 | const UnionTag = if (info.tag_type) |UT| UT else @compileError("union is untagged"); | ||
| 26 | const Tag = @typeInfo(UnionTag).Enum.tag_type; | ||
| 27 | inline for (info.fields) |field| { | ||
| 28 | if (field.enum_field.?.value == @enumToInt(UnionTag(v))) | ||
| 29 | return field.name; | ||
| 30 | } | ||
| 31 | |||
| 32 | unreachable; | ||
| 33 | }, | ||
| 34 | TypeId.ErrorSet => |info| { | ||
| 35 | inline for (info.errors) |err| { | ||
| 36 | if (err.value == @errorToInt(v)) return err.name; | ||
| 37 | } | ||
| 38 | |||
| 39 | unreachable; | ||
| 40 | }, | ||
| 41 | else => @compileError("expected enum, error set or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | test "std.meta.tagName" { | ||
| 46 | const E1 = enum { | ||
| 47 | A, | ||
| 48 | B, | ||
| 49 | }; | ||
| 50 | const E2 = enum(u8) { | ||
| 51 | C = 33, | ||
| 52 | D, | ||
| 53 | }; | ||
| 54 | const U1 = union(enum) { | ||
| 55 | G: u8, | ||
| 56 | H: u16, | ||
| 57 | }; | ||
| 58 | const U2 = union(E2) { | ||
| 59 | C: u8, | ||
| 60 | D: u16, | ||
| 61 | }; | ||
| 62 | |||
| 63 | var u1g = U1{ .G = 0 }; | ||
| 64 | var u1h = U1{ .H = 0 }; | ||
| 65 | var u2a = U2{ .C = 0 }; | ||
| 66 | var u2b = U2{ .D = 0 }; | ||
| 67 | |||
| 68 | testing.expect(mem.eql(u8, tagName(E1.A), "A")); | ||
| 69 | testing.expect(mem.eql(u8, tagName(E1.B), "B")); | ||
| 70 | testing.expect(mem.eql(u8, tagName(E2.C), "C")); | ||
| 71 | testing.expect(mem.eql(u8, tagName(E2.D), "D")); | ||
| 72 | testing.expect(mem.eql(u8, tagName(error.E), "E")); | ||
| 73 | testing.expect(mem.eql(u8, tagName(error.F), "F")); | ||
| 74 | testing.expect(mem.eql(u8, tagName(u1g), "G")); | ||
| 75 | testing.expect(mem.eql(u8, tagName(u1h), "H")); | ||
| 76 | testing.expect(mem.eql(u8, tagName(u2a), "C")); | ||
| 77 | testing.expect(mem.eql(u8, tagName(u2b), "D")); | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn stringToEnum(comptime T: type, str: []const u8) ?T { | ||
| 81 | inline for (@typeInfo(T).Enum.fields) |enumField| { | ||
| 82 | if (std.mem.eql(u8, str, enumField.name)) { | ||
| 83 | return @field(T, enumField.name); | ||
| 84 | } | ||
| 85 | } | ||
| 86 | return null; | ||
| 87 | } | ||
| 88 | |||
| 89 | test "std.meta.stringToEnum" { | ||
| 90 | const E1 = enum { | ||
| 91 | A, | ||
| 92 | B, | ||
| 93 | }; | ||
| 94 | testing.expect(E1.A == stringToEnum(E1, "A").?); | ||
| 95 | testing.expect(E1.B == stringToEnum(E1, "B").?); | ||
| 96 | testing.expect(null == stringToEnum(E1, "C")); | ||
| 97 | } | ||
| 98 | |||
| 99 | pub fn bitCount(comptime T: type) comptime_int { | ||
| 100 | return switch (@typeInfo(T)) { | ||
| 101 | TypeId.Int => |info| info.bits, | ||
| 102 | TypeId.Float => |info| info.bits, | ||
| 103 | else => @compileError("Expected int or float type, found '" ++ @typeName(T) ++ "'"), | ||
| 104 | }; | ||
| 105 | } | ||
| 106 | |||
| 107 | test "std.meta.bitCount" { | ||
| 108 | testing.expect(bitCount(u8) == 8); | ||
| 109 | testing.expect(bitCount(f32) == 32); | ||
| 110 | } | ||
| 111 | |||
| 112 | pub fn alignment(comptime T: type) comptime_int { | ||
| 113 | //@alignOf works on non-pointer types | ||
| 114 | const P = if (comptime trait.is(TypeId.Pointer)(T)) T else *T; | ||
| 115 | return @typeInfo(P).Pointer.alignment; | ||
| 116 | } | ||
| 117 | |||
| 118 | test "std.meta.alignment" { | ||
| 119 | testing.expect(alignment(u8) == 1); | ||
| 120 | testing.expect(alignment(*align(1) u8) == 1); | ||
| 121 | testing.expect(alignment(*align(2) u8) == 2); | ||
| 122 | testing.expect(alignment([]align(1) u8) == 1); | ||
| 123 | testing.expect(alignment([]align(2) u8) == 2); | ||
| 124 | } | ||
| 125 | |||
| 126 | pub fn Child(comptime T: type) type { | ||
| 127 | return switch (@typeInfo(T)) { | ||
| 128 | TypeId.Array => |info| info.child, | ||
| 129 | TypeId.Pointer => |info| info.child, | ||
| 130 | TypeId.Optional => |info| info.child, | ||
| 131 | TypeId.Promise => |info| if (info.child) |child| child else null, | ||
| 132 | else => @compileError("Expected promise, pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"), | ||
| 133 | }; | ||
| 134 | } | ||
| 135 | |||
| 136 | test "std.meta.Child" { | ||
| 137 | testing.expect(Child([1]u8) == u8); | ||
| 138 | testing.expect(Child(*u8) == u8); | ||
| 139 | testing.expect(Child([]u8) == u8); | ||
| 140 | testing.expect(Child(?u8) == u8); | ||
| 141 | testing.expect(Child(promise->u8) == u8); | ||
| 142 | } | ||
| 143 | |||
| 144 | pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout { | ||
| 145 | return switch (@typeInfo(T)) { | ||
| 146 | TypeId.Struct => |info| info.layout, | ||
| 147 | TypeId.Enum => |info| info.layout, | ||
| 148 | TypeId.Union => |info| info.layout, | ||
| 149 | else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 150 | }; | ||
| 151 | } | ||
| 152 | |||
| 153 | test "std.meta.containerLayout" { | ||
| 154 | const E1 = enum { | ||
| 155 | A, | ||
| 156 | }; | ||
| 157 | const E2 = packed enum { | ||
| 158 | A, | ||
| 159 | }; | ||
| 160 | const E3 = extern enum { | ||
| 161 | A, | ||
| 162 | }; | ||
| 163 | const S1 = struct {}; | ||
| 164 | const S2 = packed struct {}; | ||
| 165 | const S3 = extern struct {}; | ||
| 166 | const U1 = union { | ||
| 167 | a: u8, | ||
| 168 | }; | ||
| 169 | const U2 = packed union { | ||
| 170 | a: u8, | ||
| 171 | }; | ||
| 172 | const U3 = extern union { | ||
| 173 | a: u8, | ||
| 174 | }; | ||
| 175 | |||
| 176 | testing.expect(containerLayout(E1) == TypeInfo.ContainerLayout.Auto); | ||
| 177 | testing.expect(containerLayout(E2) == TypeInfo.ContainerLayout.Packed); | ||
| 178 | testing.expect(containerLayout(E3) == TypeInfo.ContainerLayout.Extern); | ||
| 179 | testing.expect(containerLayout(S1) == TypeInfo.ContainerLayout.Auto); | ||
| 180 | testing.expect(containerLayout(S2) == TypeInfo.ContainerLayout.Packed); | ||
| 181 | testing.expect(containerLayout(S3) == TypeInfo.ContainerLayout.Extern); | ||
| 182 | testing.expect(containerLayout(U1) == TypeInfo.ContainerLayout.Auto); | ||
| 183 | testing.expect(containerLayout(U2) == TypeInfo.ContainerLayout.Packed); | ||
| 184 | testing.expect(containerLayout(U3) == TypeInfo.ContainerLayout.Extern); | ||
| 185 | } | ||
| 186 | |||
| 187 | pub fn definitions(comptime T: type) []TypeInfo.Definition { | ||
| 188 | return switch (@typeInfo(T)) { | ||
| 189 | TypeId.Struct => |info| info.defs, | ||
| 190 | TypeId.Enum => |info| info.defs, | ||
| 191 | TypeId.Union => |info| info.defs, | ||
| 192 | else => @compileError("Expected struct, enum or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 193 | }; | ||
| 194 | } | ||
| 195 | |||
| 196 | test "std.meta.definitions" { | ||
| 197 | const E1 = enum { | ||
| 198 | A, | ||
| 199 | |||
| 200 | fn a() void {} | ||
| 201 | }; | ||
| 202 | const S1 = struct { | ||
| 203 | fn a() void {} | ||
| 204 | }; | ||
| 205 | const U1 = union { | ||
| 206 | a: u8, | ||
| 207 | |||
| 208 | fn a() void {} | ||
| 209 | }; | ||
| 210 | |||
| 211 | const defs = comptime [][]TypeInfo.Definition{ | ||
| 212 | definitions(E1), | ||
| 213 | definitions(S1), | ||
| 214 | definitions(U1), | ||
| 215 | }; | ||
| 216 | |||
| 217 | inline for (defs) |def| { | ||
| 218 | testing.expect(def.len == 1); | ||
| 219 | testing.expect(comptime mem.eql(u8, def[0].name, "a")); | ||
| 220 | } | ||
| 221 | } | ||
| 222 | |||
| 223 | pub fn definitionInfo(comptime T: type, comptime def_name: []const u8) TypeInfo.Definition { | ||
| 224 | inline for (comptime definitions(T)) |def| { | ||
| 225 | if (comptime mem.eql(u8, def.name, def_name)) | ||
| 226 | return def; | ||
| 227 | } | ||
| 228 | |||
| 229 | @compileError("'" ++ @typeName(T) ++ "' has no definition '" ++ def_name ++ "'"); | ||
| 230 | } | ||
| 231 | |||
| 232 | test "std.meta.definitionInfo" { | ||
| 233 | const E1 = enum { | ||
| 234 | A, | ||
| 235 | |||
| 236 | fn a() void {} | ||
| 237 | }; | ||
| 238 | const S1 = struct { | ||
| 239 | fn a() void {} | ||
| 240 | }; | ||
| 241 | const U1 = union { | ||
| 242 | a: u8, | ||
| 243 | |||
| 244 | fn a() void {} | ||
| 245 | }; | ||
| 246 | |||
| 247 | const infos = comptime []TypeInfo.Definition{ | ||
| 248 | definitionInfo(E1, "a"), | ||
| 249 | definitionInfo(S1, "a"), | ||
| 250 | definitionInfo(U1, "a"), | ||
| 251 | }; | ||
| 252 | |||
| 253 | inline for (infos) |info| { | ||
| 254 | testing.expect(comptime mem.eql(u8, info.name, "a")); | ||
| 255 | testing.expect(!info.is_pub); | ||
| 256 | } | ||
| 257 | } | ||
| 258 | |||
| 259 | pub fn fields(comptime T: type) switch (@typeInfo(T)) { | ||
| 260 | TypeId.Struct => []TypeInfo.StructField, | ||
| 261 | TypeId.Union => []TypeInfo.UnionField, | ||
| 262 | TypeId.ErrorSet => []TypeInfo.Error, | ||
| 263 | TypeId.Enum => []TypeInfo.EnumField, | ||
| 264 | else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), | ||
| 265 | } { | ||
| 266 | return switch (@typeInfo(T)) { | ||
| 267 | TypeId.Struct => |info| info.fields, | ||
| 268 | TypeId.Union => |info| info.fields, | ||
| 269 | TypeId.Enum => |info| info.fields, | ||
| 270 | TypeId.ErrorSet => |info| info.errors, | ||
| 271 | else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), | ||
| 272 | }; | ||
| 273 | } | ||
| 274 | |||
| 275 | test "std.meta.fields" { | ||
| 276 | const E1 = enum { | ||
| 277 | A, | ||
| 278 | }; | ||
| 279 | const E2 = error{A}; | ||
| 280 | const S1 = struct { | ||
| 281 | a: u8, | ||
| 282 | }; | ||
| 283 | const U1 = union { | ||
| 284 | a: u8, | ||
| 285 | }; | ||
| 286 | |||
| 287 | const e1f = comptime fields(E1); | ||
| 288 | const e2f = comptime fields(E2); | ||
| 289 | const sf = comptime fields(S1); | ||
| 290 | const uf = comptime fields(U1); | ||
| 291 | |||
| 292 | testing.expect(e1f.len == 1); | ||
| 293 | testing.expect(e2f.len == 1); | ||
| 294 | testing.expect(sf.len == 1); | ||
| 295 | testing.expect(uf.len == 1); | ||
| 296 | testing.expect(mem.eql(u8, e1f[0].name, "A")); | ||
| 297 | testing.expect(mem.eql(u8, e2f[0].name, "A")); | ||
| 298 | testing.expect(mem.eql(u8, sf[0].name, "a")); | ||
| 299 | testing.expect(mem.eql(u8, uf[0].name, "a")); | ||
| 300 | testing.expect(comptime sf[0].field_type == u8); | ||
| 301 | testing.expect(comptime uf[0].field_type == u8); | ||
| 302 | } | ||
| 303 | |||
| 304 | pub fn fieldInfo(comptime T: type, comptime field_name: []const u8) switch (@typeInfo(T)) { | ||
| 305 | TypeId.Struct => TypeInfo.StructField, | ||
| 306 | TypeId.Union => TypeInfo.UnionField, | ||
| 307 | TypeId.ErrorSet => TypeInfo.Error, | ||
| 308 | TypeId.Enum => TypeInfo.EnumField, | ||
| 309 | else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"), | ||
| 310 | } { | ||
| 311 | inline for (comptime fields(T)) |field| { | ||
| 312 | if (comptime mem.eql(u8, field.name, field_name)) | ||
| 313 | return field; | ||
| 314 | } | ||
| 315 | |||
| 316 | @compileError("'" ++ @typeName(T) ++ "' has no field '" ++ field_name ++ "'"); | ||
| 317 | } | ||
| 318 | |||
| 319 | test "std.meta.fieldInfo" { | ||
| 320 | const E1 = enum { | ||
| 321 | A, | ||
| 322 | }; | ||
| 323 | const E2 = error{A}; | ||
| 324 | const S1 = struct { | ||
| 325 | a: u8, | ||
| 326 | }; | ||
| 327 | const U1 = union { | ||
| 328 | a: u8, | ||
| 329 | }; | ||
| 330 | |||
| 331 | const e1f = comptime fieldInfo(E1, "A"); | ||
| 332 | const e2f = comptime fieldInfo(E2, "A"); | ||
| 333 | const sf = comptime fieldInfo(S1, "a"); | ||
| 334 | const uf = comptime fieldInfo(U1, "a"); | ||
| 335 | |||
| 336 | testing.expect(mem.eql(u8, e1f.name, "A")); | ||
| 337 | testing.expect(mem.eql(u8, e2f.name, "A")); | ||
| 338 | testing.expect(mem.eql(u8, sf.name, "a")); | ||
| 339 | testing.expect(mem.eql(u8, uf.name, "a")); | ||
| 340 | testing.expect(comptime sf.field_type == u8); | ||
| 341 | testing.expect(comptime uf.field_type == u8); | ||
| 342 | } | ||
| 343 | |||
| 344 | pub fn TagType(comptime T: type) type { | ||
| 345 | return switch (@typeInfo(T)) { | ||
| 346 | TypeId.Enum => |info| info.tag_type, | ||
| 347 | TypeId.Union => |info| if (info.tag_type) |Tag| Tag else null, | ||
| 348 | else => @compileError("expected enum or union type, found '" ++ @typeName(T) ++ "'"), | ||
| 349 | }; | ||
| 350 | } | ||
| 351 | |||
| 352 | test "std.meta.TagType" { | ||
| 353 | const E = enum(u8) { | ||
| 354 | C = 33, | ||
| 355 | D, | ||
| 356 | }; | ||
| 357 | const U = union(E) { | ||
| 358 | C: u8, | ||
| 359 | D: u16, | ||
| 360 | }; | ||
| 361 | |||
| 362 | testing.expect(TagType(E) == u8); | ||
| 363 | testing.expect(TagType(U) == E); | ||
| 364 | } | ||
| 365 | |||
| 366 | ///Returns the active tag of a tagged union | ||
| 367 | pub fn activeTag(u: var) @TagType(@typeOf(u)) { | ||
| 368 | const T = @typeOf(u); | ||
| 369 | return @TagType(T)(u); | ||
| 370 | } | ||
| 371 | |||
| 372 | test "std.meta.activeTag" { | ||
| 373 | const UE = enum { | ||
| 374 | Int, | ||
| 375 | Float, | ||
| 376 | }; | ||
| 377 | |||
| 378 | const U = union(UE) { | ||
| 379 | Int: u32, | ||
| 380 | Float: f32, | ||
| 381 | }; | ||
| 382 | |||
| 383 | var u = U{ .Int = 32 }; | ||
| 384 | testing.expect(activeTag(u) == UE.Int); | ||
| 385 | |||
| 386 | u = U{ .Float = 112.9876 }; | ||
| 387 | testing.expect(activeTag(u) == UE.Float); | ||
| 388 | } | ||
| 389 | |||
| 390 | ///Given a tagged union type, and an enum, return the type of the union | ||
| 391 | /// field corresponding to the enum tag. | ||
| 392 | pub fn TagPayloadType(comptime U: type, tag: var) type { | ||
| 393 | const Tag = @typeOf(tag); | ||
| 394 | testing.expect(trait.is(builtin.TypeId.Union)(U)); | ||
| 395 | testing.expect(trait.is(builtin.TypeId.Enum)(Tag)); | ||
| 396 | |||
| 397 | const info = @typeInfo(U).Union; | ||
| 398 | |||
| 399 | inline for (info.fields) |field_info| { | ||
| 400 | if (field_info.enum_field.?.value == @enumToInt(tag)) return field_info.field_type; | ||
| 401 | } | ||
| 402 | unreachable; | ||
| 403 | } | ||
| 404 | |||
| 405 | test "std.meta.TagPayloadType" { | ||
| 406 | const Event = union(enum) { | ||
| 407 | Moved: struct { | ||
| 408 | from: i32, | ||
| 409 | to: i32, | ||
| 410 | }, | ||
| 411 | }; | ||
| 412 | const MovedEvent = TagPayloadType(Event, Event.Moved); | ||
| 413 | var e: Event = undefined; | ||
| 414 | testing.expect(MovedEvent == @typeOf(e.Moved)); | ||
| 415 | } | ||
| 416 | |||
| 417 | ///Compares two of any type for equality. Containers are compared on a field-by-field basis, | ||
| 418 | /// where possible. Pointers are not followed. | ||
| 419 | pub fn eql(a: var, b: @typeOf(a)) bool { | ||
| 420 | const T = @typeOf(a); | ||
| 421 | |||
| 422 | switch (@typeId(T)) { | ||
| 423 | builtin.TypeId.Struct => { | ||
| 424 | const info = @typeInfo(T).Struct; | ||
| 425 | |||
| 426 | inline for (info.fields) |field_info| { | ||
| 427 | if (!eql(@field(a, field_info.name), @field(b, field_info.name))) return false; | ||
| 428 | } | ||
| 429 | return true; | ||
| 430 | }, | ||
| 431 | builtin.TypeId.ErrorUnion => { | ||
| 432 | if (a) |a_p| { | ||
| 433 | if (b) |b_p| return eql(a_p, b_p) else |_| return false; | ||
| 434 | } else |a_e| { | ||
| 435 | if (b) |_| return false else |b_e| return a_e == b_e; | ||
| 436 | } | ||
| 437 | }, | ||
| 438 | builtin.TypeId.Union => { | ||
| 439 | const info = @typeInfo(T).Union; | ||
| 440 | |||
| 441 | if (info.tag_type) |_| { | ||
| 442 | const tag_a = activeTag(a); | ||
| 443 | const tag_b = activeTag(b); | ||
| 444 | if (tag_a != tag_b) return false; | ||
| 445 | |||
| 446 | inline for (info.fields) |field_info| { | ||
| 447 | const enum_field = field_info.enum_field.?; | ||
| 448 | if (enum_field.value == @enumToInt(tag_a)) { | ||
| 449 | return eql(@field(a, enum_field.name), @field(b, enum_field.name)); | ||
| 450 | } | ||
| 451 | } | ||
| 452 | return false; | ||
| 453 | } | ||
| 454 | |||
| 455 | @compileError("cannot compare untagged union type " ++ @typeName(T)); | ||
| 456 | }, | ||
| 457 | builtin.TypeId.Array => { | ||
| 458 | if (a.len != b.len) return false; | ||
| 459 | for (a) |e, i| | ||
| 460 | if (!eql(e, b[i])) return false; | ||
| 461 | return true; | ||
| 462 | }, | ||
| 463 | builtin.TypeId.Pointer => { | ||
| 464 | const info = @typeInfo(T).Pointer; | ||
| 465 | switch (info.size) { | ||
| 466 | builtin.TypeInfo.Pointer.Size.One, | ||
| 467 | builtin.TypeInfo.Pointer.Size.Many, | ||
| 468 | builtin.TypeInfo.Pointer.Size.C, | ||
| 469 | => return a == b, | ||
| 470 | builtin.TypeInfo.Pointer.Size.Slice => return a.ptr == b.ptr and a.len == b.len, | ||
| 471 | } | ||
| 472 | }, | ||
| 473 | builtin.TypeId.Optional => { | ||
| 474 | if (a == null and b == null) return true; | ||
| 475 | if (a == null or b == null) return false; | ||
| 476 | return eql(a.?, b.?); | ||
| 477 | }, | ||
| 478 | else => return a == b, | ||
| 479 | } | ||
| 480 | } | ||
| 481 | |||
| 482 | test "std.meta.eql" { | ||
| 483 | const S = struct { | ||
| 484 | a: u32, | ||
| 485 | b: f64, | ||
| 486 | c: [5]u8, | ||
| 487 | }; | ||
| 488 | |||
| 489 | const U = union(enum) { | ||
| 490 | s: S, | ||
| 491 | f: ?f32, | ||
| 492 | }; | ||
| 493 | |||
| 494 | const s_1 = S{ | ||
| 495 | .a = 134, | ||
| 496 | .b = 123.3, | ||
| 497 | .c = "12345", | ||
| 498 | }; | ||
| 499 | |||
| 500 | const s_2 = S{ | ||
| 501 | .a = 1, | ||
| 502 | .b = 123.3, | ||
| 503 | .c = "54321", | ||
| 504 | }; | ||
| 505 | |||
| 506 | const s_3 = S{ | ||
| 507 | .a = 134, | ||
| 508 | .b = 123.3, | ||
| 509 | .c = "12345", | ||
| 510 | }; | ||
| 511 | |||
| 512 | const u_1 = U{ .f = 24 }; | ||
| 513 | const u_2 = U{ .s = s_1 }; | ||
| 514 | const u_3 = U{ .f = 24 }; | ||
| 515 | |||
| 516 | testing.expect(eql(s_1, s_3)); | ||
| 517 | testing.expect(eql(&s_1, &s_1)); | ||
| 518 | testing.expect(!eql(&s_1, &s_3)); | ||
| 519 | testing.expect(eql(u_1, u_3)); | ||
| 520 | testing.expect(!eql(u_1, u_2)); | ||
| 521 | |||
| 522 | var a1 = "abcdef"; | ||
| 523 | var a2 = "abcdef"; | ||
| 524 | var a3 = "ghijkl"; | ||
| 525 | |||
| 526 | testing.expect(eql(a1, a2)); | ||
| 527 | testing.expect(!eql(a1, a3)); | ||
| 528 | testing.expect(!eql(a1[0..], a2[0..])); | ||
| 529 | |||
| 530 | const EU = struct { | ||
| 531 | fn tst(err: bool) !u8 { | ||
| 532 | if (err) return error.Error; | ||
| 533 | return u8(5); | ||
| 534 | } | ||
| 535 | }; | ||
| 536 | |||
| 537 | testing.expect(eql(EU.tst(true), EU.tst(true))); | ||
| 538 | testing.expect(eql(EU.tst(false), EU.tst(false))); | ||
| 539 | testing.expect(!eql(EU.tst(false), EU.tst(true))); | ||
| 540 | } | ||
| 541 | |||
| 542 | test "intToEnum with error return" { | ||
| 543 | const E1 = enum { | ||
| 544 | A, | ||
| 545 | }; | ||
| 546 | const E2 = enum { | ||
| 547 | A, | ||
| 548 | B, | ||
| 549 | }; | ||
| 550 | |||
| 551 | var zero: u8 = 0; | ||
| 552 | var one: u16 = 1; | ||
| 553 | testing.expect(intToEnum(E1, zero) catch unreachable == E1.A); | ||
| 554 | testing.expect(intToEnum(E2, one) catch unreachable == E2.B); | ||
| 555 | testing.expectError(error.InvalidEnumTag, intToEnum(E1, one)); | ||
| 556 | } | ||
| 557 | |||
| 558 | pub const IntToEnumError = error{InvalidEnumTag}; | ||
| 559 | |||
| 560 | pub fn intToEnum(comptime Tag: type, tag_int: var) IntToEnumError!Tag { | ||
| 561 | comptime var i = 0; | ||
| 562 | inline while (i != @memberCount(Tag)) : (i += 1) { | ||
| 563 | const this_tag_value = @field(Tag, @memberName(Tag, i)); | ||
| 564 | if (tag_int == @enumToInt(this_tag_value)) { | ||
| 565 | return this_tag_value; | ||
| 566 | } | ||
| 567 | } | ||
| 568 | return error.InvalidEnumTag; | ||
| 569 | } | ||
std/meta/trait.zig+2-2| ... | @@ -1,11 +1,11 @@ | ... | @@ -1,11 +1,11 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
| 5 | const testing = std.testing; | 5 | const testing = std.testing; |
| 6 | const warn = debug.warn; | 6 | const warn = debug.warn; |
| 7 | 7 | ||
| 8 | const meta = @import("index.zig"); | 8 | const meta = @import("../meta.zig"); |
| 9 | 9 | ||
| 10 | //This is necessary if we want to return generic functions directly because of how the | 10 | //This is necessary if we want to return generic functions directly because of how the |
| 11 | // the type erasure works. see: #1375 | 11 | // the type erasure works. see: #1375 |
std/mutex.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
std/net.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const net = @This(); | 4 | const net = @This(); |
std/os.zig created+3432| ... | @@ -0,0 +1,3432 @@ | ||
| 1 | const std = @import("std.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Os = builtin.Os; | ||
| 4 | const is_windows = builtin.os == Os.windows; | ||
| 5 | const is_posix = switch (builtin.os) { | ||
| 6 | builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, | ||
| 7 | else => false, | ||
| 8 | }; | ||
| 9 | const os = @This(); | ||
| 10 | |||
| 11 | comptime { | ||
| 12 | assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir | ||
| 13 | } | ||
| 14 | |||
| 15 | test "std.os" { | ||
| 16 | _ = @import("os/child_process.zig"); | ||
| 17 | _ = @import("os/darwin.zig"); | ||
| 18 | _ = @import("os/darwin/errno.zig"); | ||
| 19 | _ = @import("os/get_user_id.zig"); | ||
| 20 | _ = @import("os/linux.zig"); | ||
| 21 | _ = @import("os/path.zig"); | ||
| 22 | _ = @import("os/test.zig"); | ||
| 23 | _ = @import("os/time.zig"); | ||
| 24 | _ = @import("os/windows.zig"); | ||
| 25 | _ = @import("os/uefi.zig"); | ||
| 26 | _ = @import("os/get_app_data_dir.zig"); | ||
| 27 | } | ||
| 28 | |||
| 29 | pub const windows = @import("os/windows.zig"); | ||
| 30 | pub const darwin = @import("os/darwin.zig"); | ||
| 31 | pub const linux = @import("os/linux.zig"); | ||
| 32 | pub const freebsd = @import("os/freebsd.zig"); | ||
| 33 | pub const netbsd = @import("os/netbsd.zig"); | ||
| 34 | pub const zen = @import("os/zen.zig"); | ||
| 35 | pub const uefi = @import("os/uefi.zig"); | ||
| 36 | |||
| 37 | pub const posix = switch (builtin.os) { | ||
| 38 | Os.linux => linux, | ||
| 39 | Os.macosx, Os.ios => darwin, | ||
| 40 | Os.freebsd => freebsd, | ||
| 41 | Os.netbsd => netbsd, | ||
| 42 | Os.zen => zen, | ||
| 43 | else => @compileError("Unsupported OS"), | ||
| 44 | }; | ||
| 45 | |||
| 46 | pub const net = @import("net.zig"); | ||
| 47 | |||
| 48 | pub const ChildProcess = @import("os/child_process.zig").ChildProcess; | ||
| 49 | pub const path = @import("os/path.zig"); | ||
| 50 | pub const File = @import("os/file.zig").File; | ||
| 51 | pub const time = @import("os/time.zig"); | ||
| 52 | |||
| 53 | pub const page_size = 4 * 1024; | ||
| 54 | pub const MAX_PATH_BYTES = switch (builtin.os) { | ||
| 55 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posix.PATH_MAX, | ||
| 56 | // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. | ||
| 57 | // If it would require 4 UTF-8 bytes, then there would be a surrogate | ||
| 58 | // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. | ||
| 59 | // +1 for the null byte at the end, which can be encoded in 1 byte. | ||
| 60 | Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, | ||
| 61 | else => @compileError("Unsupported OS"), | ||
| 62 | }; | ||
| 63 | |||
| 64 | pub const UserInfo = @import("os/get_user_id.zig").UserInfo; | ||
| 65 | pub const getUserInfo = @import("os/get_user_id.zig").getUserInfo; | ||
| 66 | |||
| 67 | const windows_util = @import("os/windows/util.zig"); | ||
| 68 | pub const windowsWaitSingle = windows_util.windowsWaitSingle; | ||
| 69 | pub const windowsWrite = windows_util.windowsWrite; | ||
| 70 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; | ||
| 71 | pub const windowsOpen = windows_util.windowsOpen; | ||
| 72 | pub const windowsOpenW = windows_util.windowsOpenW; | ||
| 73 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; | ||
| 74 | |||
| 75 | pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError; | ||
| 76 | pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort; | ||
| 77 | |||
| 78 | pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError; | ||
| 79 | pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus; | ||
| 80 | |||
| 81 | pub const WindowsWaitResult = windows_util.WindowsWaitResult; | ||
| 82 | pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus; | ||
| 83 | |||
| 84 | pub const WindowsWaitError = windows_util.WaitError; | ||
| 85 | pub const WindowsOpenError = windows_util.OpenError; | ||
| 86 | pub const WindowsWriteError = windows_util.WriteError; | ||
| 87 | pub const WindowsReadError = windows_util.ReadError; | ||
| 88 | |||
| 89 | pub const FileHandle = if (is_windows) windows.HANDLE else i32; | ||
| 90 | |||
| 91 | pub const getAppDataDir = @import("os/get_app_data_dir.zig").getAppDataDir; | ||
| 92 | pub const GetAppDataDirError = @import("os/get_app_data_dir.zig").GetAppDataDirError; | ||
| 93 | |||
| 94 | const debug = std.debug; | ||
| 95 | const assert = debug.assert; | ||
| 96 | const testing = std.testing; | ||
| 97 | |||
| 98 | const c = std.c; | ||
| 99 | |||
| 100 | const mem = std.mem; | ||
| 101 | const Allocator = mem.Allocator; | ||
| 102 | |||
| 103 | const BufMap = std.BufMap; | ||
| 104 | const cstr = std.cstr; | ||
| 105 | |||
| 106 | const io = std.io; | ||
| 107 | const base64 = std.base64; | ||
| 108 | const ArrayList = std.ArrayList; | ||
| 109 | const Buffer = std.Buffer; | ||
| 110 | const math = std.math; | ||
| 111 | |||
| 112 | /// Fills `buf` with random bytes. If linking against libc, this calls the | ||
| 113 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | ||
| 114 | /// library implementation. | ||
| 115 | pub fn getRandomBytes(buf: []u8) !void { | ||
| 116 | switch (builtin.os) { | ||
| 117 | Os.linux => while (true) { | ||
| 118 | // TODO check libc version and potentially call c.getrandom. | ||
| 119 | // See #397 | ||
| 120 | const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); | ||
| 121 | switch (errno) { | ||
| 122 | 0 => return, | ||
| 123 | posix.EINVAL => unreachable, | ||
| 124 | posix.EFAULT => unreachable, | ||
| 125 | posix.EINTR => continue, | ||
| 126 | posix.ENOSYS => return getRandomBytesDevURandom(buf), | ||
| 127 | else => return unexpectedErrorPosix(errno), | ||
| 128 | } | ||
| 129 | }, | ||
| 130 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), | ||
| 131 | Os.windows => { | ||
| 132 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | ||
| 133 | // https://github.com/rust-lang-nursery/rand/issues/111 | ||
| 134 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 | ||
| 135 | if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { | ||
| 136 | const err = windows.GetLastError(); | ||
| 137 | return switch (err) { | ||
| 138 | else => unexpectedErrorWindows(err), | ||
| 139 | }; | ||
| 140 | } | ||
| 141 | }, | ||
| 142 | Os.zen => { | ||
| 143 | const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; | ||
| 144 | var i: usize = 0; | ||
| 145 | while (i < buf.len) : (i += 1) { | ||
| 146 | if (i > randomness.len) return error.Unknown; | ||
| 147 | buf[i] = randomness[i]; | ||
| 148 | } | ||
| 149 | }, | ||
| 150 | else => @compileError("Unsupported OS"), | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | fn getRandomBytesDevURandom(buf: []u8) !void { | ||
| 155 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | ||
| 156 | defer close(fd); | ||
| 157 | |||
| 158 | const stream = &File.openHandle(fd).inStream().stream; | ||
| 159 | stream.readNoEof(buf) catch |err| switch (err) { | ||
| 160 | error.EndOfStream => unreachable, | ||
| 161 | error.OperationAborted => unreachable, | ||
| 162 | error.BrokenPipe => unreachable, | ||
| 163 | error.Unexpected => return error.Unexpected, | ||
| 164 | error.InputOutput => return error.Unexpected, | ||
| 165 | error.SystemResources => return error.Unexpected, | ||
| 166 | error.IsDir => unreachable, | ||
| 167 | }; | ||
| 168 | } | ||
| 169 | |||
| 170 | test "os.getRandomBytes" { | ||
| 171 | var buf_a: [50]u8 = undefined; | ||
| 172 | var buf_b: [50]u8 = undefined; | ||
| 173 | // Call Twice | ||
| 174 | try getRandomBytes(buf_a[0..]); | ||
| 175 | try getRandomBytes(buf_b[0..]); | ||
| 176 | |||
| 177 | // Check if random (not 100% conclusive) | ||
| 178 | testing.expect(!mem.eql(u8, buf_a, buf_b)); | ||
| 179 | } | ||
| 180 | |||
| 181 | /// Raises a signal in the current kernel thread, ending its execution. | ||
| 182 | /// If linking against libc, this calls the abort() libc function. Otherwise | ||
| 183 | /// it uses the zig standard library implementation. | ||
| 184 | pub fn abort() noreturn { | ||
| 185 | @setCold(true); | ||
| 186 | if (builtin.link_libc) { | ||
| 187 | c.abort(); | ||
| 188 | } | ||
| 189 | switch (builtin.os) { | ||
| 190 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 191 | _ = posix.raise(posix.SIGABRT); | ||
| 192 | _ = posix.raise(posix.SIGKILL); | ||
| 193 | while (true) {} | ||
| 194 | }, | ||
| 195 | Os.windows => { | ||
| 196 | if (builtin.mode == builtin.Mode.Debug) { | ||
| 197 | @breakpoint(); | ||
| 198 | } | ||
| 199 | windows.ExitProcess(3); | ||
| 200 | }, | ||
| 201 | Os.uefi => { | ||
| 202 | // TODO there's gotta be a better thing to do here than loop forever | ||
| 203 | while (true) {} | ||
| 204 | }, | ||
| 205 | else => @compileError("Unsupported OS"), | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Exits the program cleanly with the specified status code. | ||
| 210 | pub fn exit(status: u8) noreturn { | ||
| 211 | @setCold(true); | ||
| 212 | if (builtin.link_libc) { | ||
| 213 | c.exit(status); | ||
| 214 | } | ||
| 215 | switch (builtin.os) { | ||
| 216 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 217 | posix.exit(status); | ||
| 218 | }, | ||
| 219 | Os.windows => { | ||
| 220 | windows.ExitProcess(status); | ||
| 221 | }, | ||
| 222 | else => @compileError("Unsupported OS"), | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | /// When a file descriptor is closed on linux, it pops the first | ||
| 227 | /// node from this queue and resumes it. | ||
| 228 | /// Async functions which get the EMFILE error code can suspend, | ||
| 229 | /// putting their coroutine handle into this list. | ||
| 230 | /// TODO make this an atomic linked list | ||
| 231 | pub var emfile_promise_queue = std.LinkedList(promise).init(); | ||
| 232 | |||
| 233 | /// Closes the file handle. Keeps trying if it gets interrupted by a signal. | ||
| 234 | pub fn close(handle: FileHandle) void { | ||
| 235 | if (is_windows) { | ||
| 236 | windows_util.windowsClose(handle); | ||
| 237 | } else { | ||
| 238 | while (true) { | ||
| 239 | const err = posix.getErrno(posix.close(handle)); | ||
| 240 | switch (err) { | ||
| 241 | posix.EINTR => continue, | ||
| 242 | else => { | ||
| 243 | if (emfile_promise_queue.popFirst()) |p| resume p.data; | ||
| 244 | return; | ||
| 245 | }, | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | pub const PosixReadError = error{ | ||
| 252 | InputOutput, | ||
| 253 | SystemResources, | ||
| 254 | IsDir, | ||
| 255 | Unexpected, | ||
| 256 | }; | ||
| 257 | |||
| 258 | /// Returns the number of bytes that were read, which can be less than | ||
| 259 | /// buf.len. If 0 bytes were read, that means EOF. | ||
| 260 | pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { | ||
| 261 | // Linux can return EINVAL when read amount is > 0x7ffff000 | ||
| 262 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 | ||
| 263 | const max_buf_len = 0x7ffff000; | ||
| 264 | |||
| 265 | var index: usize = 0; | ||
| 266 | while (index < buf.len) { | ||
| 267 | const want_to_read = math.min(buf.len - index, usize(max_buf_len)); | ||
| 268 | const rc = posix.read(fd, buf.ptr + index, want_to_read); | ||
| 269 | const err = posix.getErrno(rc); | ||
| 270 | switch (err) { | ||
| 271 | 0 => { | ||
| 272 | index += rc; | ||
| 273 | if (rc == want_to_read) continue; | ||
| 274 | // Read returned less than buf.len. | ||
| 275 | return index; | ||
| 276 | }, | ||
| 277 | posix.EINTR => continue, | ||
| 278 | posix.EINVAL => unreachable, | ||
| 279 | posix.EFAULT => unreachable, | ||
| 280 | posix.EAGAIN => unreachable, | ||
| 281 | posix.EBADF => unreachable, // always a race condition | ||
| 282 | posix.EIO => return error.InputOutput, | ||
| 283 | posix.EISDIR => return error.IsDir, | ||
| 284 | posix.ENOBUFS => return error.SystemResources, | ||
| 285 | posix.ENOMEM => return error.SystemResources, | ||
| 286 | else => return unexpectedErrorPosix(err), | ||
| 287 | } | ||
| 288 | } | ||
| 289 | return index; | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | ||
| 293 | pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { | ||
| 294 | switch (builtin.os) { | ||
| 295 | builtin.Os.macosx => { | ||
| 296 | // Darwin does not have preadv but it does have pread. | ||
| 297 | var off: usize = 0; | ||
| 298 | var iov_i: usize = 0; | ||
| 299 | var inner_off: usize = 0; | ||
| 300 | while (true) { | ||
| 301 | const v = iov[iov_i]; | ||
| 302 | const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 303 | const err = darwin.getErrno(rc); | ||
| 304 | switch (err) { | ||
| 305 | 0 => { | ||
| 306 | off += rc; | ||
| 307 | inner_off += rc; | ||
| 308 | if (inner_off == v.iov_len) { | ||
| 309 | iov_i += 1; | ||
| 310 | inner_off = 0; | ||
| 311 | if (iov_i == count) { | ||
| 312 | return off; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | if (rc == 0) return off; // EOF | ||
| 316 | continue; | ||
| 317 | }, | ||
| 318 | posix.EINTR => continue, | ||
| 319 | posix.EINVAL => unreachable, | ||
| 320 | posix.EFAULT => unreachable, | ||
| 321 | posix.ESPIPE => unreachable, // fd is not seekable | ||
| 322 | posix.EAGAIN => unreachable, // this function is not for non blocking | ||
| 323 | posix.EBADF => unreachable, // always a race condition | ||
| 324 | posix.EIO => return error.InputOutput, | ||
| 325 | posix.EISDIR => return error.IsDir, | ||
| 326 | posix.ENOBUFS => return error.SystemResources, | ||
| 327 | posix.ENOMEM => return error.SystemResources, | ||
| 328 | else => return unexpectedErrorPosix(err), | ||
| 329 | } | ||
| 330 | } | ||
| 331 | }, | ||
| 332 | builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { | ||
| 333 | const rc = posix.preadv(fd, iov, count, offset); | ||
| 334 | const err = posix.getErrno(rc); | ||
| 335 | switch (err) { | ||
| 336 | 0 => return rc, | ||
| 337 | posix.EINTR => continue, | ||
| 338 | posix.EINVAL => unreachable, | ||
| 339 | posix.EFAULT => unreachable, | ||
| 340 | posix.EAGAIN => unreachable, // don't call this function for non blocking | ||
| 341 | posix.EBADF => unreachable, // always a race condition | ||
| 342 | posix.EIO => return error.InputOutput, | ||
| 343 | posix.EISDIR => return error.IsDir, | ||
| 344 | posix.ENOBUFS => return error.SystemResources, | ||
| 345 | posix.ENOMEM => return error.SystemResources, | ||
| 346 | else => return unexpectedErrorPosix(err), | ||
| 347 | } | ||
| 348 | }, | ||
| 349 | else => @compileError("Unsupported OS"), | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | pub const PosixWriteError = error{ | ||
| 354 | DiskQuota, | ||
| 355 | FileTooBig, | ||
| 356 | InputOutput, | ||
| 357 | NoSpaceLeft, | ||
| 358 | AccessDenied, | ||
| 359 | BrokenPipe, | ||
| 360 | |||
| 361 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 362 | Unexpected, | ||
| 363 | }; | ||
| 364 | |||
| 365 | /// Calls POSIX write, and keeps trying if it gets interrupted. | ||
| 366 | pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { | ||
| 367 | // Linux can return EINVAL when write amount is > 0x7ffff000 | ||
| 368 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 | ||
| 369 | const max_bytes_len = 0x7ffff000; | ||
| 370 | |||
| 371 | var index: usize = 0; | ||
| 372 | while (index < bytes.len) { | ||
| 373 | const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); | ||
| 374 | const rc = posix.write(fd, bytes.ptr + index, amt_to_write); | ||
| 375 | const write_err = posix.getErrno(rc); | ||
| 376 | switch (write_err) { | ||
| 377 | 0 => { | ||
| 378 | index += rc; | ||
| 379 | continue; | ||
| 380 | }, | ||
| 381 | posix.EINTR => continue, | ||
| 382 | posix.EINVAL => unreachable, | ||
| 383 | posix.EFAULT => unreachable, | ||
| 384 | posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking | ||
| 385 | posix.EBADF => unreachable, // always a race condition | ||
| 386 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 387 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 388 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 389 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 390 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 391 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 392 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 393 | else => return unexpectedErrorPosix(write_err), | ||
| 394 | } | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { | ||
| 399 | switch (builtin.os) { | ||
| 400 | builtin.Os.macosx => { | ||
| 401 | // Darwin does not have pwritev but it does have pwrite. | ||
| 402 | var off: usize = 0; | ||
| 403 | var iov_i: usize = 0; | ||
| 404 | var inner_off: usize = 0; | ||
| 405 | while (true) { | ||
| 406 | const v = iov[iov_i]; | ||
| 407 | const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 408 | const err = darwin.getErrno(rc); | ||
| 409 | switch (err) { | ||
| 410 | 0 => { | ||
| 411 | off += rc; | ||
| 412 | inner_off += rc; | ||
| 413 | if (inner_off == v.iov_len) { | ||
| 414 | iov_i += 1; | ||
| 415 | inner_off = 0; | ||
| 416 | if (iov_i == count) { | ||
| 417 | return; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | continue; | ||
| 421 | }, | ||
| 422 | posix.EINTR => continue, | ||
| 423 | posix.ESPIPE => unreachable, // fd is not seekable | ||
| 424 | posix.EINVAL => unreachable, | ||
| 425 | posix.EFAULT => unreachable, | ||
| 426 | posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking | ||
| 427 | posix.EBADF => unreachable, // always a race condition | ||
| 428 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 429 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 430 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 431 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 432 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 433 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 434 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 435 | else => return unexpectedErrorPosix(err), | ||
| 436 | } | ||
| 437 | } | ||
| 438 | }, | ||
| 439 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { | ||
| 440 | const rc = posix.pwritev(fd, iov, count, offset); | ||
| 441 | const err = posix.getErrno(rc); | ||
| 442 | switch (err) { | ||
| 443 | 0 => return, | ||
| 444 | posix.EINTR => continue, | ||
| 445 | posix.EINVAL => unreachable, | ||
| 446 | posix.EFAULT => unreachable, | ||
| 447 | posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking | ||
| 448 | posix.EBADF => unreachable, // always a race condition | ||
| 449 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 450 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 451 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 452 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 453 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 454 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 455 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 456 | else => return unexpectedErrorPosix(err), | ||
| 457 | } | ||
| 458 | }, | ||
| 459 | else => @compileError("Unsupported OS"), | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | pub const PosixOpenError = error{ | ||
| 464 | AccessDenied, | ||
| 465 | FileTooBig, | ||
| 466 | IsDir, | ||
| 467 | SymLinkLoop, | ||
| 468 | ProcessFdQuotaExceeded, | ||
| 469 | NameTooLong, | ||
| 470 | SystemFdQuotaExceeded, | ||
| 471 | NoDevice, | ||
| 472 | FileNotFound, | ||
| 473 | SystemResources, | ||
| 474 | NoSpaceLeft, | ||
| 475 | NotDir, | ||
| 476 | PathAlreadyExists, | ||
| 477 | DeviceBusy, | ||
| 478 | |||
| 479 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 480 | Unexpected, | ||
| 481 | }; | ||
| 482 | |||
| 483 | /// ::file_path needs to be copied in memory to add a null terminating byte. | ||
| 484 | /// Calls POSIX open, keeps trying if it gets interrupted, and translates | ||
| 485 | /// the return value into zig errors. | ||
| 486 | pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { | ||
| 487 | const file_path_c = try toPosixPath(file_path); | ||
| 488 | return posixOpenC(&file_path_c, flags, perm); | ||
| 489 | } | ||
| 490 | |||
| 491 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 492 | pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { | ||
| 493 | while (true) { | ||
| 494 | const result = posix.open(file_path, flags, perm); | ||
| 495 | const err = posix.getErrno(result); | ||
| 496 | if (err > 0) { | ||
| 497 | switch (err) { | ||
| 498 | posix.EINTR => continue, | ||
| 499 | |||
| 500 | posix.EFAULT => unreachable, | ||
| 501 | posix.EINVAL => unreachable, | ||
| 502 | posix.EACCES => return PosixOpenError.AccessDenied, | ||
| 503 | posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, | ||
| 504 | posix.EISDIR => return PosixOpenError.IsDir, | ||
| 505 | posix.ELOOP => return PosixOpenError.SymLinkLoop, | ||
| 506 | posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, | ||
| 507 | posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, | ||
| 508 | posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, | ||
| 509 | posix.ENODEV => return PosixOpenError.NoDevice, | ||
| 510 | posix.ENOENT => return PosixOpenError.FileNotFound, | ||
| 511 | posix.ENOMEM => return PosixOpenError.SystemResources, | ||
| 512 | posix.ENOSPC => return PosixOpenError.NoSpaceLeft, | ||
| 513 | posix.ENOTDIR => return PosixOpenError.NotDir, | ||
| 514 | posix.EPERM => return PosixOpenError.AccessDenied, | ||
| 515 | posix.EEXIST => return PosixOpenError.PathAlreadyExists, | ||
| 516 | posix.EBUSY => return PosixOpenError.DeviceBusy, | ||
| 517 | else => return unexpectedErrorPosix(err), | ||
| 518 | } | ||
| 519 | } | ||
| 520 | return @intCast(i32, result); | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | /// Used to convert a slice to a null terminated slice on the stack. | ||
| 525 | /// TODO well defined copy elision | ||
| 526 | pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { | ||
| 527 | var path_with_null: [posix.PATH_MAX]u8 = undefined; | ||
| 528 | if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; | ||
| 529 | mem.copy(u8, path_with_null[0..], file_path); | ||
| 530 | path_with_null[file_path.len] = 0; | ||
| 531 | return path_with_null; | ||
| 532 | } | ||
| 533 | |||
| 534 | pub fn posixDup2(old_fd: i32, new_fd: i32) !void { | ||
| 535 | while (true) { | ||
| 536 | const err = posix.getErrno(posix.dup2(old_fd, new_fd)); | ||
| 537 | if (err > 0) { | ||
| 538 | return switch (err) { | ||
| 539 | posix.EBUSY, posix.EINTR => continue, | ||
| 540 | posix.EMFILE => error.ProcessFdQuotaExceeded, | ||
| 541 | posix.EINVAL => unreachable, | ||
| 542 | else => unexpectedErrorPosix(err), | ||
| 543 | }; | ||
| 544 | } | ||
| 545 | return; | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { | ||
| 550 | const envp_count = env_map.count(); | ||
| 551 | const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); | ||
| 552 | mem.set(?[*]u8, envp_buf, null); | ||
| 553 | errdefer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 554 | { | ||
| 555 | var it = env_map.iterator(); | ||
| 556 | var i: usize = 0; | ||
| 557 | while (it.next()) |pair| : (i += 1) { | ||
| 558 | const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); | ||
| 559 | @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); | ||
| 560 | env_buf[pair.key.len] = '='; | ||
| 561 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); | ||
| 562 | env_buf[env_buf.len - 1] = 0; | ||
| 563 | |||
| 564 | envp_buf[i] = env_buf.ptr; | ||
| 565 | } | ||
| 566 | assert(i == envp_count); | ||
| 567 | } | ||
| 568 | assert(envp_buf[envp_count] == null); | ||
| 569 | return envp_buf; | ||
| 570 | } | ||
| 571 | |||
| 572 | pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { | ||
| 573 | for (envp_buf) |env| { | ||
| 574 | const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; | ||
| 575 | allocator.free(env_buf); | ||
| 576 | } | ||
| 577 | allocator.free(envp_buf); | ||
| 578 | } | ||
| 579 | |||
| 580 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | ||
| 581 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | ||
| 582 | /// pointers after the args and after the environment variables. | ||
| 583 | /// `argv[0]` is the executable path. | ||
| 584 | /// This function also uses the PATH environment variable to get the full path to the executable. | ||
| 585 | pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { | ||
| 586 | const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); | ||
| 587 | mem.set(?[*]u8, argv_buf, null); | ||
| 588 | defer { | ||
| 589 | for (argv_buf) |arg| { | ||
| 590 | const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; | ||
| 591 | allocator.free(arg_buf); | ||
| 592 | } | ||
| 593 | allocator.free(argv_buf); | ||
| 594 | } | ||
| 595 | for (argv) |arg, i| { | ||
| 596 | const arg_buf = try allocator.alloc(u8, arg.len + 1); | ||
| 597 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); | ||
| 598 | arg_buf[arg.len] = 0; | ||
| 599 | |||
| 600 | argv_buf[i] = arg_buf.ptr; | ||
| 601 | } | ||
| 602 | argv_buf[argv.len] = null; | ||
| 603 | |||
| 604 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); | ||
| 605 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 606 | |||
| 607 | const exe_path = argv[0]; | ||
| 608 | if (mem.indexOfScalar(u8, exe_path, '/') != null) { | ||
| 609 | return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); | ||
| 610 | } | ||
| 611 | |||
| 612 | const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; | ||
| 613 | // PATH.len because it is >= the largest search_path | ||
| 614 | // +1 for the / to join the search path and exe_path | ||
| 615 | // +1 for the null terminating byte | ||
| 616 | const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); | ||
| 617 | defer allocator.free(path_buf); | ||
| 618 | var it = mem.tokenize(PATH, ":"); | ||
| 619 | var seen_eacces = false; | ||
| 620 | var err: usize = undefined; | ||
| 621 | while (it.next()) |search_path| { | ||
| 622 | mem.copy(u8, path_buf, search_path); | ||
| 623 | path_buf[search_path.len] = '/'; | ||
| 624 | mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); | ||
| 625 | path_buf[search_path.len + exe_path.len + 1] = 0; | ||
| 626 | err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); | ||
| 627 | assert(err > 0); | ||
| 628 | if (err == posix.EACCES) { | ||
| 629 | seen_eacces = true; | ||
| 630 | } else if (err != posix.ENOENT) { | ||
| 631 | return posixExecveErrnoToErr(err); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | if (seen_eacces) { | ||
| 635 | err = posix.EACCES; | ||
| 636 | } | ||
| 637 | return posixExecveErrnoToErr(err); | ||
| 638 | } | ||
| 639 | |||
| 640 | pub const PosixExecveError = error{ | ||
| 641 | SystemResources, | ||
| 642 | AccessDenied, | ||
| 643 | InvalidExe, | ||
| 644 | FileSystem, | ||
| 645 | IsDir, | ||
| 646 | FileNotFound, | ||
| 647 | NotDir, | ||
| 648 | FileBusy, | ||
| 649 | |||
| 650 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 651 | Unexpected, | ||
| 652 | }; | ||
| 653 | |||
| 654 | fn posixExecveErrnoToErr(err: usize) PosixExecveError { | ||
| 655 | assert(err > 0); | ||
| 656 | switch (err) { | ||
| 657 | posix.EFAULT => unreachable, | ||
| 658 | posix.E2BIG => return error.SystemResources, | ||
| 659 | posix.EMFILE => return error.SystemResources, | ||
| 660 | posix.ENAMETOOLONG => return error.SystemResources, | ||
| 661 | posix.ENFILE => return error.SystemResources, | ||
| 662 | posix.ENOMEM => return error.SystemResources, | ||
| 663 | posix.EACCES => return error.AccessDenied, | ||
| 664 | posix.EPERM => return error.AccessDenied, | ||
| 665 | posix.EINVAL => return error.InvalidExe, | ||
| 666 | posix.ENOEXEC => return error.InvalidExe, | ||
| 667 | posix.EIO => return error.FileSystem, | ||
| 668 | posix.ELOOP => return error.FileSystem, | ||
| 669 | posix.EISDIR => return error.IsDir, | ||
| 670 | posix.ENOENT => return error.FileNotFound, | ||
| 671 | posix.ENOTDIR => return error.NotDir, | ||
| 672 | posix.ETXTBSY => return error.FileBusy, | ||
| 673 | else => return unexpectedErrorPosix(err), | ||
| 674 | } | ||
| 675 | } | ||
| 676 | |||
| 677 | pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; | ||
| 678 | pub var posix_environ_raw: [][*]u8 = undefined; | ||
| 679 | |||
| 680 | /// See std.elf for the constants. | ||
| 681 | pub fn linuxGetAuxVal(index: usize) usize { | ||
| 682 | if (builtin.link_libc) { | ||
| 683 | return usize(std.c.getauxval(index)); | ||
| 684 | } else if (linux_elf_aux_maybe) |auxv| { | ||
| 685 | var i: usize = 0; | ||
| 686 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 687 | if (auxv[i].a_type == index) | ||
| 688 | return auxv[i].a_un.a_val; | ||
| 689 | } | ||
| 690 | } | ||
| 691 | return 0; | ||
| 692 | } | ||
| 693 | |||
| 694 | pub fn getBaseAddress() usize { | ||
| 695 | switch (builtin.os) { | ||
| 696 | builtin.Os.linux => { | ||
| 697 | const base = linuxGetAuxVal(std.elf.AT_BASE); | ||
| 698 | if (base != 0) { | ||
| 699 | return base; | ||
| 700 | } | ||
| 701 | const phdr = linuxGetAuxVal(std.elf.AT_PHDR); | ||
| 702 | return phdr - @sizeOf(std.elf.Ehdr); | ||
| 703 | }, | ||
| 704 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { | ||
| 705 | return @ptrToInt(&std.c._mh_execute_header); | ||
| 706 | }, | ||
| 707 | builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), | ||
| 708 | else => @compileError("Unsupported OS"), | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | /// Caller must free result when done. | ||
| 713 | /// TODO make this go through libc when we have it | ||
| 714 | pub fn getEnvMap(allocator: *Allocator) !BufMap { | ||
| 715 | var result = BufMap.init(allocator); | ||
| 716 | errdefer result.deinit(); | ||
| 717 | |||
| 718 | if (is_windows) { | ||
| 719 | const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; | ||
| 720 | defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); | ||
| 721 | |||
| 722 | var i: usize = 0; | ||
| 723 | while (true) { | ||
| 724 | if (ptr[i] == 0) return result; | ||
| 725 | |||
| 726 | const key_start = i; | ||
| 727 | |||
| 728 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 729 | const key_w = ptr[key_start..i]; | ||
| 730 | const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); | ||
| 731 | errdefer allocator.free(key); | ||
| 732 | |||
| 733 | if (ptr[i] == '=') i += 1; | ||
| 734 | |||
| 735 | const value_start = i; | ||
| 736 | while (ptr[i] != 0) : (i += 1) {} | ||
| 737 | const value_w = ptr[value_start..i]; | ||
| 738 | const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); | ||
| 739 | errdefer allocator.free(value); | ||
| 740 | |||
| 741 | i += 1; // skip over null byte | ||
| 742 | |||
| 743 | try result.setMove(key, value); | ||
| 744 | } | ||
| 745 | } else { | ||
| 746 | for (posix_environ_raw) |ptr| { | ||
| 747 | var line_i: usize = 0; | ||
| 748 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | ||
| 749 | const key = ptr[0..line_i]; | ||
| 750 | |||
| 751 | var end_i: usize = line_i; | ||
| 752 | while (ptr[end_i] != 0) : (end_i += 1) {} | ||
| 753 | const value = ptr[line_i + 1 .. end_i]; | ||
| 754 | |||
| 755 | try result.set(key, value); | ||
| 756 | } | ||
| 757 | return result; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | test "os.getEnvMap" { | ||
| 762 | var env = try getEnvMap(std.debug.global_allocator); | ||
| 763 | defer env.deinit(); | ||
| 764 | } | ||
| 765 | |||
| 766 | /// TODO make this go through libc when we have it | ||
| 767 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { | ||
| 768 | for (posix_environ_raw) |ptr| { | ||
| 769 | var line_i: usize = 0; | ||
| 770 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | ||
| 771 | const this_key = ptr[0..line_i]; | ||
| 772 | if (!mem.eql(u8, key, this_key)) continue; | ||
| 773 | |||
| 774 | var end_i: usize = line_i; | ||
| 775 | while (ptr[end_i] != 0) : (end_i += 1) {} | ||
| 776 | const this_value = ptr[line_i + 1 .. end_i]; | ||
| 777 | |||
| 778 | return this_value; | ||
| 779 | } | ||
| 780 | return null; | ||
| 781 | } | ||
| 782 | |||
| 783 | pub const GetEnvVarOwnedError = error{ | ||
| 784 | OutOfMemory, | ||
| 785 | EnvironmentVariableNotFound, | ||
| 786 | |||
| 787 | /// See https://github.com/ziglang/zig/issues/1774 | ||
| 788 | InvalidUtf8, | ||
| 789 | }; | ||
| 790 | |||
| 791 | /// Caller must free returned memory. | ||
| 792 | /// TODO make this go through libc when we have it | ||
| 793 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { | ||
| 794 | if (is_windows) { | ||
| 795 | const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); | ||
| 796 | defer allocator.free(key_with_null); | ||
| 797 | |||
| 798 | var buf = try allocator.alloc(u16, 256); | ||
| 799 | defer allocator.free(buf); | ||
| 800 | |||
| 801 | while (true) { | ||
| 802 | const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; | ||
| 803 | const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); | ||
| 804 | |||
| 805 | if (result == 0) { | ||
| 806 | const err = windows.GetLastError(); | ||
| 807 | return switch (err) { | ||
| 808 | windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, | ||
| 809 | else => { | ||
| 810 | _ = unexpectedErrorWindows(err); | ||
| 811 | return error.EnvironmentVariableNotFound; | ||
| 812 | }, | ||
| 813 | }; | ||
| 814 | } | ||
| 815 | |||
| 816 | if (result > buf.len) { | ||
| 817 | buf = try allocator.realloc(u16, buf, result); | ||
| 818 | continue; | ||
| 819 | } | ||
| 820 | |||
| 821 | return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { | ||
| 822 | error.DanglingSurrogateHalf => return error.InvalidUtf8, | ||
| 823 | error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, | ||
| 824 | error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, | ||
| 825 | error.OutOfMemory => return error.OutOfMemory, | ||
| 826 | }; | ||
| 827 | } | ||
| 828 | } else { | ||
| 829 | const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; | ||
| 830 | return mem.dupe(allocator, u8, result); | ||
| 831 | } | ||
| 832 | } | ||
| 833 | |||
| 834 | test "os.getEnvVarOwned" { | ||
| 835 | var ga = debug.global_allocator; | ||
| 836 | testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); | ||
| 837 | } | ||
| 838 | |||
| 839 | /// Caller must free the returned memory. | ||
| 840 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { | ||
| 841 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 842 | return mem.dupe(allocator, u8, try getCwd(&buf)); | ||
| 843 | } | ||
| 844 | |||
| 845 | pub const GetCwdError = error{Unexpected}; | ||
| 846 | |||
| 847 | /// The result is a slice of out_buffer. | ||
| 848 | pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { | ||
| 849 | switch (builtin.os) { | ||
| 850 | Os.windows => { | ||
| 851 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 852 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | ||
| 853 | const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast | ||
| 854 | const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); | ||
| 855 | if (result == 0) { | ||
| 856 | const err = windows.GetLastError(); | ||
| 857 | switch (err) { | ||
| 858 | else => return unexpectedErrorWindows(err), | ||
| 859 | } | ||
| 860 | } | ||
| 861 | assert(result <= utf16le_buf.len); | ||
| 862 | const utf16le_slice = utf16le_buf[0..result]; | ||
| 863 | // Trust that Windows gives us valid UTF-16LE. | ||
| 864 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | ||
| 865 | return out_buffer[0..end_index]; | ||
| 866 | }, | ||
| 867 | else => { | ||
| 868 | const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); | ||
| 869 | switch (err) { | ||
| 870 | 0 => return cstr.toSlice(out_buffer), | ||
| 871 | posix.ERANGE => unreachable, | ||
| 872 | else => return unexpectedErrorPosix(err), | ||
| 873 | } | ||
| 874 | }, | ||
| 875 | } | ||
| 876 | } | ||
| 877 | |||
| 878 | test "os.getCwd" { | ||
| 879 | // at least call it so it gets compiled | ||
| 880 | _ = getCwdAlloc(debug.global_allocator); | ||
| 881 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 882 | _ = getCwd(&buf); | ||
| 883 | } | ||
| 884 | |||
| 885 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; | ||
| 886 | |||
| 887 | /// TODO add a symLinkC variant | ||
| 888 | pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { | ||
| 889 | if (is_windows) { | ||
| 890 | return symLinkWindows(existing_path, new_path); | ||
| 891 | } else { | ||
| 892 | return symLinkPosix(existing_path, new_path); | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | pub const WindowsSymLinkError = error{ | ||
| 897 | NameTooLong, | ||
| 898 | InvalidUtf8, | ||
| 899 | BadPathName, | ||
| 900 | |||
| 901 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 902 | Unexpected, | ||
| 903 | }; | ||
| 904 | |||
| 905 | pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { | ||
| 906 | if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { | ||
| 907 | const err = windows.GetLastError(); | ||
| 908 | switch (err) { | ||
| 909 | else => return unexpectedErrorWindows(err), | ||
| 910 | } | ||
| 911 | } | ||
| 912 | } | ||
| 913 | |||
| 914 | pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { | ||
| 915 | const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); | ||
| 916 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 917 | return symLinkW(&existing_path_w, &new_path_w); | ||
| 918 | } | ||
| 919 | |||
| 920 | pub const PosixSymLinkError = error{ | ||
| 921 | AccessDenied, | ||
| 922 | DiskQuota, | ||
| 923 | PathAlreadyExists, | ||
| 924 | FileSystem, | ||
| 925 | SymLinkLoop, | ||
| 926 | NameTooLong, | ||
| 927 | FileNotFound, | ||
| 928 | SystemResources, | ||
| 929 | NoSpaceLeft, | ||
| 930 | ReadOnlyFileSystem, | ||
| 931 | NotDir, | ||
| 932 | |||
| 933 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 934 | Unexpected, | ||
| 935 | }; | ||
| 936 | |||
| 937 | pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { | ||
| 938 | const err = posix.getErrno(posix.symlink(existing_path, new_path)); | ||
| 939 | switch (err) { | ||
| 940 | 0 => return, | ||
| 941 | posix.EFAULT => unreachable, | ||
| 942 | posix.EINVAL => unreachable, | ||
| 943 | posix.EACCES => return error.AccessDenied, | ||
| 944 | posix.EPERM => return error.AccessDenied, | ||
| 945 | posix.EDQUOT => return error.DiskQuota, | ||
| 946 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 947 | posix.EIO => return error.FileSystem, | ||
| 948 | posix.ELOOP => return error.SymLinkLoop, | ||
| 949 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 950 | posix.ENOENT => return error.FileNotFound, | ||
| 951 | posix.ENOTDIR => return error.NotDir, | ||
| 952 | posix.ENOMEM => return error.SystemResources, | ||
| 953 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 954 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 955 | else => return unexpectedErrorPosix(err), | ||
| 956 | } | ||
| 957 | } | ||
| 958 | |||
| 959 | pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { | ||
| 960 | const existing_path_c = try toPosixPath(existing_path); | ||
| 961 | const new_path_c = try toPosixPath(new_path); | ||
| 962 | return symLinkPosixC(&existing_path_c, &new_path_c); | ||
| 963 | } | ||
| 964 | |||
| 965 | // here we replace the standard +/ with -_ so that it can be used in a file name | ||
| 966 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); | ||
| 967 | |||
| 968 | /// TODO remove the allocator requirement from this API | ||
| 969 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { | ||
| 970 | if (symLink(existing_path, new_path)) { | ||
| 971 | return; | ||
| 972 | } else |err| switch (err) { | ||
| 973 | error.PathAlreadyExists => {}, | ||
| 974 | else => return err, // TODO zig should know this set does not include PathAlreadyExists | ||
| 975 | } | ||
| 976 | |||
| 977 | const dirname = os.path.dirname(new_path) orelse "."; | ||
| 978 | |||
| 979 | var rand_buf: [12]u8 = undefined; | ||
| 980 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); | ||
| 981 | defer allocator.free(tmp_path); | ||
| 982 | mem.copy(u8, tmp_path[0..], dirname); | ||
| 983 | tmp_path[dirname.len] = os.path.sep; | ||
| 984 | while (true) { | ||
| 985 | try getRandomBytes(rand_buf[0..]); | ||
| 986 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); | ||
| 987 | |||
| 988 | if (symLink(existing_path, tmp_path)) { | ||
| 989 | return rename(tmp_path, new_path); | ||
| 990 | } else |err| switch (err) { | ||
| 991 | error.PathAlreadyExists => continue, | ||
| 992 | else => return err, // TODO zig should know this set does not include PathAlreadyExists | ||
| 993 | } | ||
| 994 | } | ||
| 995 | } | ||
| 996 | |||
| 997 | pub const DeleteFileError = error{ | ||
| 998 | FileNotFound, | ||
| 999 | AccessDenied, | ||
| 1000 | FileBusy, | ||
| 1001 | FileSystem, | ||
| 1002 | IsDir, | ||
| 1003 | SymLinkLoop, | ||
| 1004 | NameTooLong, | ||
| 1005 | NotDir, | ||
| 1006 | SystemResources, | ||
| 1007 | ReadOnlyFileSystem, | ||
| 1008 | |||
| 1009 | /// On Windows, file paths must be valid Unicode. | ||
| 1010 | InvalidUtf8, | ||
| 1011 | |||
| 1012 | /// On Windows, file paths cannot contain these characters: | ||
| 1013 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 1014 | BadPathName, | ||
| 1015 | |||
| 1016 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1017 | Unexpected, | ||
| 1018 | }; | ||
| 1019 | |||
| 1020 | pub fn deleteFile(file_path: []const u8) DeleteFileError!void { | ||
| 1021 | if (builtin.os == Os.windows) { | ||
| 1022 | const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); | ||
| 1023 | return deleteFileW(&file_path_w); | ||
| 1024 | } else { | ||
| 1025 | const file_path_c = try toPosixPath(file_path); | ||
| 1026 | return deleteFileC(&file_path_c); | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { | ||
| 1031 | if (windows.DeleteFileW(file_path) == 0) { | ||
| 1032 | const err = windows.GetLastError(); | ||
| 1033 | switch (err) { | ||
| 1034 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 1035 | windows.ERROR.ACCESS_DENIED => return error.AccessDenied, | ||
| 1036 | windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, | ||
| 1037 | windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, | ||
| 1038 | else => return unexpectedErrorWindows(err), | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { | ||
| 1044 | if (is_windows) { | ||
| 1045 | const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); | ||
| 1046 | return deleteFileW(&file_path_w); | ||
| 1047 | } else { | ||
| 1048 | const err = posix.getErrno(posix.unlink(file_path)); | ||
| 1049 | switch (err) { | ||
| 1050 | 0 => return, | ||
| 1051 | posix.EACCES => return error.AccessDenied, | ||
| 1052 | posix.EPERM => return error.AccessDenied, | ||
| 1053 | posix.EBUSY => return error.FileBusy, | ||
| 1054 | posix.EFAULT => unreachable, | ||
| 1055 | posix.EINVAL => unreachable, | ||
| 1056 | posix.EIO => return error.FileSystem, | ||
| 1057 | posix.EISDIR => return error.IsDir, | ||
| 1058 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1059 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1060 | posix.ENOENT => return error.FileNotFound, | ||
| 1061 | posix.ENOTDIR => return error.NotDir, | ||
| 1062 | posix.ENOMEM => return error.SystemResources, | ||
| 1063 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1064 | else => return unexpectedErrorPosix(err), | ||
| 1065 | } | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | ||
| 1070 | /// merged and readily available, | ||
| 1071 | /// there is a possibility of power loss or application termination leaving temporary files present | ||
| 1072 | /// in the same directory as dest_path. | ||
| 1073 | /// Destination file will have the same mode as the source file. | ||
| 1074 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { | ||
| 1075 | var in_file = try os.File.openRead(source_path); | ||
| 1076 | defer in_file.close(); | ||
| 1077 | |||
| 1078 | const mode = try in_file.mode(); | ||
| 1079 | |||
| 1080 | var atomic_file = try AtomicFile.init(dest_path, mode); | ||
| 1081 | defer atomic_file.deinit(); | ||
| 1082 | |||
| 1083 | var buf: [page_size]u8 = undefined; | ||
| 1084 | while (true) { | ||
| 1085 | const amt = try in_file.readFull(buf[0..]); | ||
| 1086 | try atomic_file.file.write(buf[0..amt]); | ||
| 1087 | if (amt != buf.len) { | ||
| 1088 | return atomic_file.finish(); | ||
| 1089 | } | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | ||
| 1094 | /// merged and readily available, | ||
| 1095 | /// there is a possibility of power loss or application termination leaving temporary files present | ||
| 1096 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | ||
| 1097 | var in_file = try os.File.openRead(source_path); | ||
| 1098 | defer in_file.close(); | ||
| 1099 | |||
| 1100 | var atomic_file = try AtomicFile.init(dest_path, mode); | ||
| 1101 | defer atomic_file.deinit(); | ||
| 1102 | |||
| 1103 | var buf: [page_size]u8 = undefined; | ||
| 1104 | while (true) { | ||
| 1105 | const amt = try in_file.read(buf[0..]); | ||
| 1106 | try atomic_file.file.write(buf[0..amt]); | ||
| 1107 | if (amt != buf.len) { | ||
| 1108 | return atomic_file.finish(); | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | pub const AtomicFile = struct { | ||
| 1114 | file: os.File, | ||
| 1115 | tmp_path_buf: [MAX_PATH_BYTES]u8, | ||
| 1116 | dest_path: []const u8, | ||
| 1117 | finished: bool, | ||
| 1118 | |||
| 1119 | const InitError = os.File.OpenError; | ||
| 1120 | |||
| 1121 | /// dest_path must remain valid for the lifetime of AtomicFile | ||
| 1122 | /// call finish to atomically replace dest_path with contents | ||
| 1123 | /// TODO once we have null terminated pointers, use the | ||
| 1124 | /// openWriteNoClobberN function | ||
| 1125 | pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { | ||
| 1126 | const dirname = os.path.dirname(dest_path); | ||
| 1127 | var rand_buf: [12]u8 = undefined; | ||
| 1128 | const dirname_component_len = if (dirname) |d| d.len + 1 else 0; | ||
| 1129 | const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); | ||
| 1130 | const tmp_path_len = dirname_component_len + encoded_rand_len; | ||
| 1131 | var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 1132 | if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; | ||
| 1133 | |||
| 1134 | if (dirname) |dir| { | ||
| 1135 | mem.copy(u8, tmp_path_buf[0..], dir); | ||
| 1136 | tmp_path_buf[dir.len] = os.path.sep; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | tmp_path_buf[tmp_path_len] = 0; | ||
| 1140 | |||
| 1141 | while (true) { | ||
| 1142 | try getRandomBytes(rand_buf[0..]); | ||
| 1143 | b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); | ||
| 1144 | |||
| 1145 | const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) { | ||
| 1146 | error.PathAlreadyExists => continue, | ||
| 1147 | // TODO zig should figure out that this error set does not include PathAlreadyExists since | ||
| 1148 | // it is handled in the above switch | ||
| 1149 | else => return err, | ||
| 1150 | }; | ||
| 1151 | |||
| 1152 | return AtomicFile{ | ||
| 1153 | .file = file, | ||
| 1154 | .tmp_path_buf = tmp_path_buf, | ||
| 1155 | .dest_path = dest_path, | ||
| 1156 | .finished = false, | ||
| 1157 | }; | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | /// always call deinit, even after successful finish() | ||
| 1162 | pub fn deinit(self: *AtomicFile) void { | ||
| 1163 | if (!self.finished) { | ||
| 1164 | self.file.close(); | ||
| 1165 | deleteFileC(&self.tmp_path_buf) catch {}; | ||
| 1166 | self.finished = true; | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | pub fn finish(self: *AtomicFile) !void { | ||
| 1171 | assert(!self.finished); | ||
| 1172 | self.file.close(); | ||
| 1173 | self.finished = true; | ||
| 1174 | if (is_posix) { | ||
| 1175 | const dest_path_c = try toPosixPath(self.dest_path); | ||
| 1176 | return renameC(&self.tmp_path_buf, &dest_path_c); | ||
| 1177 | } else if (is_windows) { | ||
| 1178 | const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); | ||
| 1179 | const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); | ||
| 1180 | return renameW(&tmp_path_w, &dest_path_w); | ||
| 1181 | } else { | ||
| 1182 | @compileError("Unsupported OS"); | ||
| 1183 | } | ||
| 1184 | } | ||
| 1185 | }; | ||
| 1186 | |||
| 1187 | pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { | ||
| 1188 | if (is_windows) { | ||
| 1189 | const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); | ||
| 1190 | const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); | ||
| 1191 | return renameW(&old_path_w, &new_path_w); | ||
| 1192 | } else { | ||
| 1193 | const err = posix.getErrno(posix.rename(old_path, new_path)); | ||
| 1194 | switch (err) { | ||
| 1195 | 0 => return, | ||
| 1196 | posix.EACCES => return error.AccessDenied, | ||
| 1197 | posix.EPERM => return error.AccessDenied, | ||
| 1198 | posix.EBUSY => return error.FileBusy, | ||
| 1199 | posix.EDQUOT => return error.DiskQuota, | ||
| 1200 | posix.EFAULT => unreachable, | ||
| 1201 | posix.EINVAL => unreachable, | ||
| 1202 | posix.EISDIR => return error.IsDir, | ||
| 1203 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1204 | posix.EMLINK => return error.LinkQuotaExceeded, | ||
| 1205 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1206 | posix.ENOENT => return error.FileNotFound, | ||
| 1207 | posix.ENOTDIR => return error.NotDir, | ||
| 1208 | posix.ENOMEM => return error.SystemResources, | ||
| 1209 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1210 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1211 | posix.ENOTEMPTY => return error.PathAlreadyExists, | ||
| 1212 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1213 | posix.EXDEV => return error.RenameAcrossMountPoints, | ||
| 1214 | else => return unexpectedErrorPosix(err), | ||
| 1215 | } | ||
| 1216 | } | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { | ||
| 1220 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | ||
| 1221 | if (windows.MoveFileExW(old_path, new_path, flags) == 0) { | ||
| 1222 | const err = windows.GetLastError(); | ||
| 1223 | switch (err) { | ||
| 1224 | else => return unexpectedErrorWindows(err), | ||
| 1225 | } | ||
| 1226 | } | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | pub fn rename(old_path: []const u8, new_path: []const u8) !void { | ||
| 1230 | if (is_windows) { | ||
| 1231 | const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); | ||
| 1232 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 1233 | return renameW(&old_path_w, &new_path_w); | ||
| 1234 | } else { | ||
| 1235 | const old_path_c = try toPosixPath(old_path); | ||
| 1236 | const new_path_c = try toPosixPath(new_path); | ||
| 1237 | return renameC(&old_path_c, &new_path_c); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | pub fn makeDir(dir_path: []const u8) !void { | ||
| 1242 | if (is_windows) { | ||
| 1243 | return makeDirWindows(dir_path); | ||
| 1244 | } else { | ||
| 1245 | return makeDirPosix(dir_path); | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | pub fn makeDirWindows(dir_path: []const u8) !void { | ||
| 1250 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | ||
| 1251 | |||
| 1252 | if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { | ||
| 1253 | const err = windows.GetLastError(); | ||
| 1254 | return switch (err) { | ||
| 1255 | windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, | ||
| 1256 | windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, | ||
| 1257 | else => unexpectedErrorWindows(err), | ||
| 1258 | }; | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | pub fn makeDirPosixC(dir_path: [*]const u8) !void { | ||
| 1263 | const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); | ||
| 1264 | switch (err) { | ||
| 1265 | 0 => return, | ||
| 1266 | posix.EACCES => return error.AccessDenied, | ||
| 1267 | posix.EPERM => return error.AccessDenied, | ||
| 1268 | posix.EDQUOT => return error.DiskQuota, | ||
| 1269 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1270 | posix.EFAULT => unreachable, | ||
| 1271 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1272 | posix.EMLINK => return error.LinkQuotaExceeded, | ||
| 1273 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1274 | posix.ENOENT => return error.FileNotFound, | ||
| 1275 | posix.ENOMEM => return error.SystemResources, | ||
| 1276 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1277 | posix.ENOTDIR => return error.NotDir, | ||
| 1278 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1279 | else => return unexpectedErrorPosix(err), | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | pub fn makeDirPosix(dir_path: []const u8) !void { | ||
| 1284 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1285 | return makeDirPosixC(&dir_path_c); | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | /// Calls makeDir recursively to make an entire path. Returns success if the path | ||
| 1289 | /// already exists and is a directory. | ||
| 1290 | /// TODO determine if we can remove the allocator requirement from this function | ||
| 1291 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { | ||
| 1292 | const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); | ||
| 1293 | defer allocator.free(resolved_path); | ||
| 1294 | |||
| 1295 | var end_index: usize = resolved_path.len; | ||
| 1296 | while (true) { | ||
| 1297 | makeDir(resolved_path[0..end_index]) catch |err| switch (err) { | ||
| 1298 | error.PathAlreadyExists => { | ||
| 1299 | // TODO stat the file and return an error if it's not a directory | ||
| 1300 | // this is important because otherwise a dangling symlink | ||
| 1301 | // could cause an infinite loop | ||
| 1302 | if (end_index == resolved_path.len) return; | ||
| 1303 | }, | ||
| 1304 | error.FileNotFound => { | ||
| 1305 | // march end_index backward until next path component | ||
| 1306 | while (true) { | ||
| 1307 | end_index -= 1; | ||
| 1308 | if (os.path.isSep(resolved_path[end_index])) break; | ||
| 1309 | } | ||
| 1310 | continue; | ||
| 1311 | }, | ||
| 1312 | else => return err, | ||
| 1313 | }; | ||
| 1314 | if (end_index == resolved_path.len) return; | ||
| 1315 | // march end_index forward until next path component | ||
| 1316 | while (true) { | ||
| 1317 | end_index += 1; | ||
| 1318 | if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | pub const DeleteDirError = error{ | ||
| 1324 | AccessDenied, | ||
| 1325 | FileBusy, | ||
| 1326 | SymLinkLoop, | ||
| 1327 | NameTooLong, | ||
| 1328 | FileNotFound, | ||
| 1329 | SystemResources, | ||
| 1330 | NotDir, | ||
| 1331 | DirNotEmpty, | ||
| 1332 | ReadOnlyFileSystem, | ||
| 1333 | InvalidUtf8, | ||
| 1334 | BadPathName, | ||
| 1335 | |||
| 1336 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1337 | Unexpected, | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { | ||
| 1341 | switch (builtin.os) { | ||
| 1342 | Os.windows => { | ||
| 1343 | const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); | ||
| 1344 | return deleteDirW(&dir_path_w); | ||
| 1345 | }, | ||
| 1346 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 1347 | const err = posix.getErrno(posix.rmdir(dir_path)); | ||
| 1348 | switch (err) { | ||
| 1349 | 0 => return, | ||
| 1350 | posix.EACCES => return error.AccessDenied, | ||
| 1351 | posix.EPERM => return error.AccessDenied, | ||
| 1352 | posix.EBUSY => return error.FileBusy, | ||
| 1353 | posix.EFAULT => unreachable, | ||
| 1354 | posix.EINVAL => unreachable, | ||
| 1355 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1356 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1357 | posix.ENOENT => return error.FileNotFound, | ||
| 1358 | posix.ENOMEM => return error.SystemResources, | ||
| 1359 | posix.ENOTDIR => return error.NotDir, | ||
| 1360 | posix.EEXIST => return error.DirNotEmpty, | ||
| 1361 | posix.ENOTEMPTY => return error.DirNotEmpty, | ||
| 1362 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1363 | else => return unexpectedErrorPosix(err), | ||
| 1364 | } | ||
| 1365 | }, | ||
| 1366 | else => @compileError("unimplemented"), | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { | ||
| 1371 | if (windows.RemoveDirectoryW(dir_path_w) == 0) { | ||
| 1372 | const err = windows.GetLastError(); | ||
| 1373 | switch (err) { | ||
| 1374 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 1375 | windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, | ||
| 1376 | else => return unexpectedErrorWindows(err), | ||
| 1377 | } | ||
| 1378 | } | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | /// Returns ::error.DirNotEmpty if the directory is not empty. | ||
| 1382 | /// To delete a directory recursively, see ::deleteTree | ||
| 1383 | pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { | ||
| 1384 | switch (builtin.os) { | ||
| 1385 | Os.windows => { | ||
| 1386 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | ||
| 1387 | return deleteDirW(&dir_path_w); | ||
| 1388 | }, | ||
| 1389 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 1390 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1391 | return deleteDirC(&dir_path_c); | ||
| 1392 | }, | ||
| 1393 | else => @compileError("unimplemented"), | ||
| 1394 | } | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | /// Whether ::full_path describes a symlink, file, or directory, this function | ||
| 1398 | /// removes it. If it cannot be removed because it is a non-empty directory, | ||
| 1399 | /// this function recursively removes its entries and then tries again. | ||
| 1400 | const DeleteTreeError = error{ | ||
| 1401 | OutOfMemory, | ||
| 1402 | AccessDenied, | ||
| 1403 | FileTooBig, | ||
| 1404 | IsDir, | ||
| 1405 | SymLinkLoop, | ||
| 1406 | ProcessFdQuotaExceeded, | ||
| 1407 | NameTooLong, | ||
| 1408 | SystemFdQuotaExceeded, | ||
| 1409 | NoDevice, | ||
| 1410 | SystemResources, | ||
| 1411 | NoSpaceLeft, | ||
| 1412 | PathAlreadyExists, | ||
| 1413 | ReadOnlyFileSystem, | ||
| 1414 | NotDir, | ||
| 1415 | FileNotFound, | ||
| 1416 | FileSystem, | ||
| 1417 | FileBusy, | ||
| 1418 | DirNotEmpty, | ||
| 1419 | DeviceBusy, | ||
| 1420 | |||
| 1421 | /// On Windows, file paths must be valid Unicode. | ||
| 1422 | InvalidUtf8, | ||
| 1423 | |||
| 1424 | /// On Windows, file paths cannot contain these characters: | ||
| 1425 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 1426 | BadPathName, | ||
| 1427 | |||
| 1428 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1429 | Unexpected, | ||
| 1430 | }; | ||
| 1431 | |||
| 1432 | /// TODO determine if we can remove the allocator requirement | ||
| 1433 | pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { | ||
| 1434 | start_over: while (true) { | ||
| 1435 | var got_access_denied = false; | ||
| 1436 | // First, try deleting the item as a file. This way we don't follow sym links. | ||
| 1437 | if (deleteFile(full_path)) { | ||
| 1438 | return; | ||
| 1439 | } else |err| switch (err) { | ||
| 1440 | error.FileNotFound => return, | ||
| 1441 | error.IsDir => {}, | ||
| 1442 | error.AccessDenied => got_access_denied = true, | ||
| 1443 | |||
| 1444 | error.InvalidUtf8, | ||
| 1445 | error.SymLinkLoop, | ||
| 1446 | error.NameTooLong, | ||
| 1447 | error.SystemResources, | ||
| 1448 | error.ReadOnlyFileSystem, | ||
| 1449 | error.NotDir, | ||
| 1450 | error.FileSystem, | ||
| 1451 | error.FileBusy, | ||
| 1452 | error.BadPathName, | ||
| 1453 | error.Unexpected, | ||
| 1454 | => return err, | ||
| 1455 | } | ||
| 1456 | { | ||
| 1457 | var dir = Dir.open(allocator, full_path) catch |err| switch (err) { | ||
| 1458 | error.NotDir => { | ||
| 1459 | if (got_access_denied) { | ||
| 1460 | return error.AccessDenied; | ||
| 1461 | } | ||
| 1462 | continue :start_over; | ||
| 1463 | }, | ||
| 1464 | |||
| 1465 | error.OutOfMemory, | ||
| 1466 | error.AccessDenied, | ||
| 1467 | error.FileTooBig, | ||
| 1468 | error.IsDir, | ||
| 1469 | error.SymLinkLoop, | ||
| 1470 | error.ProcessFdQuotaExceeded, | ||
| 1471 | error.NameTooLong, | ||
| 1472 | error.SystemFdQuotaExceeded, | ||
| 1473 | error.NoDevice, | ||
| 1474 | error.FileNotFound, | ||
| 1475 | error.SystemResources, | ||
| 1476 | error.NoSpaceLeft, | ||
| 1477 | error.PathAlreadyExists, | ||
| 1478 | error.Unexpected, | ||
| 1479 | error.InvalidUtf8, | ||
| 1480 | error.BadPathName, | ||
| 1481 | error.DeviceBusy, | ||
| 1482 | => return err, | ||
| 1483 | }; | ||
| 1484 | defer dir.close(); | ||
| 1485 | |||
| 1486 | var full_entry_buf = ArrayList(u8).init(allocator); | ||
| 1487 | defer full_entry_buf.deinit(); | ||
| 1488 | |||
| 1489 | while (try dir.next()) |entry| { | ||
| 1490 | try full_entry_buf.resize(full_path.len + entry.name.len + 1); | ||
| 1491 | const full_entry_path = full_entry_buf.toSlice(); | ||
| 1492 | mem.copy(u8, full_entry_path, full_path); | ||
| 1493 | full_entry_path[full_path.len] = path.sep; | ||
| 1494 | mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); | ||
| 1495 | |||
| 1496 | try deleteTree(allocator, full_entry_path); | ||
| 1497 | } | ||
| 1498 | } | ||
| 1499 | return deleteDir(full_path); | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | pub const Dir = struct { | ||
| 1504 | handle: Handle, | ||
| 1505 | allocator: *Allocator, | ||
| 1506 | |||
| 1507 | pub const Handle = switch (builtin.os) { | ||
| 1508 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct { | ||
| 1509 | fd: i32, | ||
| 1510 | seek: i64, | ||
| 1511 | buf: []u8, | ||
| 1512 | index: usize, | ||
| 1513 | end_index: usize, | ||
| 1514 | }, | ||
| 1515 | Os.linux => struct { | ||
| 1516 | fd: i32, | ||
| 1517 | buf: []u8, | ||
| 1518 | index: usize, | ||
| 1519 | end_index: usize, | ||
| 1520 | }, | ||
| 1521 | Os.windows => struct { | ||
| 1522 | handle: windows.HANDLE, | ||
| 1523 | find_file_data: windows.WIN32_FIND_DATAW, | ||
| 1524 | first: bool, | ||
| 1525 | name_data: [256]u8, | ||
| 1526 | }, | ||
| 1527 | else => @compileError("unimplemented"), | ||
| 1528 | }; | ||
| 1529 | |||
| 1530 | pub const Entry = struct { | ||
| 1531 | name: []const u8, | ||
| 1532 | kind: Kind, | ||
| 1533 | |||
| 1534 | pub const Kind = enum { | ||
| 1535 | BlockDevice, | ||
| 1536 | CharacterDevice, | ||
| 1537 | Directory, | ||
| 1538 | NamedPipe, | ||
| 1539 | SymLink, | ||
| 1540 | File, | ||
| 1541 | UnixDomainSocket, | ||
| 1542 | Whiteout, | ||
| 1543 | Unknown, | ||
| 1544 | }; | ||
| 1545 | }; | ||
| 1546 | |||
| 1547 | pub const OpenError = error{ | ||
| 1548 | FileNotFound, | ||
| 1549 | NotDir, | ||
| 1550 | AccessDenied, | ||
| 1551 | FileTooBig, | ||
| 1552 | IsDir, | ||
| 1553 | SymLinkLoop, | ||
| 1554 | ProcessFdQuotaExceeded, | ||
| 1555 | NameTooLong, | ||
| 1556 | SystemFdQuotaExceeded, | ||
| 1557 | NoDevice, | ||
| 1558 | SystemResources, | ||
| 1559 | NoSpaceLeft, | ||
| 1560 | PathAlreadyExists, | ||
| 1561 | OutOfMemory, | ||
| 1562 | InvalidUtf8, | ||
| 1563 | BadPathName, | ||
| 1564 | DeviceBusy, | ||
| 1565 | |||
| 1566 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1567 | Unexpected, | ||
| 1568 | }; | ||
| 1569 | |||
| 1570 | /// TODO remove the allocator requirement from this API | ||
| 1571 | pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { | ||
| 1572 | return Dir{ | ||
| 1573 | .allocator = allocator, | ||
| 1574 | .handle = switch (builtin.os) { | ||
| 1575 | Os.windows => blk: { | ||
| 1576 | var find_file_data: windows.WIN32_FIND_DATAW = undefined; | ||
| 1577 | const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data); | ||
| 1578 | break :blk Handle{ | ||
| 1579 | .handle = handle, | ||
| 1580 | .find_file_data = find_file_data, // TODO guaranteed copy elision | ||
| 1581 | .first = true, | ||
| 1582 | .name_data = undefined, | ||
| 1583 | }; | ||
| 1584 | }, | ||
| 1585 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{ | ||
| 1586 | .fd = try posixOpen( | ||
| 1587 | dir_path, | ||
| 1588 | posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, | ||
| 1589 | 0, | ||
| 1590 | ), | ||
| 1591 | .seek = 0, | ||
| 1592 | .index = 0, | ||
| 1593 | .end_index = 0, | ||
| 1594 | .buf = []u8{}, | ||
| 1595 | }, | ||
| 1596 | Os.linux => Handle{ | ||
| 1597 | .fd = try posixOpen( | ||
| 1598 | dir_path, | ||
| 1599 | posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, | ||
| 1600 | 0, | ||
| 1601 | ), | ||
| 1602 | .index = 0, | ||
| 1603 | .end_index = 0, | ||
| 1604 | .buf = []u8{}, | ||
| 1605 | }, | ||
| 1606 | else => @compileError("unimplemented"), | ||
| 1607 | }, | ||
| 1608 | }; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | pub fn close(self: *Dir) void { | ||
| 1612 | switch (builtin.os) { | ||
| 1613 | Os.windows => { | ||
| 1614 | _ = windows.FindClose(self.handle.handle); | ||
| 1615 | }, | ||
| 1616 | Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { | ||
| 1617 | self.allocator.free(self.handle.buf); | ||
| 1618 | os.close(self.handle.fd); | ||
| 1619 | }, | ||
| 1620 | else => @compileError("unimplemented"), | ||
| 1621 | } | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | /// Memory such as file names referenced in this returned entry becomes invalid | ||
| 1625 | /// with subsequent calls to next, as well as when this `Dir` is deinitialized. | ||
| 1626 | pub fn next(self: *Dir) !?Entry { | ||
| 1627 | switch (builtin.os) { | ||
| 1628 | Os.linux => return self.nextLinux(), | ||
| 1629 | Os.macosx, Os.ios => return self.nextDarwin(), | ||
| 1630 | Os.windows => return self.nextWindows(), | ||
| 1631 | Os.freebsd => return self.nextFreebsd(), | ||
| 1632 | Os.netbsd => return self.nextFreebsd(), | ||
| 1633 | else => @compileError("unimplemented"), | ||
| 1634 | } | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | fn nextDarwin(self: *Dir) !?Entry { | ||
| 1638 | start_over: while (true) { | ||
| 1639 | if (self.handle.index >= self.handle.end_index) { | ||
| 1640 | if (self.handle.buf.len == 0) { | ||
| 1641 | self.handle.buf = try self.allocator.alloc(u8, page_size); | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | while (true) { | ||
| 1645 | const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); | ||
| 1646 | const err = posix.getErrno(result); | ||
| 1647 | if (err > 0) { | ||
| 1648 | switch (err) { | ||
| 1649 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | ||
| 1650 | posix.EINVAL => { | ||
| 1651 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | ||
| 1652 | continue; | ||
| 1653 | }, | ||
| 1654 | else => return unexpectedErrorPosix(err), | ||
| 1655 | } | ||
| 1656 | } | ||
| 1657 | if (result == 0) return null; | ||
| 1658 | self.handle.index = 0; | ||
| 1659 | self.handle.end_index = result; | ||
| 1660 | break; | ||
| 1661 | } | ||
| 1662 | } | ||
| 1663 | const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); | ||
| 1664 | const next_index = self.handle.index + darwin_entry.d_reclen; | ||
| 1665 | self.handle.index = next_index; | ||
| 1666 | |||
| 1667 | const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; | ||
| 1668 | |||
| 1669 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 1670 | continue :start_over; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | const entry_kind = switch (darwin_entry.d_type) { | ||
| 1674 | posix.DT_BLK => Entry.Kind.BlockDevice, | ||
| 1675 | posix.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 1676 | posix.DT_DIR => Entry.Kind.Directory, | ||
| 1677 | posix.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 1678 | posix.DT_LNK => Entry.Kind.SymLink, | ||
| 1679 | posix.DT_REG => Entry.Kind.File, | ||
| 1680 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 1681 | posix.DT_WHT => Entry.Kind.Whiteout, | ||
| 1682 | else => Entry.Kind.Unknown, | ||
| 1683 | }; | ||
| 1684 | return Entry{ | ||
| 1685 | .name = name, | ||
| 1686 | .kind = entry_kind, | ||
| 1687 | }; | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | fn nextWindows(self: *Dir) !?Entry { | ||
| 1692 | while (true) { | ||
| 1693 | if (self.handle.first) { | ||
| 1694 | self.handle.first = false; | ||
| 1695 | } else { | ||
| 1696 | if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) | ||
| 1697 | return null; | ||
| 1698 | } | ||
| 1699 | const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); | ||
| 1700 | if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) | ||
| 1701 | continue; | ||
| 1702 | // Trust that Windows gives us valid UTF-16LE | ||
| 1703 | const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; | ||
| 1704 | const name_utf8 = self.handle.name_data[0..name_utf8_len]; | ||
| 1705 | const kind = blk: { | ||
| 1706 | const attrs = self.handle.find_file_data.dwFileAttributes; | ||
| 1707 | if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; | ||
| 1708 | if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; | ||
| 1709 | if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File; | ||
| 1710 | break :blk Entry.Kind.Unknown; | ||
| 1711 | }; | ||
| 1712 | return Entry{ | ||
| 1713 | .name = name_utf8, | ||
| 1714 | .kind = kind, | ||
| 1715 | }; | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | fn nextLinux(self: *Dir) !?Entry { | ||
| 1720 | start_over: while (true) { | ||
| 1721 | if (self.handle.index >= self.handle.end_index) { | ||
| 1722 | if (self.handle.buf.len == 0) { | ||
| 1723 | self.handle.buf = try self.allocator.alloc(u8, page_size); | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | while (true) { | ||
| 1727 | const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); | ||
| 1728 | const err = posix.getErrno(result); | ||
| 1729 | if (err > 0) { | ||
| 1730 | switch (err) { | ||
| 1731 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | ||
| 1732 | posix.EINVAL => { | ||
| 1733 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | ||
| 1734 | continue; | ||
| 1735 | }, | ||
| 1736 | else => return unexpectedErrorPosix(err), | ||
| 1737 | } | ||
| 1738 | } | ||
| 1739 | if (result == 0) return null; | ||
| 1740 | self.handle.index = 0; | ||
| 1741 | self.handle.end_index = result; | ||
| 1742 | break; | ||
| 1743 | } | ||
| 1744 | } | ||
| 1745 | const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]); | ||
| 1746 | const next_index = self.handle.index + linux_entry.d_reclen; | ||
| 1747 | self.handle.index = next_index; | ||
| 1748 | |||
| 1749 | const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); | ||
| 1750 | |||
| 1751 | // skip . and .. entries | ||
| 1752 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 1753 | continue :start_over; | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | const entry_kind = switch (linux_entry.d_type) { | ||
| 1757 | posix.DT_BLK => Entry.Kind.BlockDevice, | ||
| 1758 | posix.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 1759 | posix.DT_DIR => Entry.Kind.Directory, | ||
| 1760 | posix.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 1761 | posix.DT_LNK => Entry.Kind.SymLink, | ||
| 1762 | posix.DT_REG => Entry.Kind.File, | ||
| 1763 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 1764 | else => Entry.Kind.Unknown, | ||
| 1765 | }; | ||
| 1766 | return Entry{ | ||
| 1767 | .name = name, | ||
| 1768 | .kind = entry_kind, | ||
| 1769 | }; | ||
| 1770 | } | ||
| 1771 | } | ||
| 1772 | |||
| 1773 | fn nextFreebsd(self: *Dir) !?Entry { | ||
| 1774 | start_over: while (true) { | ||
| 1775 | if (self.handle.index >= self.handle.end_index) { | ||
| 1776 | if (self.handle.buf.len == 0) { | ||
| 1777 | self.handle.buf = try self.allocator.alloc(u8, page_size); | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | while (true) { | ||
| 1781 | const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); | ||
| 1782 | const err = posix.getErrno(result); | ||
| 1783 | if (err > 0) { | ||
| 1784 | switch (err) { | ||
| 1785 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | ||
| 1786 | posix.EINVAL => { | ||
| 1787 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | ||
| 1788 | continue; | ||
| 1789 | }, | ||
| 1790 | else => return unexpectedErrorPosix(err), | ||
| 1791 | } | ||
| 1792 | } | ||
| 1793 | if (result == 0) return null; | ||
| 1794 | self.handle.index = 0; | ||
| 1795 | self.handle.end_index = result; | ||
| 1796 | break; | ||
| 1797 | } | ||
| 1798 | } | ||
| 1799 | const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); | ||
| 1800 | const next_index = self.handle.index + freebsd_entry.d_reclen; | ||
| 1801 | self.handle.index = next_index; | ||
| 1802 | |||
| 1803 | const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; | ||
| 1804 | |||
| 1805 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 1806 | continue :start_over; | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | const entry_kind = switch (freebsd_entry.d_type) { | ||
| 1810 | posix.DT_BLK => Entry.Kind.BlockDevice, | ||
| 1811 | posix.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 1812 | posix.DT_DIR => Entry.Kind.Directory, | ||
| 1813 | posix.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 1814 | posix.DT_LNK => Entry.Kind.SymLink, | ||
| 1815 | posix.DT_REG => Entry.Kind.File, | ||
| 1816 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 1817 | posix.DT_WHT => Entry.Kind.Whiteout, | ||
| 1818 | else => Entry.Kind.Unknown, | ||
| 1819 | }; | ||
| 1820 | return Entry{ | ||
| 1821 | .name = name, | ||
| 1822 | .kind = entry_kind, | ||
| 1823 | }; | ||
| 1824 | } | ||
| 1825 | } | ||
| 1826 | }; | ||
| 1827 | |||
| 1828 | pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { | ||
| 1829 | const path_buf = try allocator.alloc(u8, dir_path.len + 1); | ||
| 1830 | defer allocator.free(path_buf); | ||
| 1831 | |||
| 1832 | mem.copy(u8, path_buf, dir_path); | ||
| 1833 | path_buf[dir_path.len] = 0; | ||
| 1834 | |||
| 1835 | const err = posix.getErrno(posix.chdir(path_buf.ptr)); | ||
| 1836 | if (err > 0) { | ||
| 1837 | return switch (err) { | ||
| 1838 | posix.EACCES => error.AccessDenied, | ||
| 1839 | posix.EFAULT => unreachable, | ||
| 1840 | posix.EIO => error.FileSystem, | ||
| 1841 | posix.ELOOP => error.SymLinkLoop, | ||
| 1842 | posix.ENAMETOOLONG => error.NameTooLong, | ||
| 1843 | posix.ENOENT => error.FileNotFound, | ||
| 1844 | posix.ENOMEM => error.SystemResources, | ||
| 1845 | posix.ENOTDIR => error.NotDir, | ||
| 1846 | else => unexpectedErrorPosix(err), | ||
| 1847 | }; | ||
| 1848 | } | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | /// Read value of a symbolic link. | ||
| 1852 | /// The return value is a slice of out_buffer. | ||
| 1853 | pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { | ||
| 1854 | const rc = posix.readlink(pathname, out_buffer, out_buffer.len); | ||
| 1855 | const err = posix.getErrno(rc); | ||
| 1856 | switch (err) { | ||
| 1857 | 0 => return out_buffer[0..rc], | ||
| 1858 | posix.EACCES => return error.AccessDenied, | ||
| 1859 | posix.EFAULT => unreachable, | ||
| 1860 | posix.EINVAL => unreachable, | ||
| 1861 | posix.EIO => return error.FileSystem, | ||
| 1862 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1863 | posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX | ||
| 1864 | posix.ENOENT => return error.FileNotFound, | ||
| 1865 | posix.ENOMEM => return error.SystemResources, | ||
| 1866 | posix.ENOTDIR => return error.NotDir, | ||
| 1867 | else => return unexpectedErrorPosix(err), | ||
| 1868 | } | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | /// Read value of a symbolic link. | ||
| 1872 | /// The return value is a slice of out_buffer. | ||
| 1873 | pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { | ||
| 1874 | const file_path_c = try toPosixPath(file_path); | ||
| 1875 | return readLinkC(out_buffer, &file_path_c); | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | pub fn posix_setuid(uid: u32) !void { | ||
| 1879 | const err = posix.getErrno(posix.setuid(uid)); | ||
| 1880 | if (err == 0) return; | ||
| 1881 | return switch (err) { | ||
| 1882 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1883 | posix.EINVAL => error.InvalidUserId, | ||
| 1884 | posix.EPERM => error.PermissionDenied, | ||
| 1885 | else => unexpectedErrorPosix(err), | ||
| 1886 | }; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | pub fn posix_setreuid(ruid: u32, euid: u32) !void { | ||
| 1890 | const err = posix.getErrno(posix.setreuid(ruid, euid)); | ||
| 1891 | if (err == 0) return; | ||
| 1892 | return switch (err) { | ||
| 1893 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1894 | posix.EINVAL => error.InvalidUserId, | ||
| 1895 | posix.EPERM => error.PermissionDenied, | ||
| 1896 | else => unexpectedErrorPosix(err), | ||
| 1897 | }; | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | pub fn posix_setgid(gid: u32) !void { | ||
| 1901 | const err = posix.getErrno(posix.setgid(gid)); | ||
| 1902 | if (err == 0) return; | ||
| 1903 | return switch (err) { | ||
| 1904 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1905 | posix.EINVAL => error.InvalidUserId, | ||
| 1906 | posix.EPERM => error.PermissionDenied, | ||
| 1907 | else => unexpectedErrorPosix(err), | ||
| 1908 | }; | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | pub fn posix_setregid(rgid: u32, egid: u32) !void { | ||
| 1912 | const err = posix.getErrno(posix.setregid(rgid, egid)); | ||
| 1913 | if (err == 0) return; | ||
| 1914 | return switch (err) { | ||
| 1915 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1916 | posix.EINVAL => error.InvalidUserId, | ||
| 1917 | posix.EPERM => error.PermissionDenied, | ||
| 1918 | else => unexpectedErrorPosix(err), | ||
| 1919 | }; | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | pub const WindowsGetStdHandleErrs = error{ | ||
| 1923 | NoStdHandles, | ||
| 1924 | |||
| 1925 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1926 | Unexpected, | ||
| 1927 | }; | ||
| 1928 | |||
| 1929 | pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { | ||
| 1930 | if (windows.GetStdHandle(handle_id)) |handle| { | ||
| 1931 | if (handle == windows.INVALID_HANDLE_VALUE) { | ||
| 1932 | const err = windows.GetLastError(); | ||
| 1933 | return switch (err) { | ||
| 1934 | else => os.unexpectedErrorWindows(err), | ||
| 1935 | }; | ||
| 1936 | } | ||
| 1937 | return handle; | ||
| 1938 | } else { | ||
| 1939 | return error.NoStdHandles; | ||
| 1940 | } | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | pub const ArgIteratorPosix = struct { | ||
| 1944 | index: usize, | ||
| 1945 | count: usize, | ||
| 1946 | |||
| 1947 | pub fn init() ArgIteratorPosix { | ||
| 1948 | return ArgIteratorPosix{ | ||
| 1949 | .index = 0, | ||
| 1950 | .count = raw.len, | ||
| 1951 | }; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | pub fn next(self: *ArgIteratorPosix) ?[]const u8 { | ||
| 1955 | if (self.index == self.count) return null; | ||
| 1956 | |||
| 1957 | const s = raw[self.index]; | ||
| 1958 | self.index += 1; | ||
| 1959 | return cstr.toSlice(s); | ||
| 1960 | } | ||
| 1961 | |||
| 1962 | pub fn skip(self: *ArgIteratorPosix) bool { | ||
| 1963 | if (self.index == self.count) return false; | ||
| 1964 | |||
| 1965 | self.index += 1; | ||
| 1966 | return true; | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | /// This is marked as public but actually it's only meant to be used | ||
| 1970 | /// internally by zig's startup code. | ||
| 1971 | pub var raw: [][*]u8 = undefined; | ||
| 1972 | }; | ||
| 1973 | |||
| 1974 | pub const ArgIteratorWindows = struct { | ||
| 1975 | index: usize, | ||
| 1976 | cmd_line: [*]const u8, | ||
| 1977 | in_quote: bool, | ||
| 1978 | quote_count: usize, | ||
| 1979 | seen_quote_count: usize, | ||
| 1980 | |||
| 1981 | pub const NextError = error{OutOfMemory}; | ||
| 1982 | |||
| 1983 | pub fn init() ArgIteratorWindows { | ||
| 1984 | return initWithCmdLine(windows.GetCommandLineA()); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { | ||
| 1988 | return ArgIteratorWindows{ | ||
| 1989 | .index = 0, | ||
| 1990 | .cmd_line = cmd_line, | ||
| 1991 | .in_quote = false, | ||
| 1992 | .quote_count = countQuotes(cmd_line), | ||
| 1993 | .seen_quote_count = 0, | ||
| 1994 | }; | ||
| 1995 | } | ||
| 1996 | |||
| 1997 | /// You must free the returned memory when done. | ||
| 1998 | pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { | ||
| 1999 | // march forward over whitespace | ||
| 2000 | while (true) : (self.index += 1) { | ||
| 2001 | const byte = self.cmd_line[self.index]; | ||
| 2002 | switch (byte) { | ||
| 2003 | 0 => return null, | ||
| 2004 | ' ', '\t' => continue, | ||
| 2005 | else => break, | ||
| 2006 | } | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | return self.internalNext(allocator); | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | pub fn skip(self: *ArgIteratorWindows) bool { | ||
| 2013 | // march forward over whitespace | ||
| 2014 | while (true) : (self.index += 1) { | ||
| 2015 | const byte = self.cmd_line[self.index]; | ||
| 2016 | switch (byte) { | ||
| 2017 | 0 => return false, | ||
| 2018 | ' ', '\t' => continue, | ||
| 2019 | else => break, | ||
| 2020 | } | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | var backslash_count: usize = 0; | ||
| 2024 | while (true) : (self.index += 1) { | ||
| 2025 | const byte = self.cmd_line[self.index]; | ||
| 2026 | switch (byte) { | ||
| 2027 | 0 => return true, | ||
| 2028 | '"' => { | ||
| 2029 | const quote_is_real = backslash_count % 2 == 0; | ||
| 2030 | if (quote_is_real) { | ||
| 2031 | self.seen_quote_count += 1; | ||
| 2032 | } | ||
| 2033 | }, | ||
| 2034 | '\\' => { | ||
| 2035 | backslash_count += 1; | ||
| 2036 | }, | ||
| 2037 | ' ', '\t' => { | ||
| 2038 | if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { | ||
| 2039 | return true; | ||
| 2040 | } | ||
| 2041 | backslash_count = 0; | ||
| 2042 | }, | ||
| 2043 | else => { | ||
| 2044 | backslash_count = 0; | ||
| 2045 | continue; | ||
| 2046 | }, | ||
| 2047 | } | ||
| 2048 | } | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { | ||
| 2052 | var buf = try Buffer.initSize(allocator, 0); | ||
| 2053 | defer buf.deinit(); | ||
| 2054 | |||
| 2055 | var backslash_count: usize = 0; | ||
| 2056 | while (true) : (self.index += 1) { | ||
| 2057 | const byte = self.cmd_line[self.index]; | ||
| 2058 | switch (byte) { | ||
| 2059 | 0 => return buf.toOwnedSlice(), | ||
| 2060 | '"' => { | ||
| 2061 | const quote_is_real = backslash_count % 2 == 0; | ||
| 2062 | try self.emitBackslashes(&buf, backslash_count / 2); | ||
| 2063 | backslash_count = 0; | ||
| 2064 | |||
| 2065 | if (quote_is_real) { | ||
| 2066 | self.seen_quote_count += 1; | ||
| 2067 | if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) { | ||
| 2068 | try buf.appendByte('"'); | ||
| 2069 | } | ||
| 2070 | } else { | ||
| 2071 | try buf.appendByte('"'); | ||
| 2072 | } | ||
| 2073 | }, | ||
| 2074 | '\\' => { | ||
| 2075 | backslash_count += 1; | ||
| 2076 | }, | ||
| 2077 | ' ', '\t' => { | ||
| 2078 | try self.emitBackslashes(&buf, backslash_count); | ||
| 2079 | backslash_count = 0; | ||
| 2080 | if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { | ||
| 2081 | try buf.appendByte(byte); | ||
| 2082 | } else { | ||
| 2083 | return buf.toOwnedSlice(); | ||
| 2084 | } | ||
| 2085 | }, | ||
| 2086 | else => { | ||
| 2087 | try self.emitBackslashes(&buf, backslash_count); | ||
| 2088 | backslash_count = 0; | ||
| 2089 | try buf.appendByte(byte); | ||
| 2090 | }, | ||
| 2091 | } | ||
| 2092 | } | ||
| 2093 | } | ||
| 2094 | |||
| 2095 | fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { | ||
| 2096 | var i: usize = 0; | ||
| 2097 | while (i < emit_count) : (i += 1) { | ||
| 2098 | try buf.appendByte('\\'); | ||
| 2099 | } | ||
| 2100 | } | ||
| 2101 | |||
| 2102 | fn countQuotes(cmd_line: [*]const u8) usize { | ||
| 2103 | var result: usize = 0; | ||
| 2104 | var backslash_count: usize = 0; | ||
| 2105 | var index: usize = 0; | ||
| 2106 | while (true) : (index += 1) { | ||
| 2107 | const byte = cmd_line[index]; | ||
| 2108 | switch (byte) { | ||
| 2109 | 0 => return result, | ||
| 2110 | '\\' => backslash_count += 1, | ||
| 2111 | '"' => { | ||
| 2112 | result += 1 - (backslash_count % 2); | ||
| 2113 | backslash_count = 0; | ||
| 2114 | }, | ||
| 2115 | else => { | ||
| 2116 | backslash_count = 0; | ||
| 2117 | }, | ||
| 2118 | } | ||
| 2119 | } | ||
| 2120 | } | ||
| 2121 | }; | ||
| 2122 | |||
| 2123 | pub const ArgIterator = struct { | ||
| 2124 | const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix; | ||
| 2125 | |||
| 2126 | inner: InnerType, | ||
| 2127 | |||
| 2128 | pub fn init() ArgIterator { | ||
| 2129 | return ArgIterator{ .inner = InnerType.init() }; | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | pub const NextError = ArgIteratorWindows.NextError; | ||
| 2133 | |||
| 2134 | /// You must free the returned memory when done. | ||
| 2135 | pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { | ||
| 2136 | if (builtin.os == Os.windows) { | ||
| 2137 | return self.inner.next(allocator); | ||
| 2138 | } else { | ||
| 2139 | return mem.dupe(allocator, u8, self.inner.next() orelse return null); | ||
| 2140 | } | ||
| 2141 | } | ||
| 2142 | |||
| 2143 | /// If you only are targeting posix you can call this and not need an allocator. | ||
| 2144 | pub fn nextPosix(self: *ArgIterator) ?[]const u8 { | ||
| 2145 | return self.inner.next(); | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | /// Parse past 1 argument without capturing it. | ||
| 2149 | /// Returns `true` if skipped an arg, `false` if we are at the end. | ||
| 2150 | pub fn skip(self: *ArgIterator) bool { | ||
| 2151 | return self.inner.skip(); | ||
| 2152 | } | ||
| 2153 | }; | ||
| 2154 | |||
| 2155 | pub fn args() ArgIterator { | ||
| 2156 | return ArgIterator.init(); | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | /// Caller must call argsFree on result. | ||
| 2160 | pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { | ||
| 2161 | // TODO refactor to only make 1 allocation. | ||
| 2162 | var it = args(); | ||
| 2163 | var contents = try Buffer.initSize(allocator, 0); | ||
| 2164 | defer contents.deinit(); | ||
| 2165 | |||
| 2166 | var slice_list = ArrayList(usize).init(allocator); | ||
| 2167 | defer slice_list.deinit(); | ||
| 2168 | |||
| 2169 | while (it.next(allocator)) |arg_or_err| { | ||
| 2170 | const arg = try arg_or_err; | ||
| 2171 | defer allocator.free(arg); | ||
| 2172 | try contents.append(arg); | ||
| 2173 | try slice_list.append(arg.len); | ||
| 2174 | } | ||
| 2175 | |||
| 2176 | const contents_slice = contents.toSliceConst(); | ||
| 2177 | const slice_sizes = slice_list.toSliceConst(); | ||
| 2178 | const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); | ||
| 2179 | const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); | ||
| 2180 | const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); | ||
| 2181 | errdefer allocator.free(buf); | ||
| 2182 | |||
| 2183 | const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); | ||
| 2184 | const result_contents = buf[slice_list_bytes..]; | ||
| 2185 | mem.copy(u8, result_contents, contents_slice); | ||
| 2186 | |||
| 2187 | var contents_index: usize = 0; | ||
| 2188 | for (slice_sizes) |len, i| { | ||
| 2189 | const new_index = contents_index + len; | ||
| 2190 | result_slice_list[i] = result_contents[contents_index..new_index]; | ||
| 2191 | contents_index = new_index; | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | return result_slice_list; | ||
| 2195 | } | ||
| 2196 | |||
| 2197 | pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { | ||
| 2198 | var total_bytes: usize = 0; | ||
| 2199 | for (args_alloc) |arg| { | ||
| 2200 | total_bytes += @sizeOf([]u8) + arg.len; | ||
| 2201 | } | ||
| 2202 | const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; | ||
| 2203 | const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); | ||
| 2204 | return allocator.free(aligned_allocated_buf); | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | test "windows arg parsing" { | ||
| 2208 | testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); | ||
| 2209 | testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); | ||
| 2210 | testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); | ||
| 2211 | testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); | ||
| 2212 | testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); | ||
| 2213 | testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); | ||
| 2214 | |||
| 2215 | testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ | ||
| 2216 | ".\\..\\zig-cache\\build", | ||
| 2217 | "bin\\zig.exe", | ||
| 2218 | ".\\..", | ||
| 2219 | ".\\..\\zig-cache", | ||
| 2220 | "--help", | ||
| 2221 | }); | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { | ||
| 2225 | var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); | ||
| 2226 | for (expected_args) |expected_arg| { | ||
| 2227 | const arg = it.next(debug.global_allocator).? catch unreachable; | ||
| 2228 | testing.expectEqualSlices(u8, expected_arg, arg); | ||
| 2229 | } | ||
| 2230 | testing.expect(it.next(debug.global_allocator) == null); | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | // TODO make this a build variable that you can set | ||
| 2234 | const unexpected_error_tracing = false; | ||
| 2235 | const UnexpectedError = error{ | ||
| 2236 | /// The Operating System returned an undocumented error code. | ||
| 2237 | Unexpected, | ||
| 2238 | }; | ||
| 2239 | |||
| 2240 | /// Call this when you made a syscall or something that sets errno | ||
| 2241 | /// and you get an unexpected error. | ||
| 2242 | pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { | ||
| 2243 | if (unexpected_error_tracing) { | ||
| 2244 | debug.warn("unexpected errno: {}\n", errno); | ||
| 2245 | debug.dumpCurrentStackTrace(null); | ||
| 2246 | } | ||
| 2247 | return error.Unexpected; | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | /// Call this when you made a windows DLL call or something that does SetLastError | ||
| 2251 | /// and you get an unexpected error. | ||
| 2252 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { | ||
| 2253 | if (unexpected_error_tracing) { | ||
| 2254 | debug.warn("unexpected GetLastError(): {}\n", err); | ||
| 2255 | @breakpoint(); | ||
| 2256 | debug.dumpCurrentStackTrace(null); | ||
| 2257 | } | ||
| 2258 | return error.Unexpected; | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | pub fn openSelfExe() !os.File { | ||
| 2262 | switch (builtin.os) { | ||
| 2263 | Os.linux => return os.File.openReadC(c"/proc/self/exe"), | ||
| 2264 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 2265 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 2266 | const self_exe_path = try selfExePath(&buf); | ||
| 2267 | buf[self_exe_path.len] = 0; | ||
| 2268 | return os.File.openReadC(self_exe_path.ptr); | ||
| 2269 | }, | ||
| 2270 | Os.windows => { | ||
| 2271 | var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 2272 | const wide_slice = try selfExePathW(&buf); | ||
| 2273 | return os.File.openReadW(wide_slice.ptr); | ||
| 2274 | }, | ||
| 2275 | else => @compileError("Unsupported OS"), | ||
| 2276 | } | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | test "openSelfExe" { | ||
| 2280 | switch (builtin.os) { | ||
| 2281 | Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(), | ||
| 2282 | else => return error.SkipZigTest, // Unsupported OS. | ||
| 2283 | } | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { | ||
| 2287 | const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast | ||
| 2288 | const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); | ||
| 2289 | assert(rc <= out_buffer.len); | ||
| 2290 | if (rc == 0) { | ||
| 2291 | const err = windows.GetLastError(); | ||
| 2292 | switch (err) { | ||
| 2293 | else => return unexpectedErrorWindows(err), | ||
| 2294 | } | ||
| 2295 | } | ||
| 2296 | return out_buffer[0..rc]; | ||
| 2297 | } | ||
| 2298 | |||
| 2299 | /// Get the path to the current executable. | ||
| 2300 | /// If you only need the directory, use selfExeDirPath. | ||
| 2301 | /// If you only want an open file handle, use openSelfExe. | ||
| 2302 | /// This function may return an error if the current executable | ||
| 2303 | /// was deleted after spawning. | ||
| 2304 | /// Returned value is a slice of out_buffer. | ||
| 2305 | /// | ||
| 2306 | /// On Linux, depends on procfs being mounted. If the currently executing binary has | ||
| 2307 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. | ||
| 2308 | /// TODO make the return type of this a null terminated pointer | ||
| 2309 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ||
| 2310 | switch (builtin.os) { | ||
| 2311 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), | ||
| 2312 | Os.freebsd => { | ||
| 2313 | var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; | ||
| 2314 | var out_len: usize = out_buffer.len; | ||
| 2315 | const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); | ||
| 2316 | |||
| 2317 | if (err == 0) return mem.toSlice(u8, out_buffer); | ||
| 2318 | |||
| 2319 | return switch (err) { | ||
| 2320 | posix.EFAULT => error.BadAdress, | ||
| 2321 | posix.EPERM => error.PermissionDenied, | ||
| 2322 | else => unexpectedErrorPosix(err), | ||
| 2323 | }; | ||
| 2324 | }, | ||
| 2325 | Os.netbsd => { | ||
| 2326 | var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME }; | ||
| 2327 | var out_len: usize = out_buffer.len; | ||
| 2328 | const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); | ||
| 2329 | |||
| 2330 | if (err == 0) return mem.toSlice(u8, out_buffer); | ||
| 2331 | |||
| 2332 | return switch (err) { | ||
| 2333 | posix.EFAULT => error.BadAdress, | ||
| 2334 | posix.EPERM => error.PermissionDenied, | ||
| 2335 | else => unexpectedErrorPosix(err), | ||
| 2336 | }; | ||
| 2337 | }, | ||
| 2338 | Os.windows => { | ||
| 2339 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 2340 | const utf16le_slice = try selfExePathW(&utf16le_buf); | ||
| 2341 | // Trust that Windows gives us valid UTF-16LE. | ||
| 2342 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | ||
| 2343 | return out_buffer[0..end_index]; | ||
| 2344 | }, | ||
| 2345 | Os.macosx, Os.ios => { | ||
| 2346 | var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast | ||
| 2347 | const rc = c._NSGetExecutablePath(out_buffer, &u32_len); | ||
| 2348 | if (rc != 0) return error.NameTooLong; | ||
| 2349 | return mem.toSlice(u8, out_buffer); | ||
| 2350 | }, | ||
| 2351 | else => @compileError("Unsupported OS"), | ||
| 2352 | } | ||
| 2353 | } | ||
| 2354 | |||
| 2355 | /// `selfExeDirPath` except allocates the result on the heap. | ||
| 2356 | /// Caller owns returned memory. | ||
| 2357 | pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { | ||
| 2358 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 2359 | return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | /// Get the directory path that contains the current executable. | ||
| 2363 | /// Returned value is a slice of out_buffer. | ||
| 2364 | pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { | ||
| 2365 | switch (builtin.os) { | ||
| 2366 | Os.linux => { | ||
| 2367 | // If the currently executing binary has been deleted, | ||
| 2368 | // the file path looks something like `/a/b/c/exe (deleted)` | ||
| 2369 | // This path cannot be opened, but it's valid for determining the directory | ||
| 2370 | // the executable was in when it was run. | ||
| 2371 | const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe"); | ||
| 2372 | // Assume that /proc/self/exe has an absolute path, and therefore dirname | ||
| 2373 | // will not return null. | ||
| 2374 | return path.dirname(full_exe_path).?; | ||
| 2375 | }, | ||
| 2376 | Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 2377 | const self_exe_path = try selfExePath(out_buffer); | ||
| 2378 | // Assume that the OS APIs return absolute paths, and therefore dirname | ||
| 2379 | // will not return null. | ||
| 2380 | return path.dirname(self_exe_path).?; | ||
| 2381 | }, | ||
| 2382 | else => @compileError("Unsupported OS"), | ||
| 2383 | } | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | pub fn isTty(handle: FileHandle) bool { | ||
| 2387 | if (is_windows) { | ||
| 2388 | return windows_util.windowsIsTty(handle); | ||
| 2389 | } else { | ||
| 2390 | if (builtin.link_libc) { | ||
| 2391 | return c.isatty(handle) != 0; | ||
| 2392 | } else { | ||
| 2393 | return posix.isatty(handle); | ||
| 2394 | } | ||
| 2395 | } | ||
| 2396 | } | ||
| 2397 | |||
| 2398 | pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { | ||
| 2399 | if (is_windows) { | ||
| 2400 | return windows_util.windowsIsCygwinPty(handle); | ||
| 2401 | } else { | ||
| 2402 | if (builtin.link_libc) { | ||
| 2403 | return c.isatty(handle) != 0; | ||
| 2404 | } else { | ||
| 2405 | return posix.isatty(handle); | ||
| 2406 | } | ||
| 2407 | } | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | pub const PosixSocketError = error{ | ||
| 2411 | /// Permission to create a socket of the specified type and/or | ||
| 2412 | /// pro‐tocol is denied. | ||
| 2413 | PermissionDenied, | ||
| 2414 | |||
| 2415 | /// The implementation does not support the specified address family. | ||
| 2416 | AddressFamilyNotSupported, | ||
| 2417 | |||
| 2418 | /// Unknown protocol, or protocol family not available. | ||
| 2419 | ProtocolFamilyNotAvailable, | ||
| 2420 | |||
| 2421 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 2422 | ProcessFdQuotaExceeded, | ||
| 2423 | |||
| 2424 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2425 | SystemFdQuotaExceeded, | ||
| 2426 | |||
| 2427 | /// Insufficient memory is available. The socket cannot be created until sufficient | ||
| 2428 | /// resources are freed. | ||
| 2429 | SystemResources, | ||
| 2430 | |||
| 2431 | /// The protocol type or the specified protocol is not supported within this domain. | ||
| 2432 | ProtocolNotSupported, | ||
| 2433 | }; | ||
| 2434 | |||
| 2435 | pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { | ||
| 2436 | const rc = posix.socket(domain, socket_type, protocol); | ||
| 2437 | const err = posix.getErrno(rc); | ||
| 2438 | switch (err) { | ||
| 2439 | 0 => return @intCast(i32, rc), | ||
| 2440 | posix.EACCES => return PosixSocketError.PermissionDenied, | ||
| 2441 | posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, | ||
| 2442 | posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, | ||
| 2443 | posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, | ||
| 2444 | posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, | ||
| 2445 | posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, | ||
| 2446 | posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, | ||
| 2447 | else => return unexpectedErrorPosix(err), | ||
| 2448 | } | ||
| 2449 | } | ||
| 2450 | |||
| 2451 | pub const PosixBindError = error{ | ||
| 2452 | /// The address is protected, and the user is not the superuser. | ||
| 2453 | /// For UNIX domain sockets: Search permission is denied on a component | ||
| 2454 | /// of the path prefix. | ||
| 2455 | AccessDenied, | ||
| 2456 | |||
| 2457 | /// The given address is already in use, or in the case of Internet domain sockets, | ||
| 2458 | /// The port number was specified as zero in the socket | ||
| 2459 | /// address structure, but, upon attempting to bind to an ephemeral port, it was | ||
| 2460 | /// determined that all port numbers in the ephemeral port range are currently in | ||
| 2461 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). | ||
| 2462 | AddressInUse, | ||
| 2463 | |||
| 2464 | /// A nonexistent interface was requested or the requested address was not local. | ||
| 2465 | AddressNotAvailable, | ||
| 2466 | |||
| 2467 | /// Too many symbolic links were encountered in resolving addr. | ||
| 2468 | SymLinkLoop, | ||
| 2469 | |||
| 2470 | /// addr is too long. | ||
| 2471 | NameTooLong, | ||
| 2472 | |||
| 2473 | /// A component in the directory prefix of the socket pathname does not exist. | ||
| 2474 | FileNotFound, | ||
| 2475 | |||
| 2476 | /// Insufficient kernel memory was available. | ||
| 2477 | SystemResources, | ||
| 2478 | |||
| 2479 | /// A component of the path prefix is not a directory. | ||
| 2480 | NotDir, | ||
| 2481 | |||
| 2482 | /// The socket inode would reside on a read-only filesystem. | ||
| 2483 | ReadOnlyFileSystem, | ||
| 2484 | |||
| 2485 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2486 | Unexpected, | ||
| 2487 | }; | ||
| 2488 | |||
| 2489 | /// addr is `&const T` where T is one of the sockaddr | ||
| 2490 | pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { | ||
| 2491 | const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); | ||
| 2492 | const err = posix.getErrno(rc); | ||
| 2493 | switch (err) { | ||
| 2494 | 0 => return, | ||
| 2495 | posix.EACCES => return PosixBindError.AccessDenied, | ||
| 2496 | posix.EADDRINUSE => return PosixBindError.AddressInUse, | ||
| 2497 | posix.EBADF => unreachable, // always a race condition if this error is returned | ||
| 2498 | posix.EINVAL => unreachable, | ||
| 2499 | posix.ENOTSOCK => unreachable, | ||
| 2500 | posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, | ||
| 2501 | posix.EFAULT => unreachable, | ||
| 2502 | posix.ELOOP => return PosixBindError.SymLinkLoop, | ||
| 2503 | posix.ENAMETOOLONG => return PosixBindError.NameTooLong, | ||
| 2504 | posix.ENOENT => return PosixBindError.FileNotFound, | ||
| 2505 | posix.ENOMEM => return PosixBindError.SystemResources, | ||
| 2506 | posix.ENOTDIR => return PosixBindError.NotDir, | ||
| 2507 | posix.EROFS => return PosixBindError.ReadOnlyFileSystem, | ||
| 2508 | else => return unexpectedErrorPosix(err), | ||
| 2509 | } | ||
| 2510 | } | ||
| 2511 | |||
| 2512 | const PosixListenError = error{ | ||
| 2513 | /// Another socket is already listening on the same port. | ||
| 2514 | /// For Internet domain sockets, the socket referred to by sockfd had not previously | ||
| 2515 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it | ||
| 2516 | /// was determined that all port numbers in the ephemeral port range are currently in | ||
| 2517 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2518 | AddressInUse, | ||
| 2519 | |||
| 2520 | /// The file descriptor sockfd does not refer to a socket. | ||
| 2521 | FileDescriptorNotASocket, | ||
| 2522 | |||
| 2523 | /// The socket is not of a type that supports the listen() operation. | ||
| 2524 | OperationNotSupported, | ||
| 2525 | |||
| 2526 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2527 | Unexpected, | ||
| 2528 | }; | ||
| 2529 | |||
| 2530 | pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { | ||
| 2531 | const rc = posix.listen(sockfd, backlog); | ||
| 2532 | const err = posix.getErrno(rc); | ||
| 2533 | switch (err) { | ||
| 2534 | 0 => return, | ||
| 2535 | posix.EADDRINUSE => return PosixListenError.AddressInUse, | ||
| 2536 | posix.EBADF => unreachable, | ||
| 2537 | posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, | ||
| 2538 | posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, | ||
| 2539 | else => return unexpectedErrorPosix(err), | ||
| 2540 | } | ||
| 2541 | } | ||
| 2542 | |||
| 2543 | pub const PosixAcceptError = error{ | ||
| 2544 | ConnectionAborted, | ||
| 2545 | |||
| 2546 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 2547 | ProcessFdQuotaExceeded, | ||
| 2548 | |||
| 2549 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2550 | SystemFdQuotaExceeded, | ||
| 2551 | |||
| 2552 | /// Not enough free memory. This often means that the memory allocation is limited | ||
| 2553 | /// by the socket buffer limits, not by the system memory. | ||
| 2554 | SystemResources, | ||
| 2555 | |||
| 2556 | /// The file descriptor sockfd does not refer to a socket. | ||
| 2557 | FileDescriptorNotASocket, | ||
| 2558 | |||
| 2559 | /// The referenced socket is not of type SOCK_STREAM. | ||
| 2560 | OperationNotSupported, | ||
| 2561 | |||
| 2562 | ProtocolFailure, | ||
| 2563 | |||
| 2564 | /// Firewall rules forbid connection. | ||
| 2565 | BlockedByFirewall, | ||
| 2566 | |||
| 2567 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2568 | Unexpected, | ||
| 2569 | }; | ||
| 2570 | |||
| 2571 | pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { | ||
| 2572 | while (true) { | ||
| 2573 | var sockaddr_size = u32(@sizeOf(posix.sockaddr)); | ||
| 2574 | const rc = posix.accept4(fd, addr, &sockaddr_size, flags); | ||
| 2575 | const err = posix.getErrno(rc); | ||
| 2576 | switch (err) { | ||
| 2577 | 0 => return @intCast(i32, rc), | ||
| 2578 | posix.EINTR => continue, | ||
| 2579 | else => return unexpectedErrorPosix(err), | ||
| 2580 | |||
| 2581 | posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking | ||
| 2582 | posix.EBADF => unreachable, // always a race condition | ||
| 2583 | posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, | ||
| 2584 | posix.EFAULT => unreachable, | ||
| 2585 | posix.EINVAL => unreachable, | ||
| 2586 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, | ||
| 2587 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, | ||
| 2588 | posix.ENOBUFS => return PosixAcceptError.SystemResources, | ||
| 2589 | posix.ENOMEM => return PosixAcceptError.SystemResources, | ||
| 2590 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, | ||
| 2591 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, | ||
| 2592 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, | ||
| 2593 | posix.EPERM => return PosixAcceptError.BlockedByFirewall, | ||
| 2594 | } | ||
| 2595 | } | ||
| 2596 | } | ||
| 2597 | |||
| 2598 | /// Returns -1 if would block. | ||
| 2599 | pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { | ||
| 2600 | while (true) { | ||
| 2601 | var sockaddr_size = u32(@sizeOf(posix.sockaddr)); | ||
| 2602 | const rc = posix.accept4(fd, addr, &sockaddr_size, flags); | ||
| 2603 | const err = posix.getErrno(rc); | ||
| 2604 | switch (err) { | ||
| 2605 | 0 => return @intCast(i32, rc), | ||
| 2606 | posix.EINTR => continue, | ||
| 2607 | else => return unexpectedErrorPosix(err), | ||
| 2608 | |||
| 2609 | posix.EAGAIN => return -1, | ||
| 2610 | posix.EBADF => unreachable, // always a race condition | ||
| 2611 | posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, | ||
| 2612 | posix.EFAULT => unreachable, | ||
| 2613 | posix.EINVAL => unreachable, | ||
| 2614 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, | ||
| 2615 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, | ||
| 2616 | posix.ENOBUFS => return PosixAcceptError.SystemResources, | ||
| 2617 | posix.ENOMEM => return PosixAcceptError.SystemResources, | ||
| 2618 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, | ||
| 2619 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, | ||
| 2620 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, | ||
| 2621 | posix.EPERM => return PosixAcceptError.BlockedByFirewall, | ||
| 2622 | } | ||
| 2623 | } | ||
| 2624 | } | ||
| 2625 | |||
| 2626 | pub const LinuxEpollCreateError = error{ | ||
| 2627 | /// The per-user limit on the number of epoll instances imposed by | ||
| 2628 | /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further | ||
| 2629 | /// details. | ||
| 2630 | /// Or, The per-process limit on the number of open file descriptors has been reached. | ||
| 2631 | ProcessFdQuotaExceeded, | ||
| 2632 | |||
| 2633 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2634 | SystemFdQuotaExceeded, | ||
| 2635 | |||
| 2636 | /// There was insufficient memory to create the kernel object. | ||
| 2637 | SystemResources, | ||
| 2638 | |||
| 2639 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2640 | Unexpected, | ||
| 2641 | }; | ||
| 2642 | |||
| 2643 | pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { | ||
| 2644 | const rc = posix.epoll_create1(flags); | ||
| 2645 | const err = posix.getErrno(rc); | ||
| 2646 | switch (err) { | ||
| 2647 | 0 => return @intCast(i32, rc), | ||
| 2648 | else => return unexpectedErrorPosix(err), | ||
| 2649 | |||
| 2650 | posix.EINVAL => unreachable, | ||
| 2651 | posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, | ||
| 2652 | posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, | ||
| 2653 | posix.ENOMEM => return LinuxEpollCreateError.SystemResources, | ||
| 2654 | } | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | pub const LinuxEpollCtlError = error{ | ||
| 2658 | /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered | ||
| 2659 | /// with this epoll instance. | ||
| 2660 | FileDescriptorAlreadyPresentInSet, | ||
| 2661 | |||
| 2662 | /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a | ||
| 2663 | /// circular loop of epoll instances monitoring one another. | ||
| 2664 | OperationCausesCircularLoop, | ||
| 2665 | |||
| 2666 | /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll | ||
| 2667 | /// instance. | ||
| 2668 | FileDescriptorNotRegistered, | ||
| 2669 | |||
| 2670 | /// There was insufficient memory to handle the requested op control operation. | ||
| 2671 | SystemResources, | ||
| 2672 | |||
| 2673 | /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while | ||
| 2674 | /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. | ||
| 2675 | /// See epoll(7) for further details. | ||
| 2676 | UserResourceLimitReached, | ||
| 2677 | |||
| 2678 | /// The target file fd does not support epoll. This error can occur if fd refers to, | ||
| 2679 | /// for example, a regular file or a directory. | ||
| 2680 | FileDescriptorIncompatibleWithEpoll, | ||
| 2681 | |||
| 2682 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2683 | Unexpected, | ||
| 2684 | }; | ||
| 2685 | |||
| 2686 | pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { | ||
| 2687 | const rc = posix.epoll_ctl(epfd, op, fd, event); | ||
| 2688 | const err = posix.getErrno(rc); | ||
| 2689 | switch (err) { | ||
| 2690 | 0 => return, | ||
| 2691 | else => return unexpectedErrorPosix(err), | ||
| 2692 | |||
| 2693 | posix.EBADF => unreachable, // always a race condition if this happens | ||
| 2694 | posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, | ||
| 2695 | posix.EINVAL => unreachable, | ||
| 2696 | posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, | ||
| 2697 | posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, | ||
| 2698 | posix.ENOMEM => return LinuxEpollCtlError.SystemResources, | ||
| 2699 | posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, | ||
| 2700 | posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, | ||
| 2701 | } | ||
| 2702 | } | ||
| 2703 | |||
| 2704 | pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { | ||
| 2705 | while (true) { | ||
| 2706 | const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); | ||
| 2707 | const err = posix.getErrno(rc); | ||
| 2708 | switch (err) { | ||
| 2709 | 0 => return rc, | ||
| 2710 | posix.EINTR => continue, | ||
| 2711 | posix.EBADF => unreachable, | ||
| 2712 | posix.EFAULT => unreachable, | ||
| 2713 | posix.EINVAL => unreachable, | ||
| 2714 | else => unreachable, | ||
| 2715 | } | ||
| 2716 | } | ||
| 2717 | } | ||
| 2718 | |||
| 2719 | pub const LinuxEventFdError = error{ | ||
| 2720 | InvalidFlagValue, | ||
| 2721 | SystemResources, | ||
| 2722 | ProcessFdQuotaExceeded, | ||
| 2723 | SystemFdQuotaExceeded, | ||
| 2724 | |||
| 2725 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2726 | Unexpected, | ||
| 2727 | }; | ||
| 2728 | |||
| 2729 | pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { | ||
| 2730 | const rc = posix.eventfd(initval, flags); | ||
| 2731 | const err = posix.getErrno(rc); | ||
| 2732 | switch (err) { | ||
| 2733 | 0 => return @intCast(i32, rc), | ||
| 2734 | else => return unexpectedErrorPosix(err), | ||
| 2735 | |||
| 2736 | posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, | ||
| 2737 | posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, | ||
| 2738 | posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, | ||
| 2739 | posix.ENODEV => return LinuxEventFdError.SystemResources, | ||
| 2740 | posix.ENOMEM => return LinuxEventFdError.SystemResources, | ||
| 2741 | } | ||
| 2742 | } | ||
| 2743 | |||
| 2744 | pub const PosixGetSockNameError = error{ | ||
| 2745 | /// Insufficient resources were available in the system to perform the operation. | ||
| 2746 | SystemResources, | ||
| 2747 | |||
| 2748 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2749 | Unexpected, | ||
| 2750 | }; | ||
| 2751 | |||
| 2752 | pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { | ||
| 2753 | var addr: posix.sockaddr = undefined; | ||
| 2754 | var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); | ||
| 2755 | const rc = posix.getsockname(sockfd, &addr, &addrlen); | ||
| 2756 | const err = posix.getErrno(rc); | ||
| 2757 | switch (err) { | ||
| 2758 | 0 => return addr, | ||
| 2759 | else => return unexpectedErrorPosix(err), | ||
| 2760 | |||
| 2761 | posix.EBADF => unreachable, | ||
| 2762 | posix.EFAULT => unreachable, | ||
| 2763 | posix.EINVAL => unreachable, | ||
| 2764 | posix.ENOTSOCK => unreachable, | ||
| 2765 | posix.ENOBUFS => return PosixGetSockNameError.SystemResources, | ||
| 2766 | } | ||
| 2767 | } | ||
| 2768 | |||
| 2769 | pub const PosixConnectError = error{ | ||
| 2770 | /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket | ||
| 2771 | /// file, or search permission is denied for one of the directories in the path prefix. | ||
| 2772 | /// or | ||
| 2773 | /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or | ||
| 2774 | /// the connection request failed because of a local firewall rule. | ||
| 2775 | PermissionDenied, | ||
| 2776 | |||
| 2777 | /// Local address is already in use. | ||
| 2778 | AddressInUse, | ||
| 2779 | |||
| 2780 | /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an | ||
| 2781 | /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers | ||
| 2782 | /// in the ephemeral port range are currently in use. See the discussion of | ||
| 2783 | /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2784 | AddressNotAvailable, | ||
| 2785 | |||
| 2786 | /// The passed address didn't have the correct address family in its sa_family field. | ||
| 2787 | AddressFamilyNotSupported, | ||
| 2788 | |||
| 2789 | /// Insufficient entries in the routing cache. | ||
| 2790 | SystemResources, | ||
| 2791 | |||
| 2792 | /// A connect() on a stream socket found no one listening on the remote address. | ||
| 2793 | ConnectionRefused, | ||
| 2794 | |||
| 2795 | /// Network is unreachable. | ||
| 2796 | NetworkUnreachable, | ||
| 2797 | |||
| 2798 | /// Timeout while attempting connection. The server may be too busy to accept new connections. Note | ||
| 2799 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. | ||
| 2800 | ConnectionTimedOut, | ||
| 2801 | |||
| 2802 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2803 | Unexpected, | ||
| 2804 | }; | ||
| 2805 | |||
| 2806 | pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { | ||
| 2807 | while (true) { | ||
| 2808 | const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); | ||
| 2809 | const err = posix.getErrno(rc); | ||
| 2810 | switch (err) { | ||
| 2811 | 0 => return, | ||
| 2812 | else => return unexpectedErrorPosix(err), | ||
| 2813 | |||
| 2814 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2815 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2816 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2817 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2818 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2819 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2820 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2821 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2822 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2823 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2824 | posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. | ||
| 2825 | posix.EINTR => continue, | ||
| 2826 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2827 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2828 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2829 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2830 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2831 | } | ||
| 2832 | } | ||
| 2833 | } | ||
| 2834 | |||
| 2835 | /// Same as posixConnect except it is for blocking socket file descriptors. | ||
| 2836 | /// It expects to receive EINPROGRESS. | ||
| 2837 | pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { | ||
| 2838 | while (true) { | ||
| 2839 | const rc = posix.connect(sockfd, sockaddr, len); | ||
| 2840 | const err = posix.getErrno(rc); | ||
| 2841 | switch (err) { | ||
| 2842 | 0, posix.EINPROGRESS => return, | ||
| 2843 | else => return unexpectedErrorPosix(err), | ||
| 2844 | |||
| 2845 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2846 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2847 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2848 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2849 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2850 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2851 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2852 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2853 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2854 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2855 | posix.EINTR => continue, | ||
| 2856 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2857 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2858 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2859 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2860 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2861 | } | ||
| 2862 | } | ||
| 2863 | } | ||
| 2864 | |||
| 2865 | pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { | ||
| 2866 | var err_code: i32 = undefined; | ||
| 2867 | var size: u32 = @sizeOf(i32); | ||
| 2868 | const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); | ||
| 2869 | assert(size == 4); | ||
| 2870 | const err = posix.getErrno(rc); | ||
| 2871 | switch (err) { | ||
| 2872 | 0 => switch (err_code) { | ||
| 2873 | 0 => return, | ||
| 2874 | else => return unexpectedErrorPosix(err), | ||
| 2875 | |||
| 2876 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2877 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2878 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2879 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2880 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2881 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2882 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2883 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2884 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2885 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2886 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2887 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2888 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2889 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2890 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2891 | }, | ||
| 2892 | else => return unexpectedErrorPosix(err), | ||
| 2893 | posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. | ||
| 2894 | posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. | ||
| 2895 | posix.EINVAL => unreachable, | ||
| 2896 | posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. | ||
| 2897 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2898 | } | ||
| 2899 | } | ||
| 2900 | |||
| 2901 | pub const Thread = struct { | ||
| 2902 | data: Data, | ||
| 2903 | |||
| 2904 | pub const use_pthreads = is_posix and builtin.link_libc; | ||
| 2905 | |||
| 2906 | /// Represents a kernel thread handle. | ||
| 2907 | /// May be an integer or a pointer depending on the platform. | ||
| 2908 | /// On Linux and POSIX, this is the same as Id. | ||
| 2909 | pub const Handle = if (use_pthreads) | ||
| 2910 | c.pthread_t | ||
| 2911 | else switch (builtin.os) { | ||
| 2912 | builtin.Os.linux => i32, | ||
| 2913 | builtin.Os.windows => windows.HANDLE, | ||
| 2914 | else => @compileError("Unsupported OS"), | ||
| 2915 | }; | ||
| 2916 | |||
| 2917 | /// Represents a unique ID per thread. | ||
| 2918 | /// May be an integer or pointer depending on the platform. | ||
| 2919 | /// On Linux and POSIX, this is the same as Handle. | ||
| 2920 | pub const Id = switch (builtin.os) { | ||
| 2921 | builtin.Os.windows => windows.DWORD, | ||
| 2922 | else => Handle, | ||
| 2923 | }; | ||
| 2924 | |||
| 2925 | pub const Data = if (use_pthreads) | ||
| 2926 | struct { | ||
| 2927 | handle: Thread.Handle, | ||
| 2928 | mmap_addr: usize, | ||
| 2929 | mmap_len: usize, | ||
| 2930 | } | ||
| 2931 | else switch (builtin.os) { | ||
| 2932 | builtin.Os.linux => struct { | ||
| 2933 | handle: Thread.Handle, | ||
| 2934 | mmap_addr: usize, | ||
| 2935 | mmap_len: usize, | ||
| 2936 | tls_end_addr: usize, | ||
| 2937 | }, | ||
| 2938 | builtin.Os.windows => struct { | ||
| 2939 | handle: Thread.Handle, | ||
| 2940 | alloc_start: *c_void, | ||
| 2941 | heap_handle: windows.HANDLE, | ||
| 2942 | }, | ||
| 2943 | else => @compileError("Unsupported OS"), | ||
| 2944 | }; | ||
| 2945 | |||
| 2946 | /// Returns the ID of the calling thread. | ||
| 2947 | /// Makes a syscall every time the function is called. | ||
| 2948 | /// On Linux and POSIX, this Id is the same as a Handle. | ||
| 2949 | pub fn getCurrentId() Id { | ||
| 2950 | if (use_pthreads) { | ||
| 2951 | return c.pthread_self(); | ||
| 2952 | } else | ||
| 2953 | return switch (builtin.os) { | ||
| 2954 | builtin.Os.linux => linux.gettid(), | ||
| 2955 | builtin.Os.windows => windows.GetCurrentThreadId(), | ||
| 2956 | else => @compileError("Unsupported OS"), | ||
| 2957 | }; | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | /// Returns the handle of this thread. | ||
| 2961 | /// On Linux and POSIX, this is the same as Id. | ||
| 2962 | pub fn handle(self: Thread) Handle { | ||
| 2963 | return self.data.handle; | ||
| 2964 | } | ||
| 2965 | |||
| 2966 | pub fn wait(self: *const Thread) void { | ||
| 2967 | if (use_pthreads) { | ||
| 2968 | const err = c.pthread_join(self.data.handle, null); | ||
| 2969 | switch (err) { | ||
| 2970 | 0 => {}, | ||
| 2971 | posix.EINVAL => unreachable, | ||
| 2972 | posix.ESRCH => unreachable, | ||
| 2973 | posix.EDEADLK => unreachable, | ||
| 2974 | else => unreachable, | ||
| 2975 | } | ||
| 2976 | assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); | ||
| 2977 | } else switch (builtin.os) { | ||
| 2978 | builtin.Os.linux => { | ||
| 2979 | while (true) { | ||
| 2980 | const pid_value = @atomicLoad(i32, &self.data.handle, builtin.AtomicOrder.SeqCst); | ||
| 2981 | if (pid_value == 0) break; | ||
| 2982 | const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null); | ||
| 2983 | switch (linux.getErrno(rc)) { | ||
| 2984 | 0 => continue, | ||
| 2985 | posix.EINTR => continue, | ||
| 2986 | posix.EAGAIN => continue, | ||
| 2987 | else => unreachable, | ||
| 2988 | } | ||
| 2989 | } | ||
| 2990 | assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); | ||
| 2991 | }, | ||
| 2992 | builtin.Os.windows => { | ||
| 2993 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); | ||
| 2994 | assert(windows.CloseHandle(self.data.handle) != 0); | ||
| 2995 | assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); | ||
| 2996 | }, | ||
| 2997 | else => @compileError("Unsupported OS"), | ||
| 2998 | } | ||
| 2999 | } | ||
| 3000 | }; | ||
| 3001 | |||
| 3002 | pub const SpawnThreadError = error{ | ||
| 3003 | /// A system-imposed limit on the number of threads was encountered. | ||
| 3004 | /// There are a number of limits that may trigger this error: | ||
| 3005 | /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), | ||
| 3006 | /// which limits the number of processes and threads for a real | ||
| 3007 | /// user ID, was reached; | ||
| 3008 | /// * the kernel's system-wide limit on the number of processes and | ||
| 3009 | /// threads, /proc/sys/kernel/threads-max, was reached (see | ||
| 3010 | /// proc(5)); | ||
| 3011 | /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was | ||
| 3012 | /// reached (see proc(5)); or | ||
| 3013 | /// * the PID limit (pids.max) imposed by the cgroup "process num‐ | ||
| 3014 | /// ber" (PIDs) controller was reached. | ||
| 3015 | ThreadQuotaExceeded, | ||
| 3016 | |||
| 3017 | /// The kernel cannot allocate sufficient memory to allocate a task structure | ||
| 3018 | /// for the child, or to copy those parts of the caller's context that need to | ||
| 3019 | /// be copied. | ||
| 3020 | SystemResources, | ||
| 3021 | |||
| 3022 | /// Not enough userland memory to spawn the thread. | ||
| 3023 | OutOfMemory, | ||
| 3024 | |||
| 3025 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3026 | Unexpected, | ||
| 3027 | }; | ||
| 3028 | |||
| 3029 | pub var linux_tls_phdr: ?*std.elf.Phdr = null; | ||
| 3030 | pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is | ||
| 3031 | |||
| 3032 | /// caller must call wait on the returned thread | ||
| 3033 | /// fn startFn(@typeOf(context)) T | ||
| 3034 | /// where T is u8, noreturn, void, or !void | ||
| 3035 | /// caller must call wait on the returned thread | ||
| 3036 | pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { | ||
| 3037 | if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); | ||
| 3038 | // TODO compile-time call graph analysis to determine stack upper bound | ||
| 3039 | // https://github.com/ziglang/zig/issues/157 | ||
| 3040 | const default_stack_size = 8 * 1024 * 1024; | ||
| 3041 | |||
| 3042 | const Context = @typeOf(context); | ||
| 3043 | comptime assert(@ArgType(@typeOf(startFn), 0) == Context); | ||
| 3044 | |||
| 3045 | if (builtin.os == builtin.Os.windows) { | ||
| 3046 | const WinThread = struct { | ||
| 3047 | const OuterContext = struct { | ||
| 3048 | thread: Thread, | ||
| 3049 | inner: Context, | ||
| 3050 | }; | ||
| 3051 | extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { | ||
| 3052 | const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; | ||
| 3053 | switch (@typeId(@typeOf(startFn).ReturnType)) { | ||
| 3054 | builtin.TypeId.Int => { | ||
| 3055 | return startFn(arg); | ||
| 3056 | }, | ||
| 3057 | builtin.TypeId.Void => { | ||
| 3058 | startFn(arg); | ||
| 3059 | return 0; | ||
| 3060 | }, | ||
| 3061 | else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), | ||
| 3062 | } | ||
| 3063 | } | ||
| 3064 | }; | ||
| 3065 | |||
| 3066 | const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory; | ||
| 3067 | const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); | ||
| 3068 | const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; | ||
| 3069 | errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); | ||
| 3070 | const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; | ||
| 3071 | const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; | ||
| 3072 | outer_context.* = WinThread.OuterContext{ | ||
| 3073 | .thread = Thread{ | ||
| 3074 | .data = Thread.Data{ | ||
| 3075 | .heap_handle = heap_handle, | ||
| 3076 | .alloc_start = bytes_ptr, | ||
| 3077 | .handle = undefined, | ||
| 3078 | }, | ||
| 3079 | }, | ||
| 3080 | .inner = context, | ||
| 3081 | }; | ||
| 3082 | |||
| 3083 | const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); | ||
| 3084 | outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { | ||
| 3085 | const err = windows.GetLastError(); | ||
| 3086 | return switch (err) { | ||
| 3087 | else => os.unexpectedErrorWindows(err), | ||
| 3088 | }; | ||
| 3089 | }; | ||
| 3090 | return &outer_context.thread; | ||
| 3091 | } | ||
| 3092 | |||
| 3093 | const MainFuncs = struct { | ||
| 3094 | extern fn linuxThreadMain(ctx_addr: usize) u8 { | ||
| 3095 | const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; | ||
| 3096 | |||
| 3097 | switch (@typeId(@typeOf(startFn).ReturnType)) { | ||
| 3098 | builtin.TypeId.Int => { | ||
| 3099 | return startFn(arg); | ||
| 3100 | }, | ||
| 3101 | builtin.TypeId.Void => { | ||
| 3102 | startFn(arg); | ||
| 3103 | return 0; | ||
| 3104 | }, | ||
| 3105 | else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), | ||
| 3106 | } | ||
| 3107 | } | ||
| 3108 | extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { | ||
| 3109 | if (@sizeOf(Context) == 0) { | ||
| 3110 | _ = startFn({}); | ||
| 3111 | return null; | ||
| 3112 | } else { | ||
| 3113 | _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); | ||
| 3114 | return null; | ||
| 3115 | } | ||
| 3116 | } | ||
| 3117 | }; | ||
| 3118 | |||
| 3119 | const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; | ||
| 3120 | |||
| 3121 | var stack_end_offset: usize = undefined; | ||
| 3122 | var thread_start_offset: usize = undefined; | ||
| 3123 | var context_start_offset: usize = undefined; | ||
| 3124 | var tls_start_offset: usize = undefined; | ||
| 3125 | const mmap_len = blk: { | ||
| 3126 | // First in memory will be the stack, which grows downwards. | ||
| 3127 | var l: usize = mem.alignForward(default_stack_size, os.page_size); | ||
| 3128 | stack_end_offset = l; | ||
| 3129 | // Above the stack, so that it can be in the same mmap call, put the Thread object. | ||
| 3130 | l = mem.alignForward(l, @alignOf(Thread)); | ||
| 3131 | thread_start_offset = l; | ||
| 3132 | l += @sizeOf(Thread); | ||
| 3133 | // Next, the Context object. | ||
| 3134 | if (@sizeOf(Context) != 0) { | ||
| 3135 | l = mem.alignForward(l, @alignOf(Context)); | ||
| 3136 | context_start_offset = l; | ||
| 3137 | l += @sizeOf(Context); | ||
| 3138 | } | ||
| 3139 | // Finally, the Thread Local Storage, if any. | ||
| 3140 | if (!Thread.use_pthreads) { | ||
| 3141 | if (linux_tls_phdr) |tls_phdr| { | ||
| 3142 | l = mem.alignForward(l, tls_phdr.p_align); | ||
| 3143 | tls_start_offset = l; | ||
| 3144 | l += tls_phdr.p_memsz; | ||
| 3145 | } | ||
| 3146 | } | ||
| 3147 | break :blk l; | ||
| 3148 | }; | ||
| 3149 | const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); | ||
| 3150 | if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; | ||
| 3151 | errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); | ||
| 3152 | |||
| 3153 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); | ||
| 3154 | thread_ptr.data.mmap_addr = mmap_addr; | ||
| 3155 | thread_ptr.data.mmap_len = mmap_len; | ||
| 3156 | |||
| 3157 | var arg: usize = undefined; | ||
| 3158 | if (@sizeOf(Context) != 0) { | ||
| 3159 | arg = mmap_addr + context_start_offset; | ||
| 3160 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); | ||
| 3161 | context_ptr.* = context; | ||
| 3162 | } | ||
| 3163 | |||
| 3164 | if (Thread.use_pthreads) { | ||
| 3165 | // use pthreads | ||
| 3166 | var attr: c.pthread_attr_t = undefined; | ||
| 3167 | if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; | ||
| 3168 | defer assert(c.pthread_attr_destroy(&attr) == 0); | ||
| 3169 | |||
| 3170 | assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); | ||
| 3171 | |||
| 3172 | const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); | ||
| 3173 | switch (err) { | ||
| 3174 | 0 => return thread_ptr, | ||
| 3175 | posix.EAGAIN => return SpawnThreadError.SystemResources, | ||
| 3176 | posix.EPERM => unreachable, | ||
| 3177 | posix.EINVAL => unreachable, | ||
| 3178 | else => return unexpectedErrorPosix(@intCast(usize, err)), | ||
| 3179 | } | ||
| 3180 | } else if (builtin.os == builtin.Os.linux) { | ||
| 3181 | var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | | ||
| 3182 | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | | ||
| 3183 | posix.CLONE_DETACHED; | ||
| 3184 | var newtls: usize = undefined; | ||
| 3185 | if (linux_tls_phdr) |tls_phdr| { | ||
| 3186 | @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz); | ||
| 3187 | thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; | ||
| 3188 | newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); | ||
| 3189 | flags |= posix.CLONE_SETTLS; | ||
| 3190 | } | ||
| 3191 | const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); | ||
| 3192 | const err = posix.getErrno(rc); | ||
| 3193 | switch (err) { | ||
| 3194 | 0 => return thread_ptr, | ||
| 3195 | posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, | ||
| 3196 | posix.EINVAL => unreachable, | ||
| 3197 | posix.ENOMEM => return SpawnThreadError.SystemResources, | ||
| 3198 | posix.ENOSPC => unreachable, | ||
| 3199 | posix.EPERM => unreachable, | ||
| 3200 | posix.EUSERS => unreachable, | ||
| 3201 | else => return unexpectedErrorPosix(err), | ||
| 3202 | } | ||
| 3203 | } else { | ||
| 3204 | @compileError("Unsupported OS"); | ||
| 3205 | } | ||
| 3206 | } | ||
| 3207 | |||
| 3208 | pub fn posixWait(pid: i32) i32 { | ||
| 3209 | var status: i32 = undefined; | ||
| 3210 | while (true) { | ||
| 3211 | const err = posix.getErrno(posix.waitpid(pid, &status, 0)); | ||
| 3212 | switch (err) { | ||
| 3213 | 0 => return status, | ||
| 3214 | posix.EINTR => continue, | ||
| 3215 | posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 3216 | posix.EINVAL => unreachable, // The options argument was invalid | ||
| 3217 | else => unreachable, | ||
| 3218 | } | ||
| 3219 | } | ||
| 3220 | } | ||
| 3221 | |||
| 3222 | pub fn posixFStat(fd: i32) !posix.Stat { | ||
| 3223 | var stat: posix.Stat = undefined; | ||
| 3224 | const err = posix.getErrno(posix.fstat(fd, &stat)); | ||
| 3225 | if (err > 0) { | ||
| 3226 | return switch (err) { | ||
| 3227 | // We do not make this an error code because if you get EBADF it's always a bug, | ||
| 3228 | // since the fd could have been reused. | ||
| 3229 | posix.EBADF => unreachable, | ||
| 3230 | posix.ENOMEM => error.SystemResources, | ||
| 3231 | else => os.unexpectedErrorPosix(err), | ||
| 3232 | }; | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | return stat; | ||
| 3236 | } | ||
| 3237 | |||
| 3238 | pub const CpuCountError = error{ | ||
| 3239 | OutOfMemory, | ||
| 3240 | PermissionDenied, | ||
| 3241 | |||
| 3242 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3243 | Unexpected, | ||
| 3244 | }; | ||
| 3245 | |||
| 3246 | pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ||
| 3247 | switch (builtin.os) { | ||
| 3248 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { | ||
| 3249 | var count: c_int = undefined; | ||
| 3250 | var count_len: usize = @sizeOf(c_int); | ||
| 3251 | const rc = posix.sysctlbyname(switch (builtin.os) { | ||
| 3252 | builtin.Os.macosx => c"hw.logicalcpu", | ||
| 3253 | else => c"hw.ncpu", | ||
| 3254 | }, @ptrCast(*c_void, &count), &count_len, null, 0); | ||
| 3255 | const err = posix.getErrno(rc); | ||
| 3256 | switch (err) { | ||
| 3257 | 0 => return @intCast(usize, count), | ||
| 3258 | posix.EFAULT => unreachable, | ||
| 3259 | posix.EINVAL => unreachable, | ||
| 3260 | posix.ENOMEM => return CpuCountError.OutOfMemory, | ||
| 3261 | posix.ENOTDIR => unreachable, | ||
| 3262 | posix.EISDIR => unreachable, | ||
| 3263 | posix.ENOENT => unreachable, | ||
| 3264 | posix.EPERM => unreachable, | ||
| 3265 | else => return os.unexpectedErrorPosix(err), | ||
| 3266 | } | ||
| 3267 | }, | ||
| 3268 | builtin.Os.linux => { | ||
| 3269 | const usize_count = 16; | ||
| 3270 | const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); | ||
| 3271 | |||
| 3272 | var set = try allocator.alloc(usize, usize_count); | ||
| 3273 | defer allocator.free(set); | ||
| 3274 | |||
| 3275 | while (true) { | ||
| 3276 | const rc = posix.sched_getaffinity(0, set); | ||
| 3277 | const err = posix.getErrno(rc); | ||
| 3278 | switch (err) { | ||
| 3279 | 0 => { | ||
| 3280 | if (rc < set.len * @sizeOf(usize)) { | ||
| 3281 | const result = set[0 .. rc / @sizeOf(usize)]; | ||
| 3282 | var sum: usize = 0; | ||
| 3283 | for (result) |x| { | ||
| 3284 | sum += @popCount(x); | ||
| 3285 | } | ||
| 3286 | return sum; | ||
| 3287 | } else { | ||
| 3288 | set = try allocator.realloc(usize, set, set.len * 2); | ||
| 3289 | continue; | ||
| 3290 | } | ||
| 3291 | }, | ||
| 3292 | posix.EFAULT => unreachable, | ||
| 3293 | posix.EINVAL => unreachable, | ||
| 3294 | posix.EPERM => return CpuCountError.PermissionDenied, | ||
| 3295 | posix.ESRCH => unreachable, | ||
| 3296 | else => return os.unexpectedErrorPosix(err), | ||
| 3297 | } | ||
| 3298 | } | ||
| 3299 | }, | ||
| 3300 | builtin.Os.windows => { | ||
| 3301 | var system_info: windows.SYSTEM_INFO = undefined; | ||
| 3302 | windows.GetSystemInfo(&system_info); | ||
| 3303 | return @intCast(usize, system_info.dwNumberOfProcessors); | ||
| 3304 | }, | ||
| 3305 | else => @compileError("unsupported OS"), | ||
| 3306 | } | ||
| 3307 | } | ||
| 3308 | |||
| 3309 | pub const BsdKQueueError = error{ | ||
| 3310 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 3311 | ProcessFdQuotaExceeded, | ||
| 3312 | |||
| 3313 | /// The system-wide limit on the total number of open files has been reached. | ||
| 3314 | SystemFdQuotaExceeded, | ||
| 3315 | |||
| 3316 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3317 | Unexpected, | ||
| 3318 | }; | ||
| 3319 | |||
| 3320 | pub fn bsdKQueue() BsdKQueueError!i32 { | ||
| 3321 | const rc = posix.kqueue(); | ||
| 3322 | const err = posix.getErrno(rc); | ||
| 3323 | switch (err) { | ||
| 3324 | 0 => return @intCast(i32, rc), | ||
| 3325 | posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, | ||
| 3326 | posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, | ||
| 3327 | else => return unexpectedErrorPosix(err), | ||
| 3328 | } | ||
| 3329 | } | ||
| 3330 | |||
| 3331 | pub const BsdKEventError = error{ | ||
| 3332 | /// The process does not have permission to register a filter. | ||
| 3333 | AccessDenied, | ||
| 3334 | |||
| 3335 | /// The event could not be found to be modified or deleted. | ||
| 3336 | EventNotFound, | ||
| 3337 | |||
| 3338 | /// No memory was available to register the event. | ||
| 3339 | SystemResources, | ||
| 3340 | |||
| 3341 | /// The specified process to attach to does not exist. | ||
| 3342 | ProcessNotFound, | ||
| 3343 | }; | ||
| 3344 | |||
| 3345 | pub fn bsdKEvent( | ||
| 3346 | kq: i32, | ||
| 3347 | changelist: []const posix.Kevent, | ||
| 3348 | eventlist: []posix.Kevent, | ||
| 3349 | timeout: ?*const posix.timespec, | ||
| 3350 | ) BsdKEventError!usize { | ||
| 3351 | while (true) { | ||
| 3352 | const rc = posix.kevent(kq, changelist, eventlist, timeout); | ||
| 3353 | const err = posix.getErrno(rc); | ||
| 3354 | switch (err) { | ||
| 3355 | 0 => return rc, | ||
| 3356 | posix.EACCES => return BsdKEventError.AccessDenied, | ||
| 3357 | posix.EFAULT => unreachable, | ||
| 3358 | posix.EBADF => unreachable, | ||
| 3359 | posix.EINTR => continue, | ||
| 3360 | posix.EINVAL => unreachable, | ||
| 3361 | posix.ENOENT => return BsdKEventError.EventNotFound, | ||
| 3362 | posix.ENOMEM => return BsdKEventError.SystemResources, | ||
| 3363 | posix.ESRCH => return BsdKEventError.ProcessNotFound, | ||
| 3364 | else => unreachable, | ||
| 3365 | } | ||
| 3366 | } | ||
| 3367 | } | ||
| 3368 | |||
| 3369 | pub fn linuxINotifyInit1(flags: u32) !i32 { | ||
| 3370 | const rc = linux.inotify_init1(flags); | ||
| 3371 | const err = posix.getErrno(rc); | ||
| 3372 | switch (err) { | ||
| 3373 | 0 => return @intCast(i32, rc), | ||
| 3374 | posix.EINVAL => unreachable, | ||
| 3375 | posix.EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 3376 | posix.ENFILE => return error.SystemFdQuotaExceeded, | ||
| 3377 | posix.ENOMEM => return error.SystemResources, | ||
| 3378 | else => return unexpectedErrorPosix(err), | ||
| 3379 | } | ||
| 3380 | } | ||
| 3381 | |||
| 3382 | pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { | ||
| 3383 | const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); | ||
| 3384 | const err = posix.getErrno(rc); | ||
| 3385 | switch (err) { | ||
| 3386 | 0 => return @intCast(i32, rc), | ||
| 3387 | posix.EACCES => return error.AccessDenied, | ||
| 3388 | posix.EBADF => unreachable, | ||
| 3389 | posix.EFAULT => unreachable, | ||
| 3390 | posix.EINVAL => unreachable, | ||
| 3391 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 3392 | posix.ENOENT => return error.FileNotFound, | ||
| 3393 | posix.ENOMEM => return error.SystemResources, | ||
| 3394 | posix.ENOSPC => return error.UserResourceLimitReached, | ||
| 3395 | else => return unexpectedErrorPosix(err), | ||
| 3396 | } | ||
| 3397 | } | ||
| 3398 | |||
| 3399 | pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { | ||
| 3400 | const rc = linux.inotify_rm_watch(inotify_fd, wd); | ||
| 3401 | const err = posix.getErrno(rc); | ||
| 3402 | switch (err) { | ||
| 3403 | 0 => return rc, | ||
| 3404 | posix.EBADF => unreachable, | ||
| 3405 | posix.EINVAL => unreachable, | ||
| 3406 | else => unreachable, | ||
| 3407 | } | ||
| 3408 | } | ||
| 3409 | |||
| 3410 | pub const MProtectError = error{ | ||
| 3411 | AccessDenied, | ||
| 3412 | OutOfMemory, | ||
| 3413 | Unexpected, | ||
| 3414 | }; | ||
| 3415 | |||
| 3416 | /// address and length must be page-aligned | ||
| 3417 | pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { | ||
| 3418 | const negative_page_size = @bitCast(usize, -isize(page_size)); | ||
| 3419 | const aligned_address = address & negative_page_size; | ||
| 3420 | const aligned_end = (address + length + page_size - 1) & negative_page_size; | ||
| 3421 | assert(address == aligned_address); | ||
| 3422 | assert(length == aligned_end - aligned_address); | ||
| 3423 | const rc = posix.mprotect(address, length, protection); | ||
| 3424 | const err = posix.getErrno(rc); | ||
| 3425 | switch (err) { | ||
| 3426 | 0 => return, | ||
| 3427 | posix.EINVAL => unreachable, | ||
| 3428 | posix.EACCES => return error.AccessDenied, | ||
| 3429 | posix.ENOMEM => return error.OutOfMemory, | ||
| 3430 | else => return unexpectedErrorPosix(err), | ||
| 3431 | } | ||
| 3432 | } | ||
std/os/child_process.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const cstr = std.cstr; | 2 | const cstr = std.cstr; |
| 3 | const unicode = std.unicode; | 3 | const unicode = std.unicode; |
| 4 | const io = std.io; | 4 | const io = std.io; |
std/os/darwin.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const c = std.c; | 2 | const c = std.c; |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const maxInt = std.math.maxInt; | 4 | const maxInt = std.math.maxInt; |
std/os/file.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const os = std.os; | 3 | const os = std.os; |
| 4 | const io = std.io; | 4 | const io = std.io; |
std/os/freebsd.zig created+845| ... | @@ -0,0 +1,845 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | pub use @import("freebsd/errno.zig"); | ||
| 4 | |||
| 5 | const std = @import("../std.zig"); | ||
| 6 | const c = std.c; | ||
| 7 | |||
| 8 | const assert = std.debug.assert; | ||
| 9 | const maxInt = std.math.maxInt; | ||
| 10 | pub const Kevent = c.Kevent; | ||
| 11 | |||
| 12 | pub const CTL_KERN = 1; | ||
| 13 | pub const CTL_DEBUG = 5; | ||
| 14 | |||
| 15 | pub const KERN_PROC = 14; // struct: process entries | ||
| 16 | pub const KERN_PROC_PATHNAME = 12; // path to executable | ||
| 17 | |||
| 18 | pub const PATH_MAX = 1024; | ||
| 19 | |||
| 20 | pub const STDIN_FILENO = 0; | ||
| 21 | pub const STDOUT_FILENO = 1; | ||
| 22 | pub const STDERR_FILENO = 2; | ||
| 23 | |||
| 24 | pub const PROT_NONE = 0; | ||
| 25 | pub const PROT_READ = 1; | ||
| 26 | pub const PROT_WRITE = 2; | ||
| 27 | pub const PROT_EXEC = 4; | ||
| 28 | |||
| 29 | pub const CLOCK_REALTIME = 0; | ||
| 30 | pub const CLOCK_VIRTUAL = 1; | ||
| 31 | pub const CLOCK_PROF = 2; | ||
| 32 | pub const CLOCK_MONOTONIC = 4; | ||
| 33 | pub const CLOCK_UPTIME = 5; | ||
| 34 | pub const CLOCK_UPTIME_PRECISE = 7; | ||
| 35 | pub const CLOCK_UPTIME_FAST = 8; | ||
| 36 | pub const CLOCK_REALTIME_PRECISE = 9; | ||
| 37 | pub const CLOCK_REALTIME_FAST = 10; | ||
| 38 | pub const CLOCK_MONOTONIC_PRECISE = 11; | ||
| 39 | pub const CLOCK_MONOTONIC_FAST = 12; | ||
| 40 | pub const CLOCK_SECOND = 13; | ||
| 41 | pub const CLOCK_THREAD_CPUTIME_ID = 14; | ||
| 42 | pub const CLOCK_PROCESS_CPUTIME_ID = 15; | ||
| 43 | |||
| 44 | pub const MAP_FAILED = maxInt(usize); | ||
| 45 | pub const MAP_SHARED = 0x0001; | ||
| 46 | pub const MAP_PRIVATE = 0x0002; | ||
| 47 | pub const MAP_FIXED = 0x0010; | ||
| 48 | pub const MAP_STACK = 0x0400; | ||
| 49 | pub const MAP_NOSYNC = 0x0800; | ||
| 50 | pub const MAP_ANON = 0x1000; | ||
| 51 | pub const MAP_ANONYMOUS = MAP_ANON; | ||
| 52 | pub const MAP_FILE = 0; | ||
| 53 | pub const MAP_NORESERVE = 0; | ||
| 54 | |||
| 55 | pub const MAP_GUARD = 0x00002000; | ||
| 56 | pub const MAP_EXCL = 0x00004000; | ||
| 57 | pub const MAP_NOCORE = 0x00020000; | ||
| 58 | pub const MAP_PREFAULT_READ = 0x00040000; | ||
| 59 | pub const MAP_32BIT = 0x00080000; | ||
| 60 | |||
| 61 | pub const WNOHANG = 1; | ||
| 62 | pub const WUNTRACED = 2; | ||
| 63 | pub const WSTOPPED = WUNTRACED; | ||
| 64 | pub const WCONTINUED = 4; | ||
| 65 | pub const WNOWAIT = 8; | ||
| 66 | pub const WEXITED = 16; | ||
| 67 | pub const WTRAPPED = 32; | ||
| 68 | |||
| 69 | pub const SA_ONSTACK = 0x0001; | ||
| 70 | pub const SA_RESTART = 0x0002; | ||
| 71 | pub const SA_RESETHAND = 0x0004; | ||
| 72 | pub const SA_NOCLDSTOP = 0x0008; | ||
| 73 | pub const SA_NODEFER = 0x0010; | ||
| 74 | pub const SA_NOCLDWAIT = 0x0020; | ||
| 75 | pub const SA_SIGINFO = 0x0040; | ||
| 76 | |||
| 77 | pub const SIGHUP = 1; | ||
| 78 | pub const SIGINT = 2; | ||
| 79 | pub const SIGQUIT = 3; | ||
| 80 | pub const SIGILL = 4; | ||
| 81 | pub const SIGTRAP = 5; | ||
| 82 | pub const SIGABRT = 6; | ||
| 83 | pub const SIGIOT = SIGABRT; | ||
| 84 | pub const SIGEMT = 7; | ||
| 85 | pub const SIGFPE = 8; | ||
| 86 | pub const SIGKILL = 9; | ||
| 87 | pub const SIGBUS = 10; | ||
| 88 | pub const SIGSEGV = 11; | ||
| 89 | pub const SIGSYS = 12; | ||
| 90 | pub const SIGPIPE = 13; | ||
| 91 | pub const SIGALRM = 14; | ||
| 92 | pub const SIGTERM = 15; | ||
| 93 | pub const SIGURG = 16; | ||
| 94 | pub const SIGSTOP = 17; | ||
| 95 | pub const SIGTSTP = 18; | ||
| 96 | pub const SIGCONT = 19; | ||
| 97 | pub const SIGCHLD = 20; | ||
| 98 | pub const SIGTTIN = 21; | ||
| 99 | pub const SIGTTOU = 22; | ||
| 100 | pub const SIGIO = 23; | ||
| 101 | pub const SIGXCPU = 24; | ||
| 102 | pub const SIGXFSZ = 25; | ||
| 103 | pub const SIGVTALRM = 26; | ||
| 104 | pub const SIGPROF = 27; | ||
| 105 | pub const SIGWINCH = 28; | ||
| 106 | pub const SIGINFO = 29; | ||
| 107 | pub const SIGUSR1 = 30; | ||
| 108 | pub const SIGUSR2 = 31; | ||
| 109 | pub const SIGTHR = 32; | ||
| 110 | pub const SIGLWP = SIGTHR; | ||
| 111 | pub const SIGLIBRT = 33; | ||
| 112 | |||
| 113 | pub const SIGRTMIN = 65; | ||
| 114 | pub const SIGRTMAX = 126; | ||
| 115 | |||
| 116 | // access function | ||
| 117 | pub const F_OK = 0; // test for existence of file | ||
| 118 | pub const X_OK = 1; // test for execute or search permission | ||
| 119 | pub const W_OK = 2; // test for write permission | ||
| 120 | pub const R_OK = 4; // test for read permission | ||
| 121 | |||
| 122 | pub const O_RDONLY = 0x0000; | ||
| 123 | pub const O_WRONLY = 0x0001; | ||
| 124 | pub const O_RDWR = 0x0002; | ||
| 125 | pub const O_ACCMODE = 0x0003; | ||
| 126 | |||
| 127 | pub const O_CREAT = 0x0200; | ||
| 128 | pub const O_EXCL = 0x0800; | ||
| 129 | pub const O_NOCTTY = 0x8000; | ||
| 130 | pub const O_TRUNC = 0x0400; | ||
| 131 | pub const O_APPEND = 0x0008; | ||
| 132 | pub const O_NONBLOCK = 0x0004; | ||
| 133 | pub const O_DSYNC = 0o10000; | ||
| 134 | pub const O_SYNC = 0x0080; | ||
| 135 | pub const O_RSYNC = 0o4010000; | ||
| 136 | pub const O_DIRECTORY = 0o200000; | ||
| 137 | pub const O_NOFOLLOW = 0x0100; | ||
| 138 | pub const O_CLOEXEC = 0x00100000; | ||
| 139 | |||
| 140 | pub const O_ASYNC = 0x0040; | ||
| 141 | pub const O_DIRECT = 0x00010000; | ||
| 142 | pub const O_LARGEFILE = 0; | ||
| 143 | pub const O_NOATIME = 0o1000000; | ||
| 144 | pub const O_PATH = 0o10000000; | ||
| 145 | pub const O_TMPFILE = 0o20200000; | ||
| 146 | pub const O_NDELAY = O_NONBLOCK; | ||
| 147 | |||
| 148 | pub const F_DUPFD = 0; | ||
| 149 | pub const F_GETFD = 1; | ||
| 150 | pub const F_SETFD = 2; | ||
| 151 | pub const F_GETFL = 3; | ||
| 152 | pub const F_SETFL = 4; | ||
| 153 | |||
| 154 | pub const F_SETOWN = 8; | ||
| 155 | pub const F_GETOWN = 9; | ||
| 156 | pub const F_SETSIG = 10; | ||
| 157 | pub const F_GETSIG = 11; | ||
| 158 | |||
| 159 | pub const F_GETLK = 5; | ||
| 160 | pub const F_SETLK = 6; | ||
| 161 | pub const F_SETLKW = 7; | ||
| 162 | |||
| 163 | pub const F_SETOWN_EX = 15; | ||
| 164 | pub const F_GETOWN_EX = 16; | ||
| 165 | |||
| 166 | pub const F_GETOWNER_UIDS = 17; | ||
| 167 | |||
| 168 | pub const SEEK_SET = 0; | ||
| 169 | pub const SEEK_CUR = 1; | ||
| 170 | pub const SEEK_END = 2; | ||
| 171 | |||
| 172 | pub const SIG_BLOCK = 1; | ||
| 173 | pub const SIG_UNBLOCK = 2; | ||
| 174 | pub const SIG_SETMASK = 3; | ||
| 175 | |||
| 176 | pub const SOCK_STREAM = 1; | ||
| 177 | pub const SOCK_DGRAM = 2; | ||
| 178 | pub const SOCK_RAW = 3; | ||
| 179 | pub const SOCK_RDM = 4; | ||
| 180 | pub const SOCK_SEQPACKET = 5; | ||
| 181 | |||
| 182 | pub const SOCK_CLOEXEC = 0x10000000; | ||
| 183 | pub const SOCK_NONBLOCK = 0x20000000; | ||
| 184 | |||
| 185 | pub const PROTO_ip = 0o000; | ||
| 186 | pub const PROTO_icmp = 0o001; | ||
| 187 | pub const PROTO_igmp = 0o002; | ||
| 188 | pub const PROTO_ggp = 0o003; | ||
| 189 | pub const PROTO_ipencap = 0o004; | ||
| 190 | pub const PROTO_st = 0o005; | ||
| 191 | pub const PROTO_tcp = 0o006; | ||
| 192 | pub const PROTO_egp = 0o010; | ||
| 193 | pub const PROTO_pup = 0o014; | ||
| 194 | pub const PROTO_udp = 0o021; | ||
| 195 | pub const PROTO_hmp = 0o024; | ||
| 196 | pub const PROTO_xns_idp = 0o026; | ||
| 197 | pub const PROTO_rdp = 0o033; | ||
| 198 | pub const PROTO_iso_tp4 = 0o035; | ||
| 199 | pub const PROTO_xtp = 0o044; | ||
| 200 | pub const PROTO_ddp = 0o045; | ||
| 201 | pub const PROTO_idpr_cmtp = 0o046; | ||
| 202 | pub const PROTO_ipv6 = 0o051; | ||
| 203 | pub const PROTO_ipv6_route = 0o053; | ||
| 204 | pub const PROTO_ipv6_frag = 0o054; | ||
| 205 | pub const PROTO_idrp = 0o055; | ||
| 206 | pub const PROTO_rsvp = 0o056; | ||
| 207 | pub const PROTO_gre = 0o057; | ||
| 208 | pub const PROTO_esp = 0o062; | ||
| 209 | pub const PROTO_ah = 0o063; | ||
| 210 | pub const PROTO_skip = 0o071; | ||
| 211 | pub const PROTO_ipv6_icmp = 0o072; | ||
| 212 | pub const PROTO_ipv6_nonxt = 0o073; | ||
| 213 | pub const PROTO_ipv6_opts = 0o074; | ||
| 214 | pub const PROTO_rspf = 0o111; | ||
| 215 | pub const PROTO_vmtp = 0o121; | ||
| 216 | pub const PROTO_ospf = 0o131; | ||
| 217 | pub const PROTO_ipip = 0o136; | ||
| 218 | pub const PROTO_encap = 0o142; | ||
| 219 | pub const PROTO_pim = 0o147; | ||
| 220 | pub const PROTO_raw = 0o377; | ||
| 221 | |||
| 222 | pub const PF_UNSPEC = 0; | ||
| 223 | pub const PF_LOCAL = 1; | ||
| 224 | pub const PF_UNIX = PF_LOCAL; | ||
| 225 | pub const PF_FILE = PF_LOCAL; | ||
| 226 | pub const PF_INET = 2; | ||
| 227 | pub const PF_AX25 = 3; | ||
| 228 | pub const PF_IPX = 4; | ||
| 229 | pub const PF_APPLETALK = 5; | ||
| 230 | pub const PF_NETROM = 6; | ||
| 231 | pub const PF_BRIDGE = 7; | ||
| 232 | pub const PF_ATMPVC = 8; | ||
| 233 | pub const PF_X25 = 9; | ||
| 234 | pub const PF_INET6 = 10; | ||
| 235 | pub const PF_ROSE = 11; | ||
| 236 | pub const PF_DECnet = 12; | ||
| 237 | pub const PF_NETBEUI = 13; | ||
| 238 | pub const PF_SECURITY = 14; | ||
| 239 | pub const PF_KEY = 15; | ||
| 240 | pub const PF_NETLINK = 16; | ||
| 241 | pub const PF_ROUTE = PF_NETLINK; | ||
| 242 | pub const PF_PACKET = 17; | ||
| 243 | pub const PF_ASH = 18; | ||
| 244 | pub const PF_ECONET = 19; | ||
| 245 | pub const PF_ATMSVC = 20; | ||
| 246 | pub const PF_RDS = 21; | ||
| 247 | pub const PF_SNA = 22; | ||
| 248 | pub const PF_IRDA = 23; | ||
| 249 | pub const PF_PPPOX = 24; | ||
| 250 | pub const PF_WANPIPE = 25; | ||
| 251 | pub const PF_LLC = 26; | ||
| 252 | pub const PF_IB = 27; | ||
| 253 | pub const PF_MPLS = 28; | ||
| 254 | pub const PF_CAN = 29; | ||
| 255 | pub const PF_TIPC = 30; | ||
| 256 | pub const PF_BLUETOOTH = 31; | ||
| 257 | pub const PF_IUCV = 32; | ||
| 258 | pub const PF_RXRPC = 33; | ||
| 259 | pub const PF_ISDN = 34; | ||
| 260 | pub const PF_PHONET = 35; | ||
| 261 | pub const PF_IEEE802154 = 36; | ||
| 262 | pub const PF_CAIF = 37; | ||
| 263 | pub const PF_ALG = 38; | ||
| 264 | pub const PF_NFC = 39; | ||
| 265 | pub const PF_VSOCK = 40; | ||
| 266 | pub const PF_MAX = 41; | ||
| 267 | |||
| 268 | pub const AF_UNSPEC = PF_UNSPEC; | ||
| 269 | pub const AF_LOCAL = PF_LOCAL; | ||
| 270 | pub const AF_UNIX = AF_LOCAL; | ||
| 271 | pub const AF_FILE = AF_LOCAL; | ||
| 272 | pub const AF_INET = PF_INET; | ||
| 273 | pub const AF_AX25 = PF_AX25; | ||
| 274 | pub const AF_IPX = PF_IPX; | ||
| 275 | pub const AF_APPLETALK = PF_APPLETALK; | ||
| 276 | pub const AF_NETROM = PF_NETROM; | ||
| 277 | pub const AF_BRIDGE = PF_BRIDGE; | ||
| 278 | pub const AF_ATMPVC = PF_ATMPVC; | ||
| 279 | pub const AF_X25 = PF_X25; | ||
| 280 | pub const AF_INET6 = PF_INET6; | ||
| 281 | pub const AF_ROSE = PF_ROSE; | ||
| 282 | pub const AF_DECnet = PF_DECnet; | ||
| 283 | pub const AF_NETBEUI = PF_NETBEUI; | ||
| 284 | pub const AF_SECURITY = PF_SECURITY; | ||
| 285 | pub const AF_KEY = PF_KEY; | ||
| 286 | pub const AF_NETLINK = PF_NETLINK; | ||
| 287 | pub const AF_ROUTE = PF_ROUTE; | ||
| 288 | pub const AF_PACKET = PF_PACKET; | ||
| 289 | pub const AF_ASH = PF_ASH; | ||
| 290 | pub const AF_ECONET = PF_ECONET; | ||
| 291 | pub const AF_ATMSVC = PF_ATMSVC; | ||
| 292 | pub const AF_RDS = PF_RDS; | ||
| 293 | pub const AF_SNA = PF_SNA; | ||
| 294 | pub const AF_IRDA = PF_IRDA; | ||
| 295 | pub const AF_PPPOX = PF_PPPOX; | ||
| 296 | pub const AF_WANPIPE = PF_WANPIPE; | ||
| 297 | pub const AF_LLC = PF_LLC; | ||
| 298 | pub const AF_IB = PF_IB; | ||
| 299 | pub const AF_MPLS = PF_MPLS; | ||
| 300 | pub const AF_CAN = PF_CAN; | ||
| 301 | pub const AF_TIPC = PF_TIPC; | ||
| 302 | pub const AF_BLUETOOTH = PF_BLUETOOTH; | ||
| 303 | pub const AF_IUCV = PF_IUCV; | ||
| 304 | pub const AF_RXRPC = PF_RXRPC; | ||
| 305 | pub const AF_ISDN = PF_ISDN; | ||
| 306 | pub const AF_PHONET = PF_PHONET; | ||
| 307 | pub const AF_IEEE802154 = PF_IEEE802154; | ||
| 308 | pub const AF_CAIF = PF_CAIF; | ||
| 309 | pub const AF_ALG = PF_ALG; | ||
| 310 | pub const AF_NFC = PF_NFC; | ||
| 311 | pub const AF_VSOCK = PF_VSOCK; | ||
| 312 | pub const AF_MAX = PF_MAX; | ||
| 313 | |||
| 314 | pub const DT_UNKNOWN = 0; | ||
| 315 | pub const DT_FIFO = 1; | ||
| 316 | pub const DT_CHR = 2; | ||
| 317 | pub const DT_DIR = 4; | ||
| 318 | pub const DT_BLK = 6; | ||
| 319 | pub const DT_REG = 8; | ||
| 320 | pub const DT_LNK = 10; | ||
| 321 | pub const DT_SOCK = 12; | ||
| 322 | pub const DT_WHT = 14; | ||
| 323 | |||
| 324 | /// add event to kq (implies enable) | ||
| 325 | pub const EV_ADD = 0x0001; | ||
| 326 | |||
| 327 | /// delete event from kq | ||
| 328 | pub const EV_DELETE = 0x0002; | ||
| 329 | |||
| 330 | /// enable event | ||
| 331 | pub const EV_ENABLE = 0x0004; | ||
| 332 | |||
| 333 | /// disable event (not reported) | ||
| 334 | pub const EV_DISABLE = 0x0008; | ||
| 335 | |||
| 336 | /// only report one occurrence | ||
| 337 | pub const EV_ONESHOT = 0x0010; | ||
| 338 | |||
| 339 | /// clear event state after reporting | ||
| 340 | pub const EV_CLEAR = 0x0020; | ||
| 341 | |||
| 342 | /// force immediate event output | ||
| 343 | /// ... with or without EV_ERROR | ||
| 344 | /// ... use KEVENT_FLAG_ERROR_EVENTS | ||
| 345 | /// on syscalls supporting flags | ||
| 346 | pub const EV_RECEIPT = 0x0040; | ||
| 347 | |||
| 348 | /// disable event after reporting | ||
| 349 | pub const EV_DISPATCH = 0x0080; | ||
| 350 | |||
| 351 | pub const EVFILT_READ = -1; | ||
| 352 | pub const EVFILT_WRITE = -2; | ||
| 353 | |||
| 354 | /// attached to aio requests | ||
| 355 | pub const EVFILT_AIO = -3; | ||
| 356 | |||
| 357 | /// attached to vnodes | ||
| 358 | pub const EVFILT_VNODE = -4; | ||
| 359 | |||
| 360 | /// attached to struct proc | ||
| 361 | pub const EVFILT_PROC = -5; | ||
| 362 | |||
| 363 | /// attached to struct proc | ||
| 364 | pub const EVFILT_SIGNAL = -6; | ||
| 365 | |||
| 366 | /// timers | ||
| 367 | pub const EVFILT_TIMER = -7; | ||
| 368 | |||
| 369 | /// Process descriptors | ||
| 370 | pub const EVFILT_PROCDESC = -8; | ||
| 371 | |||
| 372 | /// Filesystem events | ||
| 373 | pub const EVFILT_FS = -9; | ||
| 374 | |||
| 375 | pub const EVFILT_LIO = -10; | ||
| 376 | |||
| 377 | /// User events | ||
| 378 | pub const EVFILT_USER = -11; | ||
| 379 | |||
| 380 | /// Sendfile events | ||
| 381 | pub const EVFILT_SENDFILE = -12; | ||
| 382 | |||
| 383 | pub const EVFILT_EMPTY = -13; | ||
| 384 | |||
| 385 | /// On input, NOTE_TRIGGER causes the event to be triggered for output. | ||
| 386 | pub const NOTE_TRIGGER = 0x01000000; | ||
| 387 | |||
| 388 | /// ignore input fflags | ||
| 389 | pub const NOTE_FFNOP = 0x00000000; | ||
| 390 | |||
| 391 | /// and fflags | ||
| 392 | pub const NOTE_FFAND = 0x40000000; | ||
| 393 | |||
| 394 | /// or fflags | ||
| 395 | pub const NOTE_FFOR = 0x80000000; | ||
| 396 | |||
| 397 | /// copy fflags | ||
| 398 | pub const NOTE_FFCOPY = 0xc0000000; | ||
| 399 | |||
| 400 | /// mask for operations | ||
| 401 | pub const NOTE_FFCTRLMASK = 0xc0000000; | ||
| 402 | pub const NOTE_FFLAGSMASK = 0x00ffffff; | ||
| 403 | |||
| 404 | /// low water mark | ||
| 405 | pub const NOTE_LOWAT = 0x00000001; | ||
| 406 | |||
| 407 | /// behave like poll() | ||
| 408 | pub const NOTE_FILE_POLL = 0x00000002; | ||
| 409 | |||
| 410 | /// vnode was removed | ||
| 411 | pub const NOTE_DELETE = 0x00000001; | ||
| 412 | |||
| 413 | /// data contents changed | ||
| 414 | pub const NOTE_WRITE = 0x00000002; | ||
| 415 | |||
| 416 | /// size increased | ||
| 417 | pub const NOTE_EXTEND = 0x00000004; | ||
| 418 | |||
| 419 | /// attributes changed | ||
| 420 | pub const NOTE_ATTRIB = 0x00000008; | ||
| 421 | |||
| 422 | /// link count changed | ||
| 423 | pub const NOTE_LINK = 0x00000010; | ||
| 424 | |||
| 425 | /// vnode was renamed | ||
| 426 | pub const NOTE_RENAME = 0x00000020; | ||
| 427 | |||
| 428 | /// vnode access was revoked | ||
| 429 | pub const NOTE_REVOKE = 0x00000040; | ||
| 430 | |||
| 431 | /// vnode was opened | ||
| 432 | pub const NOTE_OPEN = 0x00000080; | ||
| 433 | |||
| 434 | /// file closed, fd did not allow write | ||
| 435 | pub const NOTE_CLOSE = 0x00000100; | ||
| 436 | |||
| 437 | /// file closed, fd did allow write | ||
| 438 | pub const NOTE_CLOSE_WRITE = 0x00000200; | ||
| 439 | |||
| 440 | /// file was read | ||
| 441 | pub const NOTE_READ = 0x00000400; | ||
| 442 | |||
| 443 | /// process exited | ||
| 444 | pub const NOTE_EXIT = 0x80000000; | ||
| 445 | |||
| 446 | /// process forked | ||
| 447 | pub const NOTE_FORK = 0x40000000; | ||
| 448 | |||
| 449 | /// process exec'd | ||
| 450 | pub const NOTE_EXEC = 0x20000000; | ||
| 451 | |||
| 452 | /// mask for signal & exit status | ||
| 453 | pub const NOTE_PDATAMASK = 0x000fffff; | ||
| 454 | pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); | ||
| 455 | |||
| 456 | /// data is seconds | ||
| 457 | pub const NOTE_SECONDS = 0x00000001; | ||
| 458 | |||
| 459 | /// data is milliseconds | ||
| 460 | pub const NOTE_MSECONDS = 0x00000002; | ||
| 461 | |||
| 462 | /// data is microseconds | ||
| 463 | pub const NOTE_USECONDS = 0x00000004; | ||
| 464 | |||
| 465 | /// data is nanoseconds | ||
| 466 | pub const NOTE_NSECONDS = 0x00000008; | ||
| 467 | |||
| 468 | /// timeout is absolute | ||
| 469 | pub const NOTE_ABSTIME = 0x00000010; | ||
| 470 | |||
| 471 | pub const TCGETS = 0x5401; | ||
| 472 | pub const TCSETS = 0x5402; | ||
| 473 | pub const TCSETSW = 0x5403; | ||
| 474 | pub const TCSETSF = 0x5404; | ||
| 475 | pub const TCGETA = 0x5405; | ||
| 476 | pub const TCSETA = 0x5406; | ||
| 477 | pub const TCSETAW = 0x5407; | ||
| 478 | pub const TCSETAF = 0x5408; | ||
| 479 | pub const TCSBRK = 0x5409; | ||
| 480 | pub const TCXONC = 0x540A; | ||
| 481 | pub const TCFLSH = 0x540B; | ||
| 482 | pub const TIOCEXCL = 0x540C; | ||
| 483 | pub const TIOCNXCL = 0x540D; | ||
| 484 | pub const TIOCSCTTY = 0x540E; | ||
| 485 | pub const TIOCGPGRP = 0x540F; | ||
| 486 | pub const TIOCSPGRP = 0x5410; | ||
| 487 | pub const TIOCOUTQ = 0x5411; | ||
| 488 | pub const TIOCSTI = 0x5412; | ||
| 489 | pub const TIOCGWINSZ = 0x5413; | ||
| 490 | pub const TIOCSWINSZ = 0x5414; | ||
| 491 | pub const TIOCMGET = 0x5415; | ||
| 492 | pub const TIOCMBIS = 0x5416; | ||
| 493 | pub const TIOCMBIC = 0x5417; | ||
| 494 | pub const TIOCMSET = 0x5418; | ||
| 495 | pub const TIOCGSOFTCAR = 0x5419; | ||
| 496 | pub const TIOCSSOFTCAR = 0x541A; | ||
| 497 | pub const FIONREAD = 0x541B; | ||
| 498 | pub const TIOCINQ = FIONREAD; | ||
| 499 | pub const TIOCLINUX = 0x541C; | ||
| 500 | pub const TIOCCONS = 0x541D; | ||
| 501 | pub const TIOCGSERIAL = 0x541E; | ||
| 502 | pub const TIOCSSERIAL = 0x541F; | ||
| 503 | pub const TIOCPKT = 0x5420; | ||
| 504 | pub const FIONBIO = 0x5421; | ||
| 505 | pub const TIOCNOTTY = 0x5422; | ||
| 506 | pub const TIOCSETD = 0x5423; | ||
| 507 | pub const TIOCGETD = 0x5424; | ||
| 508 | pub const TCSBRKP = 0x5425; | ||
| 509 | pub const TIOCSBRK = 0x5427; | ||
| 510 | pub const TIOCCBRK = 0x5428; | ||
| 511 | pub const TIOCGSID = 0x5429; | ||
| 512 | pub const TIOCGRS485 = 0x542E; | ||
| 513 | pub const TIOCSRS485 = 0x542F; | ||
| 514 | pub const TIOCGPTN = 0x80045430; | ||
| 515 | pub const TIOCSPTLCK = 0x40045431; | ||
| 516 | pub const TIOCGDEV = 0x80045432; | ||
| 517 | pub const TCGETX = 0x5432; | ||
| 518 | pub const TCSETX = 0x5433; | ||
| 519 | pub const TCSETXF = 0x5434; | ||
| 520 | pub const TCSETXW = 0x5435; | ||
| 521 | pub const TIOCSIG = 0x40045436; | ||
| 522 | pub const TIOCVHANGUP = 0x5437; | ||
| 523 | pub const TIOCGPKT = 0x80045438; | ||
| 524 | pub const TIOCGPTLCK = 0x80045439; | ||
| 525 | pub const TIOCGEXCL = 0x80045440; | ||
| 526 | |||
| 527 | pub const sockaddr = c.sockaddr; | ||
| 528 | pub const sockaddr_in = c.sockaddr_in; | ||
| 529 | pub const sockaddr_in6 = c.sockaddr_in6; | ||
| 530 | |||
| 531 | fn unsigned(s: i32) u32 { | ||
| 532 | return @bitCast(u32, s); | ||
| 533 | } | ||
| 534 | fn signed(s: u32) i32 { | ||
| 535 | return @bitCast(i32, s); | ||
| 536 | } | ||
| 537 | pub fn WEXITSTATUS(s: i32) i32 { | ||
| 538 | return signed((unsigned(s) & 0xff00) >> 8); | ||
| 539 | } | ||
| 540 | pub fn WTERMSIG(s: i32) i32 { | ||
| 541 | return signed(unsigned(s) & 0x7f); | ||
| 542 | } | ||
| 543 | pub fn WSTOPSIG(s: i32) i32 { | ||
| 544 | return WEXITSTATUS(s); | ||
| 545 | } | ||
| 546 | pub fn WIFEXITED(s: i32) bool { | ||
| 547 | return WTERMSIG(s) == 0; | ||
| 548 | } | ||
| 549 | pub fn WIFSTOPPED(s: i32) bool { | ||
| 550 | return @intCast(u16, (((unsigned(s) & 0xffff) *% 0x10001) >> 8)) > 0x7f00; | ||
| 551 | } | ||
| 552 | pub fn WIFSIGNALED(s: i32) bool { | ||
| 553 | return (unsigned(s) & 0xffff) -% 1 < 0xff; | ||
| 554 | } | ||
| 555 | |||
| 556 | pub const winsize = extern struct { | ||
| 557 | ws_row: u16, | ||
| 558 | ws_col: u16, | ||
| 559 | ws_xpixel: u16, | ||
| 560 | ws_ypixel: u16, | ||
| 561 | }; | ||
| 562 | |||
| 563 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 564 | pub fn getErrno(r: usize) usize { | ||
| 565 | const signed_r = @bitCast(isize, r); | ||
| 566 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 567 | } | ||
| 568 | |||
| 569 | pub fn dup2(old: i32, new: i32) usize { | ||
| 570 | return errnoWrap(c.dup2(old, new)); | ||
| 571 | } | ||
| 572 | |||
| 573 | pub fn chdir(path: [*]const u8) usize { | ||
| 574 | return errnoWrap(c.chdir(path)); | ||
| 575 | } | ||
| 576 | |||
| 577 | pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { | ||
| 578 | return errnoWrap(c.execve(path, argv, envp)); | ||
| 579 | } | ||
| 580 | |||
| 581 | pub fn fork() usize { | ||
| 582 | return errnoWrap(c.fork()); | ||
| 583 | } | ||
| 584 | |||
| 585 | pub fn access(path: [*]const u8, mode: u32) usize { | ||
| 586 | return errnoWrap(c.access(path, mode)); | ||
| 587 | } | ||
| 588 | |||
| 589 | pub fn getcwd(buf: [*]u8, size: usize) usize { | ||
| 590 | return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 591 | } | ||
| 592 | |||
| 593 | pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { | ||
| 594 | return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); | ||
| 595 | } | ||
| 596 | |||
| 597 | pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { | ||
| 598 | return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); | ||
| 599 | } | ||
| 600 | |||
| 601 | pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { | ||
| 602 | return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 603 | } | ||
| 604 | |||
| 605 | pub fn isatty(fd: i32) bool { | ||
| 606 | return c.isatty(fd) != 0; | ||
| 607 | } | ||
| 608 | |||
| 609 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 610 | return errnoWrap(c.readlink(path, buf_ptr, buf_len)); | ||
| 611 | } | ||
| 612 | |||
| 613 | pub fn mkdir(path: [*]const u8, mode: u32) usize { | ||
| 614 | return errnoWrap(c.mkdir(path, mode)); | ||
| 615 | } | ||
| 616 | |||
| 617 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | ||
| 618 | const ptr_result = c.mmap( | ||
| 619 | @ptrCast(?*c_void, address), | ||
| 620 | length, | ||
| 621 | @bitCast(c_int, @intCast(c_uint, prot)), | ||
| 622 | @bitCast(c_int, c_uint(flags)), | ||
| 623 | fd, | ||
| 624 | offset, | ||
| 625 | ); | ||
| 626 | const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); | ||
| 627 | return errnoWrap(isize_result); | ||
| 628 | } | ||
| 629 | |||
| 630 | pub fn munmap(address: usize, length: usize) usize { | ||
| 631 | return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); | ||
| 632 | } | ||
| 633 | |||
| 634 | pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { | ||
| 635 | return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); | ||
| 636 | } | ||
| 637 | |||
| 638 | pub fn rmdir(path: [*]const u8) usize { | ||
| 639 | return errnoWrap(c.rmdir(path)); | ||
| 640 | } | ||
| 641 | |||
| 642 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | ||
| 643 | return errnoWrap(c.symlink(existing, new)); | ||
| 644 | } | ||
| 645 | |||
| 646 | pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { | ||
| 647 | return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); | ||
| 648 | } | ||
| 649 | |||
| 650 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { | ||
| 651 | return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 652 | } | ||
| 653 | |||
| 654 | pub fn pipe(fd: *[2]i32) usize { | ||
| 655 | return pipe2(fd, 0); | ||
| 656 | } | ||
| 657 | |||
| 658 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | ||
| 659 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 660 | return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); | ||
| 661 | } | ||
| 662 | |||
| 663 | pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { | ||
| 664 | return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); | ||
| 665 | } | ||
| 666 | |||
| 667 | pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { | ||
| 668 | return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); | ||
| 669 | } | ||
| 670 | |||
| 671 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { | ||
| 672 | return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 673 | } | ||
| 674 | |||
| 675 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | ||
| 676 | return errnoWrap(c.rename(old, new)); | ||
| 677 | } | ||
| 678 | |||
| 679 | pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { | ||
| 680 | return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); | ||
| 681 | } | ||
| 682 | |||
| 683 | pub fn create(path: [*]const u8, perm: usize) usize { | ||
| 684 | return arch.syscall2(SYS_creat, @ptrToInt(path), perm); | ||
| 685 | } | ||
| 686 | |||
| 687 | pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { | ||
| 688 | return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); | ||
| 689 | } | ||
| 690 | |||
| 691 | pub fn close(fd: i32) usize { | ||
| 692 | return errnoWrap(c.close(fd)); | ||
| 693 | } | ||
| 694 | |||
| 695 | pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { | ||
| 696 | return errnoWrap(c.lseek(fd, offset, whence)); | ||
| 697 | } | ||
| 698 | |||
| 699 | pub fn exit(code: i32) noreturn { | ||
| 700 | c.exit(code); | ||
| 701 | } | ||
| 702 | |||
| 703 | pub fn kill(pid: i32, sig: i32) usize { | ||
| 704 | return errnoWrap(c.kill(pid, sig)); | ||
| 705 | } | ||
| 706 | |||
| 707 | pub fn unlink(path: [*]const u8) usize { | ||
| 708 | return errnoWrap(c.unlink(path)); | ||
| 709 | } | ||
| 710 | |||
| 711 | pub fn waitpid(pid: i32, status: *i32, options: u32) usize { | ||
| 712 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 713 | return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); | ||
| 714 | } | ||
| 715 | |||
| 716 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | ||
| 717 | return errnoWrap(c.nanosleep(req, rem)); | ||
| 718 | } | ||
| 719 | |||
| 720 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | ||
| 721 | return errnoWrap(c.clock_gettime(clk_id, tp)); | ||
| 722 | } | ||
| 723 | |||
| 724 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | ||
| 725 | return errnoWrap(c.clock_getres(clk_id, tp)); | ||
| 726 | } | ||
| 727 | |||
| 728 | pub fn setuid(uid: u32) usize { | ||
| 729 | return errnoWrap(c.setuid(uid)); | ||
| 730 | } | ||
| 731 | |||
| 732 | pub fn setgid(gid: u32) usize { | ||
| 733 | return errnoWrap(c.setgid(gid)); | ||
| 734 | } | ||
| 735 | |||
| 736 | pub fn setreuid(ruid: u32, euid: u32) usize { | ||
| 737 | return errnoWrap(c.setreuid(ruid, euid)); | ||
| 738 | } | ||
| 739 | |||
| 740 | pub fn setregid(rgid: u32, egid: u32) usize { | ||
| 741 | return errnoWrap(c.setregid(rgid, egid)); | ||
| 742 | } | ||
| 743 | |||
| 744 | const NSIG = 32; | ||
| 745 | |||
| 746 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | ||
| 747 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ||
| 748 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | ||
| 749 | |||
| 750 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | ||
| 751 | pub const Sigaction = extern struct { | ||
| 752 | /// signal handler | ||
| 753 | __sigaction_u: extern union { | ||
| 754 | __sa_handler: extern fn (i32) void, | ||
| 755 | __sa_sigaction: extern fn (i32, *__siginfo, usize) void, | ||
| 756 | }, | ||
| 757 | |||
| 758 | /// see signal options | ||
| 759 | sa_flags: u32, | ||
| 760 | |||
| 761 | /// signal mask to apply | ||
| 762 | sa_mask: sigset_t, | ||
| 763 | }; | ||
| 764 | |||
| 765 | pub const _SIG_WORDS = 4; | ||
| 766 | pub const _SIG_MAXSIG = 128; | ||
| 767 | |||
| 768 | pub inline fn _SIG_IDX(sig: usize) usize { | ||
| 769 | return sig - 1; | ||
| 770 | } | ||
| 771 | pub inline fn _SIG_WORD(sig: usize) usize { | ||
| 772 | return_SIG_IDX(sig) >> 5; | ||
| 773 | } | ||
| 774 | pub inline fn _SIG_BIT(sig: usize) usize { | ||
| 775 | return 1 << (_SIG_IDX(sig) & 31); | ||
| 776 | } | ||
| 777 | pub inline fn _SIG_VALID(sig: usize) usize { | ||
| 778 | return sig <= _SIG_MAXSIG and sig > 0; | ||
| 779 | } | ||
| 780 | |||
| 781 | pub const sigset_t = extern struct { | ||
| 782 | __bits: [_SIG_WORDS]u32, | ||
| 783 | }; | ||
| 784 | |||
| 785 | pub fn raise(sig: i32) usize { | ||
| 786 | return errnoWrap(c.raise(sig)); | ||
| 787 | } | ||
| 788 | |||
| 789 | pub const Stat = c.Stat; | ||
| 790 | pub const dirent = c.dirent; | ||
| 791 | pub const timespec = c.timespec; | ||
| 792 | |||
| 793 | pub fn fstat(fd: i32, buf: *c.Stat) usize { | ||
| 794 | return errnoWrap(c.fstat(fd, buf)); | ||
| 795 | } | ||
| 796 | pub const iovec = extern struct { | ||
| 797 | iov_base: [*]u8, | ||
| 798 | iov_len: usize, | ||
| 799 | }; | ||
| 800 | |||
| 801 | pub const iovec_const = extern struct { | ||
| 802 | iov_base: [*]const u8, | ||
| 803 | iov_len: usize, | ||
| 804 | }; | ||
| 805 | |||
| 806 | // TODO avoid libc dependency | ||
| 807 | pub fn kqueue() usize { | ||
| 808 | return errnoWrap(c.kqueue()); | ||
| 809 | } | ||
| 810 | |||
| 811 | // TODO avoid libc dependency | ||
| 812 | pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { | ||
| 813 | return errnoWrap(c.kevent( | ||
| 814 | kq, | ||
| 815 | changelist.ptr, | ||
| 816 | @intCast(c_int, changelist.len), | ||
| 817 | eventlist.ptr, | ||
| 818 | @intCast(c_int, eventlist.len), | ||
| 819 | timeout, | ||
| 820 | )); | ||
| 821 | } | ||
| 822 | |||
| 823 | // TODO avoid libc dependency | ||
| 824 | pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 825 | return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); | ||
| 826 | } | ||
| 827 | |||
| 828 | // TODO avoid libc dependency | ||
| 829 | pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 830 | return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); | ||
| 831 | } | ||
| 832 | |||
| 833 | // TODO avoid libc dependency | ||
| 834 | pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { | ||
| 835 | return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); | ||
| 836 | } | ||
| 837 | |||
| 838 | // TODO avoid libc dependency | ||
| 839 | |||
| 840 | /// Takes the return value from a syscall and formats it back in the way | ||
| 841 | /// that the kernel represents it to libc. Errno was a mistake, let's make | ||
| 842 | /// it go away forever. | ||
| 843 | fn errnoWrap(value: isize) usize { | ||
| 844 | return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); | ||
| 845 | } | ||
std/os/freebsd/index.zig deleted-846| ... | @@ -1,846 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | pub use @import("errno.zig"); | ||
| 4 | |||
| 5 | const std = @import("../../index.zig"); | ||
| 6 | const c = std.c; | ||
| 7 | |||
| 8 | const assert = std.debug.assert; | ||
| 9 | const maxInt = std.math.maxInt; | ||
| 10 | pub const Kevent = c.Kevent; | ||
| 11 | |||
| 12 | pub const CTL_KERN = 1; | ||
| 13 | pub const CTL_DEBUG = 5; | ||
| 14 | |||
| 15 | pub const KERN_PROC = 14; // struct: process entries | ||
| 16 | pub const KERN_PROC_PATHNAME = 12; // path to executable | ||
| 17 | |||
| 18 | pub const PATH_MAX = 1024; | ||
| 19 | |||
| 20 | pub const STDIN_FILENO = 0; | ||
| 21 | pub const STDOUT_FILENO = 1; | ||
| 22 | pub const STDERR_FILENO = 2; | ||
| 23 | |||
| 24 | pub const PROT_NONE = 0; | ||
| 25 | pub const PROT_READ = 1; | ||
| 26 | pub const PROT_WRITE = 2; | ||
| 27 | pub const PROT_EXEC = 4; | ||
| 28 | |||
| 29 | pub const CLOCK_REALTIME = 0; | ||
| 30 | pub const CLOCK_VIRTUAL = 1; | ||
| 31 | pub const CLOCK_PROF = 2; | ||
| 32 | pub const CLOCK_MONOTONIC = 4; | ||
| 33 | pub const CLOCK_UPTIME = 5; | ||
| 34 | pub const CLOCK_UPTIME_PRECISE = 7; | ||
| 35 | pub const CLOCK_UPTIME_FAST = 8; | ||
| 36 | pub const CLOCK_REALTIME_PRECISE = 9; | ||
| 37 | pub const CLOCK_REALTIME_FAST = 10; | ||
| 38 | pub const CLOCK_MONOTONIC_PRECISE = 11; | ||
| 39 | pub const CLOCK_MONOTONIC_FAST = 12; | ||
| 40 | pub const CLOCK_SECOND = 13; | ||
| 41 | pub const CLOCK_THREAD_CPUTIME_ID = 14; | ||
| 42 | pub const CLOCK_PROCESS_CPUTIME_ID = 15; | ||
| 43 | |||
| 44 | pub const MAP_FAILED = maxInt(usize); | ||
| 45 | pub const MAP_SHARED = 0x0001; | ||
| 46 | pub const MAP_PRIVATE = 0x0002; | ||
| 47 | pub const MAP_FIXED = 0x0010; | ||
| 48 | pub const MAP_STACK = 0x0400; | ||
| 49 | pub const MAP_NOSYNC = 0x0800; | ||
| 50 | pub const MAP_ANON = 0x1000; | ||
| 51 | pub const MAP_ANONYMOUS = MAP_ANON; | ||
| 52 | pub const MAP_FILE = 0; | ||
| 53 | pub const MAP_NORESERVE = 0; | ||
| 54 | |||
| 55 | pub const MAP_GUARD = 0x00002000; | ||
| 56 | pub const MAP_EXCL = 0x00004000; | ||
| 57 | pub const MAP_NOCORE = 0x00020000; | ||
| 58 | pub const MAP_PREFAULT_READ = 0x00040000; | ||
| 59 | pub const MAP_32BIT = 0x00080000; | ||
| 60 | |||
| 61 | pub const WNOHANG = 1; | ||
| 62 | pub const WUNTRACED = 2; | ||
| 63 | pub const WSTOPPED = WUNTRACED; | ||
| 64 | pub const WCONTINUED = 4; | ||
| 65 | pub const WNOWAIT = 8; | ||
| 66 | pub const WEXITED = 16; | ||
| 67 | pub const WTRAPPED = 32; | ||
| 68 | |||
| 69 | pub const SA_ONSTACK = 0x0001; | ||
| 70 | pub const SA_RESTART = 0x0002; | ||
| 71 | pub const SA_RESETHAND = 0x0004; | ||
| 72 | pub const SA_NOCLDSTOP = 0x0008; | ||
| 73 | pub const SA_NODEFER = 0x0010; | ||
| 74 | pub const SA_NOCLDWAIT = 0x0020; | ||
| 75 | pub const SA_SIGINFO = 0x0040; | ||
| 76 | |||
| 77 | pub const SIGHUP = 1; | ||
| 78 | pub const SIGINT = 2; | ||
| 79 | pub const SIGQUIT = 3; | ||
| 80 | pub const SIGILL = 4; | ||
| 81 | pub const SIGTRAP = 5; | ||
| 82 | pub const SIGABRT = 6; | ||
| 83 | pub const SIGIOT = SIGABRT; | ||
| 84 | pub const SIGEMT = 7; | ||
| 85 | pub const SIGFPE = 8; | ||
| 86 | pub const SIGKILL = 9; | ||
| 87 | pub const SIGBUS = 10; | ||
| 88 | pub const SIGSEGV = 11; | ||
| 89 | pub const SIGSYS = 12; | ||
| 90 | pub const SIGPIPE = 13; | ||
| 91 | pub const SIGALRM = 14; | ||
| 92 | pub const SIGTERM = 15; | ||
| 93 | pub const SIGURG = 16; | ||
| 94 | pub const SIGSTOP = 17; | ||
| 95 | pub const SIGTSTP = 18; | ||
| 96 | pub const SIGCONT = 19; | ||
| 97 | pub const SIGCHLD = 20; | ||
| 98 | pub const SIGTTIN = 21; | ||
| 99 | pub const SIGTTOU = 22; | ||
| 100 | pub const SIGIO = 23; | ||
| 101 | pub const SIGXCPU = 24; | ||
| 102 | pub const SIGXFSZ = 25; | ||
| 103 | pub const SIGVTALRM = 26; | ||
| 104 | pub const SIGPROF = 27; | ||
| 105 | pub const SIGWINCH = 28; | ||
| 106 | pub const SIGINFO = 29; | ||
| 107 | pub const SIGUSR1 = 30; | ||
| 108 | pub const SIGUSR2 = 31; | ||
| 109 | pub const SIGTHR = 32; | ||
| 110 | pub const SIGLWP = SIGTHR; | ||
| 111 | pub const SIGLIBRT = 33; | ||
| 112 | |||
| 113 | pub const SIGRTMIN = 65; | ||
| 114 | pub const SIGRTMAX = 126; | ||
| 115 | |||
| 116 | // access function | ||
| 117 | pub const F_OK = 0; // test for existence of file | ||
| 118 | pub const X_OK = 1; // test for execute or search permission | ||
| 119 | pub const W_OK = 2; // test for write permission | ||
| 120 | pub const R_OK = 4; // test for read permission | ||
| 121 | |||
| 122 | |||
| 123 | pub const O_RDONLY = 0x0000; | ||
| 124 | pub const O_WRONLY = 0x0001; | ||
| 125 | pub const O_RDWR = 0x0002; | ||
| 126 | pub const O_ACCMODE = 0x0003; | ||
| 127 | |||
| 128 | pub const O_CREAT = 0x0200; | ||
| 129 | pub const O_EXCL = 0x0800; | ||
| 130 | pub const O_NOCTTY = 0x8000; | ||
| 131 | pub const O_TRUNC = 0x0400; | ||
| 132 | pub const O_APPEND = 0x0008; | ||
| 133 | pub const O_NONBLOCK = 0x0004; | ||
| 134 | pub const O_DSYNC = 0o10000; | ||
| 135 | pub const O_SYNC = 0x0080; | ||
| 136 | pub const O_RSYNC = 0o4010000; | ||
| 137 | pub const O_DIRECTORY = 0o200000; | ||
| 138 | pub const O_NOFOLLOW = 0x0100; | ||
| 139 | pub const O_CLOEXEC = 0x00100000; | ||
| 140 | |||
| 141 | pub const O_ASYNC = 0x0040; | ||
| 142 | pub const O_DIRECT = 0x00010000; | ||
| 143 | pub const O_LARGEFILE = 0; | ||
| 144 | pub const O_NOATIME = 0o1000000; | ||
| 145 | pub const O_PATH = 0o10000000; | ||
| 146 | pub const O_TMPFILE = 0o20200000; | ||
| 147 | pub const O_NDELAY = O_NONBLOCK; | ||
| 148 | |||
| 149 | pub const F_DUPFD = 0; | ||
| 150 | pub const F_GETFD = 1; | ||
| 151 | pub const F_SETFD = 2; | ||
| 152 | pub const F_GETFL = 3; | ||
| 153 | pub const F_SETFL = 4; | ||
| 154 | |||
| 155 | pub const F_SETOWN = 8; | ||
| 156 | pub const F_GETOWN = 9; | ||
| 157 | pub const F_SETSIG = 10; | ||
| 158 | pub const F_GETSIG = 11; | ||
| 159 | |||
| 160 | pub const F_GETLK = 5; | ||
| 161 | pub const F_SETLK = 6; | ||
| 162 | pub const F_SETLKW = 7; | ||
| 163 | |||
| 164 | pub const F_SETOWN_EX = 15; | ||
| 165 | pub const F_GETOWN_EX = 16; | ||
| 166 | |||
| 167 | pub const F_GETOWNER_UIDS = 17; | ||
| 168 | |||
| 169 | pub const SEEK_SET = 0; | ||
| 170 | pub const SEEK_CUR = 1; | ||
| 171 | pub const SEEK_END = 2; | ||
| 172 | |||
| 173 | pub const SIG_BLOCK = 1; | ||
| 174 | pub const SIG_UNBLOCK = 2; | ||
| 175 | pub const SIG_SETMASK = 3; | ||
| 176 | |||
| 177 | pub const SOCK_STREAM = 1; | ||
| 178 | pub const SOCK_DGRAM = 2; | ||
| 179 | pub const SOCK_RAW = 3; | ||
| 180 | pub const SOCK_RDM = 4; | ||
| 181 | pub const SOCK_SEQPACKET = 5; | ||
| 182 | |||
| 183 | pub const SOCK_CLOEXEC = 0x10000000; | ||
| 184 | pub const SOCK_NONBLOCK = 0x20000000; | ||
| 185 | |||
| 186 | pub const PROTO_ip = 0o000; | ||
| 187 | pub const PROTO_icmp = 0o001; | ||
| 188 | pub const PROTO_igmp = 0o002; | ||
| 189 | pub const PROTO_ggp = 0o003; | ||
| 190 | pub const PROTO_ipencap = 0o004; | ||
| 191 | pub const PROTO_st = 0o005; | ||
| 192 | pub const PROTO_tcp = 0o006; | ||
| 193 | pub const PROTO_egp = 0o010; | ||
| 194 | pub const PROTO_pup = 0o014; | ||
| 195 | pub const PROTO_udp = 0o021; | ||
| 196 | pub const PROTO_hmp = 0o024; | ||
| 197 | pub const PROTO_xns_idp = 0o026; | ||
| 198 | pub const PROTO_rdp = 0o033; | ||
| 199 | pub const PROTO_iso_tp4 = 0o035; | ||
| 200 | pub const PROTO_xtp = 0o044; | ||
| 201 | pub const PROTO_ddp = 0o045; | ||
| 202 | pub const PROTO_idpr_cmtp = 0o046; | ||
| 203 | pub const PROTO_ipv6 = 0o051; | ||
| 204 | pub const PROTO_ipv6_route = 0o053; | ||
| 205 | pub const PROTO_ipv6_frag = 0o054; | ||
| 206 | pub const PROTO_idrp = 0o055; | ||
| 207 | pub const PROTO_rsvp = 0o056; | ||
| 208 | pub const PROTO_gre = 0o057; | ||
| 209 | pub const PROTO_esp = 0o062; | ||
| 210 | pub const PROTO_ah = 0o063; | ||
| 211 | pub const PROTO_skip = 0o071; | ||
| 212 | pub const PROTO_ipv6_icmp = 0o072; | ||
| 213 | pub const PROTO_ipv6_nonxt = 0o073; | ||
| 214 | pub const PROTO_ipv6_opts = 0o074; | ||
| 215 | pub const PROTO_rspf = 0o111; | ||
| 216 | pub const PROTO_vmtp = 0o121; | ||
| 217 | pub const PROTO_ospf = 0o131; | ||
| 218 | pub const PROTO_ipip = 0o136; | ||
| 219 | pub const PROTO_encap = 0o142; | ||
| 220 | pub const PROTO_pim = 0o147; | ||
| 221 | pub const PROTO_raw = 0o377; | ||
| 222 | |||
| 223 | pub const PF_UNSPEC = 0; | ||
| 224 | pub const PF_LOCAL = 1; | ||
| 225 | pub const PF_UNIX = PF_LOCAL; | ||
| 226 | pub const PF_FILE = PF_LOCAL; | ||
| 227 | pub const PF_INET = 2; | ||
| 228 | pub const PF_AX25 = 3; | ||
| 229 | pub const PF_IPX = 4; | ||
| 230 | pub const PF_APPLETALK = 5; | ||
| 231 | pub const PF_NETROM = 6; | ||
| 232 | pub const PF_BRIDGE = 7; | ||
| 233 | pub const PF_ATMPVC = 8; | ||
| 234 | pub const PF_X25 = 9; | ||
| 235 | pub const PF_INET6 = 10; | ||
| 236 | pub const PF_ROSE = 11; | ||
| 237 | pub const PF_DECnet = 12; | ||
| 238 | pub const PF_NETBEUI = 13; | ||
| 239 | pub const PF_SECURITY = 14; | ||
| 240 | pub const PF_KEY = 15; | ||
| 241 | pub const PF_NETLINK = 16; | ||
| 242 | pub const PF_ROUTE = PF_NETLINK; | ||
| 243 | pub const PF_PACKET = 17; | ||
| 244 | pub const PF_ASH = 18; | ||
| 245 | pub const PF_ECONET = 19; | ||
| 246 | pub const PF_ATMSVC = 20; | ||
| 247 | pub const PF_RDS = 21; | ||
| 248 | pub const PF_SNA = 22; | ||
| 249 | pub const PF_IRDA = 23; | ||
| 250 | pub const PF_PPPOX = 24; | ||
| 251 | pub const PF_WANPIPE = 25; | ||
| 252 | pub const PF_LLC = 26; | ||
| 253 | pub const PF_IB = 27; | ||
| 254 | pub const PF_MPLS = 28; | ||
| 255 | pub const PF_CAN = 29; | ||
| 256 | pub const PF_TIPC = 30; | ||
| 257 | pub const PF_BLUETOOTH = 31; | ||
| 258 | pub const PF_IUCV = 32; | ||
| 259 | pub const PF_RXRPC = 33; | ||
| 260 | pub const PF_ISDN = 34; | ||
| 261 | pub const PF_PHONET = 35; | ||
| 262 | pub const PF_IEEE802154 = 36; | ||
| 263 | pub const PF_CAIF = 37; | ||
| 264 | pub const PF_ALG = 38; | ||
| 265 | pub const PF_NFC = 39; | ||
| 266 | pub const PF_VSOCK = 40; | ||
| 267 | pub const PF_MAX = 41; | ||
| 268 | |||
| 269 | pub const AF_UNSPEC = PF_UNSPEC; | ||
| 270 | pub const AF_LOCAL = PF_LOCAL; | ||
| 271 | pub const AF_UNIX = AF_LOCAL; | ||
| 272 | pub const AF_FILE = AF_LOCAL; | ||
| 273 | pub const AF_INET = PF_INET; | ||
| 274 | pub const AF_AX25 = PF_AX25; | ||
| 275 | pub const AF_IPX = PF_IPX; | ||
| 276 | pub const AF_APPLETALK = PF_APPLETALK; | ||
| 277 | pub const AF_NETROM = PF_NETROM; | ||
| 278 | pub const AF_BRIDGE = PF_BRIDGE; | ||
| 279 | pub const AF_ATMPVC = PF_ATMPVC; | ||
| 280 | pub const AF_X25 = PF_X25; | ||
| 281 | pub const AF_INET6 = PF_INET6; | ||
| 282 | pub const AF_ROSE = PF_ROSE; | ||
| 283 | pub const AF_DECnet = PF_DECnet; | ||
| 284 | pub const AF_NETBEUI = PF_NETBEUI; | ||
| 285 | pub const AF_SECURITY = PF_SECURITY; | ||
| 286 | pub const AF_KEY = PF_KEY; | ||
| 287 | pub const AF_NETLINK = PF_NETLINK; | ||
| 288 | pub const AF_ROUTE = PF_ROUTE; | ||
| 289 | pub const AF_PACKET = PF_PACKET; | ||
| 290 | pub const AF_ASH = PF_ASH; | ||
| 291 | pub const AF_ECONET = PF_ECONET; | ||
| 292 | pub const AF_ATMSVC = PF_ATMSVC; | ||
| 293 | pub const AF_RDS = PF_RDS; | ||
| 294 | pub const AF_SNA = PF_SNA; | ||
| 295 | pub const AF_IRDA = PF_IRDA; | ||
| 296 | pub const AF_PPPOX = PF_PPPOX; | ||
| 297 | pub const AF_WANPIPE = PF_WANPIPE; | ||
| 298 | pub const AF_LLC = PF_LLC; | ||
| 299 | pub const AF_IB = PF_IB; | ||
| 300 | pub const AF_MPLS = PF_MPLS; | ||
| 301 | pub const AF_CAN = PF_CAN; | ||
| 302 | pub const AF_TIPC = PF_TIPC; | ||
| 303 | pub const AF_BLUETOOTH = PF_BLUETOOTH; | ||
| 304 | pub const AF_IUCV = PF_IUCV; | ||
| 305 | pub const AF_RXRPC = PF_RXRPC; | ||
| 306 | pub const AF_ISDN = PF_ISDN; | ||
| 307 | pub const AF_PHONET = PF_PHONET; | ||
| 308 | pub const AF_IEEE802154 = PF_IEEE802154; | ||
| 309 | pub const AF_CAIF = PF_CAIF; | ||
| 310 | pub const AF_ALG = PF_ALG; | ||
| 311 | pub const AF_NFC = PF_NFC; | ||
| 312 | pub const AF_VSOCK = PF_VSOCK; | ||
| 313 | pub const AF_MAX = PF_MAX; | ||
| 314 | |||
| 315 | pub const DT_UNKNOWN = 0; | ||
| 316 | pub const DT_FIFO = 1; | ||
| 317 | pub const DT_CHR = 2; | ||
| 318 | pub const DT_DIR = 4; | ||
| 319 | pub const DT_BLK = 6; | ||
| 320 | pub const DT_REG = 8; | ||
| 321 | pub const DT_LNK = 10; | ||
| 322 | pub const DT_SOCK = 12; | ||
| 323 | pub const DT_WHT = 14; | ||
| 324 | |||
| 325 | /// add event to kq (implies enable) | ||
| 326 | pub const EV_ADD = 0x0001; | ||
| 327 | |||
| 328 | /// delete event from kq | ||
| 329 | pub const EV_DELETE = 0x0002; | ||
| 330 | |||
| 331 | /// enable event | ||
| 332 | pub const EV_ENABLE = 0x0004; | ||
| 333 | |||
| 334 | /// disable event (not reported) | ||
| 335 | pub const EV_DISABLE = 0x0008; | ||
| 336 | |||
| 337 | /// only report one occurrence | ||
| 338 | pub const EV_ONESHOT = 0x0010; | ||
| 339 | |||
| 340 | /// clear event state after reporting | ||
| 341 | pub const EV_CLEAR = 0x0020; | ||
| 342 | |||
| 343 | /// force immediate event output | ||
| 344 | /// ... with or without EV_ERROR | ||
| 345 | /// ... use KEVENT_FLAG_ERROR_EVENTS | ||
| 346 | /// on syscalls supporting flags | ||
| 347 | pub const EV_RECEIPT = 0x0040; | ||
| 348 | |||
| 349 | /// disable event after reporting | ||
| 350 | pub const EV_DISPATCH = 0x0080; | ||
| 351 | |||
| 352 | pub const EVFILT_READ = -1; | ||
| 353 | pub const EVFILT_WRITE = -2; | ||
| 354 | |||
| 355 | /// attached to aio requests | ||
| 356 | pub const EVFILT_AIO = -3; | ||
| 357 | |||
| 358 | /// attached to vnodes | ||
| 359 | pub const EVFILT_VNODE = -4; | ||
| 360 | |||
| 361 | /// attached to struct proc | ||
| 362 | pub const EVFILT_PROC = -5; | ||
| 363 | |||
| 364 | /// attached to struct proc | ||
| 365 | pub const EVFILT_SIGNAL = -6; | ||
| 366 | |||
| 367 | /// timers | ||
| 368 | pub const EVFILT_TIMER = -7; | ||
| 369 | |||
| 370 | /// Process descriptors | ||
| 371 | pub const EVFILT_PROCDESC = -8; | ||
| 372 | |||
| 373 | /// Filesystem events | ||
| 374 | pub const EVFILT_FS = -9; | ||
| 375 | |||
| 376 | pub const EVFILT_LIO = -10; | ||
| 377 | |||
| 378 | /// User events | ||
| 379 | pub const EVFILT_USER = -11; | ||
| 380 | |||
| 381 | /// Sendfile events | ||
| 382 | pub const EVFILT_SENDFILE = -12; | ||
| 383 | |||
| 384 | pub const EVFILT_EMPTY = -13; | ||
| 385 | |||
| 386 | /// On input, NOTE_TRIGGER causes the event to be triggered for output. | ||
| 387 | pub const NOTE_TRIGGER = 0x01000000; | ||
| 388 | |||
| 389 | /// ignore input fflags | ||
| 390 | pub const NOTE_FFNOP = 0x00000000; | ||
| 391 | |||
| 392 | /// and fflags | ||
| 393 | pub const NOTE_FFAND = 0x40000000; | ||
| 394 | |||
| 395 | /// or fflags | ||
| 396 | pub const NOTE_FFOR = 0x80000000; | ||
| 397 | |||
| 398 | /// copy fflags | ||
| 399 | pub const NOTE_FFCOPY = 0xc0000000; | ||
| 400 | |||
| 401 | /// mask for operations | ||
| 402 | pub const NOTE_FFCTRLMASK = 0xc0000000; | ||
| 403 | pub const NOTE_FFLAGSMASK = 0x00ffffff; | ||
| 404 | |||
| 405 | /// low water mark | ||
| 406 | pub const NOTE_LOWAT = 0x00000001; | ||
| 407 | |||
| 408 | /// behave like poll() | ||
| 409 | pub const NOTE_FILE_POLL = 0x00000002; | ||
| 410 | |||
| 411 | /// vnode was removed | ||
| 412 | pub const NOTE_DELETE = 0x00000001; | ||
| 413 | |||
| 414 | /// data contents changed | ||
| 415 | pub const NOTE_WRITE = 0x00000002; | ||
| 416 | |||
| 417 | /// size increased | ||
| 418 | pub const NOTE_EXTEND = 0x00000004; | ||
| 419 | |||
| 420 | /// attributes changed | ||
| 421 | pub const NOTE_ATTRIB = 0x00000008; | ||
| 422 | |||
| 423 | /// link count changed | ||
| 424 | pub const NOTE_LINK = 0x00000010; | ||
| 425 | |||
| 426 | /// vnode was renamed | ||
| 427 | pub const NOTE_RENAME = 0x00000020; | ||
| 428 | |||
| 429 | /// vnode access was revoked | ||
| 430 | pub const NOTE_REVOKE = 0x00000040; | ||
| 431 | |||
| 432 | /// vnode was opened | ||
| 433 | pub const NOTE_OPEN = 0x00000080; | ||
| 434 | |||
| 435 | /// file closed, fd did not allow write | ||
| 436 | pub const NOTE_CLOSE = 0x00000100; | ||
| 437 | |||
| 438 | /// file closed, fd did allow write | ||
| 439 | pub const NOTE_CLOSE_WRITE = 0x00000200; | ||
| 440 | |||
| 441 | /// file was read | ||
| 442 | pub const NOTE_READ = 0x00000400; | ||
| 443 | |||
| 444 | /// process exited | ||
| 445 | pub const NOTE_EXIT = 0x80000000; | ||
| 446 | |||
| 447 | /// process forked | ||
| 448 | pub const NOTE_FORK = 0x40000000; | ||
| 449 | |||
| 450 | /// process exec'd | ||
| 451 | pub const NOTE_EXEC = 0x20000000; | ||
| 452 | |||
| 453 | /// mask for signal & exit status | ||
| 454 | pub const NOTE_PDATAMASK = 0x000fffff; | ||
| 455 | pub const NOTE_PCTRLMASK = (~NOTE_PDATAMASK); | ||
| 456 | |||
| 457 | /// data is seconds | ||
| 458 | pub const NOTE_SECONDS = 0x00000001; | ||
| 459 | |||
| 460 | /// data is milliseconds | ||
| 461 | pub const NOTE_MSECONDS = 0x00000002; | ||
| 462 | |||
| 463 | /// data is microseconds | ||
| 464 | pub const NOTE_USECONDS = 0x00000004; | ||
| 465 | |||
| 466 | /// data is nanoseconds | ||
| 467 | pub const NOTE_NSECONDS = 0x00000008; | ||
| 468 | |||
| 469 | /// timeout is absolute | ||
| 470 | pub const NOTE_ABSTIME = 0x00000010; | ||
| 471 | |||
| 472 | pub const TCGETS = 0x5401; | ||
| 473 | pub const TCSETS = 0x5402; | ||
| 474 | pub const TCSETSW = 0x5403; | ||
| 475 | pub const TCSETSF = 0x5404; | ||
| 476 | pub const TCGETA = 0x5405; | ||
| 477 | pub const TCSETA = 0x5406; | ||
| 478 | pub const TCSETAW = 0x5407; | ||
| 479 | pub const TCSETAF = 0x5408; | ||
| 480 | pub const TCSBRK = 0x5409; | ||
| 481 | pub const TCXONC = 0x540A; | ||
| 482 | pub const TCFLSH = 0x540B; | ||
| 483 | pub const TIOCEXCL = 0x540C; | ||
| 484 | pub const TIOCNXCL = 0x540D; | ||
| 485 | pub const TIOCSCTTY = 0x540E; | ||
| 486 | pub const TIOCGPGRP = 0x540F; | ||
| 487 | pub const TIOCSPGRP = 0x5410; | ||
| 488 | pub const TIOCOUTQ = 0x5411; | ||
| 489 | pub const TIOCSTI = 0x5412; | ||
| 490 | pub const TIOCGWINSZ = 0x5413; | ||
| 491 | pub const TIOCSWINSZ = 0x5414; | ||
| 492 | pub const TIOCMGET = 0x5415; | ||
| 493 | pub const TIOCMBIS = 0x5416; | ||
| 494 | pub const TIOCMBIC = 0x5417; | ||
| 495 | pub const TIOCMSET = 0x5418; | ||
| 496 | pub const TIOCGSOFTCAR = 0x5419; | ||
| 497 | pub const TIOCSSOFTCAR = 0x541A; | ||
| 498 | pub const FIONREAD = 0x541B; | ||
| 499 | pub const TIOCINQ = FIONREAD; | ||
| 500 | pub const TIOCLINUX = 0x541C; | ||
| 501 | pub const TIOCCONS = 0x541D; | ||
| 502 | pub const TIOCGSERIAL = 0x541E; | ||
| 503 | pub const TIOCSSERIAL = 0x541F; | ||
| 504 | pub const TIOCPKT = 0x5420; | ||
| 505 | pub const FIONBIO = 0x5421; | ||
| 506 | pub const TIOCNOTTY = 0x5422; | ||
| 507 | pub const TIOCSETD = 0x5423; | ||
| 508 | pub const TIOCGETD = 0x5424; | ||
| 509 | pub const TCSBRKP = 0x5425; | ||
| 510 | pub const TIOCSBRK = 0x5427; | ||
| 511 | pub const TIOCCBRK = 0x5428; | ||
| 512 | pub const TIOCGSID = 0x5429; | ||
| 513 | pub const TIOCGRS485 = 0x542E; | ||
| 514 | pub const TIOCSRS485 = 0x542F; | ||
| 515 | pub const TIOCGPTN = 0x80045430; | ||
| 516 | pub const TIOCSPTLCK = 0x40045431; | ||
| 517 | pub const TIOCGDEV = 0x80045432; | ||
| 518 | pub const TCGETX = 0x5432; | ||
| 519 | pub const TCSETX = 0x5433; | ||
| 520 | pub const TCSETXF = 0x5434; | ||
| 521 | pub const TCSETXW = 0x5435; | ||
| 522 | pub const TIOCSIG = 0x40045436; | ||
| 523 | pub const TIOCVHANGUP = 0x5437; | ||
| 524 | pub const TIOCGPKT = 0x80045438; | ||
| 525 | pub const TIOCGPTLCK = 0x80045439; | ||
| 526 | pub const TIOCGEXCL = 0x80045440; | ||
| 527 | |||
| 528 | pub const sockaddr = c.sockaddr; | ||
| 529 | pub const sockaddr_in = c.sockaddr_in; | ||
| 530 | pub const sockaddr_in6 = c.sockaddr_in6; | ||
| 531 | |||
| 532 | fn unsigned(s: i32) u32 { | ||
| 533 | return @bitCast(u32, s); | ||
| 534 | } | ||
| 535 | fn signed(s: u32) i32 { | ||
| 536 | return @bitCast(i32, s); | ||
| 537 | } | ||
| 538 | pub fn WEXITSTATUS(s: i32) i32 { | ||
| 539 | return signed((unsigned(s) & 0xff00) >> 8); | ||
| 540 | } | ||
| 541 | pub fn WTERMSIG(s: i32) i32 { | ||
| 542 | return signed(unsigned(s) & 0x7f); | ||
| 543 | } | ||
| 544 | pub fn WSTOPSIG(s: i32) i32 { | ||
| 545 | return WEXITSTATUS(s); | ||
| 546 | } | ||
| 547 | pub fn WIFEXITED(s: i32) bool { | ||
| 548 | return WTERMSIG(s) == 0; | ||
| 549 | } | ||
| 550 | pub fn WIFSTOPPED(s: i32) bool { | ||
| 551 | return @intCast(u16, (((unsigned(s) & 0xffff) *% 0x10001) >> 8)) > 0x7f00; | ||
| 552 | } | ||
| 553 | pub fn WIFSIGNALED(s: i32) bool { | ||
| 554 | return (unsigned(s) & 0xffff) -% 1 < 0xff; | ||
| 555 | } | ||
| 556 | |||
| 557 | pub const winsize = extern struct { | ||
| 558 | ws_row: u16, | ||
| 559 | ws_col: u16, | ||
| 560 | ws_xpixel: u16, | ||
| 561 | ws_ypixel: u16, | ||
| 562 | }; | ||
| 563 | |||
| 564 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 565 | pub fn getErrno(r: usize) usize { | ||
| 566 | const signed_r = @bitCast(isize, r); | ||
| 567 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 568 | } | ||
| 569 | |||
| 570 | pub fn dup2(old: i32, new: i32) usize { | ||
| 571 | return errnoWrap(c.dup2(old, new)); | ||
| 572 | } | ||
| 573 | |||
| 574 | pub fn chdir(path: [*]const u8) usize { | ||
| 575 | return errnoWrap(c.chdir(path)); | ||
| 576 | } | ||
| 577 | |||
| 578 | pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { | ||
| 579 | return errnoWrap(c.execve(path, argv, envp)); | ||
| 580 | } | ||
| 581 | |||
| 582 | pub fn fork() usize { | ||
| 583 | return errnoWrap(c.fork()); | ||
| 584 | } | ||
| 585 | |||
| 586 | pub fn access(path: [*]const u8, mode: u32) usize { | ||
| 587 | return errnoWrap(c.access(path, mode)); | ||
| 588 | } | ||
| 589 | |||
| 590 | pub fn getcwd(buf: [*]u8, size: usize) usize { | ||
| 591 | return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 592 | } | ||
| 593 | |||
| 594 | pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { | ||
| 595 | return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); | ||
| 596 | } | ||
| 597 | |||
| 598 | pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { | ||
| 599 | return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); | ||
| 600 | } | ||
| 601 | |||
| 602 | pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { | ||
| 603 | return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 604 | } | ||
| 605 | |||
| 606 | pub fn isatty(fd: i32) bool { | ||
| 607 | return c.isatty(fd) != 0; | ||
| 608 | } | ||
| 609 | |||
| 610 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 611 | return errnoWrap(c.readlink(path, buf_ptr, buf_len)); | ||
| 612 | } | ||
| 613 | |||
| 614 | pub fn mkdir(path: [*]const u8, mode: u32) usize { | ||
| 615 | return errnoWrap(c.mkdir(path, mode)); | ||
| 616 | } | ||
| 617 | |||
| 618 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | ||
| 619 | const ptr_result = c.mmap( | ||
| 620 | @ptrCast(?*c_void, address), | ||
| 621 | length, | ||
| 622 | @bitCast(c_int, @intCast(c_uint, prot)), | ||
| 623 | @bitCast(c_int, c_uint(flags)), | ||
| 624 | fd, | ||
| 625 | offset, | ||
| 626 | ); | ||
| 627 | const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); | ||
| 628 | return errnoWrap(isize_result); | ||
| 629 | } | ||
| 630 | |||
| 631 | pub fn munmap(address: usize, length: usize) usize { | ||
| 632 | return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); | ||
| 633 | } | ||
| 634 | |||
| 635 | pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { | ||
| 636 | return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); | ||
| 637 | } | ||
| 638 | |||
| 639 | pub fn rmdir(path: [*]const u8) usize { | ||
| 640 | return errnoWrap(c.rmdir(path)); | ||
| 641 | } | ||
| 642 | |||
| 643 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | ||
| 644 | return errnoWrap(c.symlink(existing, new)); | ||
| 645 | } | ||
| 646 | |||
| 647 | pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { | ||
| 648 | return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); | ||
| 649 | } | ||
| 650 | |||
| 651 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { | ||
| 652 | return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 653 | } | ||
| 654 | |||
| 655 | pub fn pipe(fd: *[2]i32) usize { | ||
| 656 | return pipe2(fd, 0); | ||
| 657 | } | ||
| 658 | |||
| 659 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | ||
| 660 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 661 | return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); | ||
| 662 | } | ||
| 663 | |||
| 664 | pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { | ||
| 665 | return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); | ||
| 666 | } | ||
| 667 | |||
| 668 | pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { | ||
| 669 | return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); | ||
| 670 | } | ||
| 671 | |||
| 672 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { | ||
| 673 | return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 674 | } | ||
| 675 | |||
| 676 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | ||
| 677 | return errnoWrap(c.rename(old, new)); | ||
| 678 | } | ||
| 679 | |||
| 680 | pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { | ||
| 681 | return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); | ||
| 682 | } | ||
| 683 | |||
| 684 | pub fn create(path: [*]const u8, perm: usize) usize { | ||
| 685 | return arch.syscall2(SYS_creat, @ptrToInt(path), perm); | ||
| 686 | } | ||
| 687 | |||
| 688 | pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { | ||
| 689 | return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); | ||
| 690 | } | ||
| 691 | |||
| 692 | pub fn close(fd: i32) usize { | ||
| 693 | return errnoWrap(c.close(fd)); | ||
| 694 | } | ||
| 695 | |||
| 696 | pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { | ||
| 697 | return errnoWrap(c.lseek(fd, offset, whence)); | ||
| 698 | } | ||
| 699 | |||
| 700 | pub fn exit(code: i32) noreturn { | ||
| 701 | c.exit(code); | ||
| 702 | } | ||
| 703 | |||
| 704 | pub fn kill(pid: i32, sig: i32) usize { | ||
| 705 | return errnoWrap(c.kill(pid, sig)); | ||
| 706 | } | ||
| 707 | |||
| 708 | pub fn unlink(path: [*]const u8) usize { | ||
| 709 | return errnoWrap(c.unlink(path)); | ||
| 710 | } | ||
| 711 | |||
| 712 | pub fn waitpid(pid: i32, status: *i32, options: u32) usize { | ||
| 713 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 714 | return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); | ||
| 715 | } | ||
| 716 | |||
| 717 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | ||
| 718 | return errnoWrap(c.nanosleep(req, rem)); | ||
| 719 | } | ||
| 720 | |||
| 721 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | ||
| 722 | return errnoWrap(c.clock_gettime(clk_id, tp)); | ||
| 723 | } | ||
| 724 | |||
| 725 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | ||
| 726 | return errnoWrap(c.clock_getres(clk_id, tp)); | ||
| 727 | } | ||
| 728 | |||
| 729 | pub fn setuid(uid: u32) usize { | ||
| 730 | return errnoWrap(c.setuid(uid)); | ||
| 731 | } | ||
| 732 | |||
| 733 | pub fn setgid(gid: u32) usize { | ||
| 734 | return errnoWrap(c.setgid(gid)); | ||
| 735 | } | ||
| 736 | |||
| 737 | pub fn setreuid(ruid: u32, euid: u32) usize { | ||
| 738 | return errnoWrap(c.setreuid(ruid, euid)); | ||
| 739 | } | ||
| 740 | |||
| 741 | pub fn setregid(rgid: u32, egid: u32) usize { | ||
| 742 | return errnoWrap(c.setregid(rgid, egid)); | ||
| 743 | } | ||
| 744 | |||
| 745 | const NSIG = 32; | ||
| 746 | |||
| 747 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | ||
| 748 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ||
| 749 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | ||
| 750 | |||
| 751 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | ||
| 752 | pub const Sigaction = extern struct { | ||
| 753 | /// signal handler | ||
| 754 | __sigaction_u: extern union { | ||
| 755 | __sa_handler: extern fn (i32) void, | ||
| 756 | __sa_sigaction: extern fn (i32, *__siginfo, usize) void, | ||
| 757 | }, | ||
| 758 | |||
| 759 | /// see signal options | ||
| 760 | sa_flags: u32, | ||
| 761 | |||
| 762 | /// signal mask to apply | ||
| 763 | sa_mask: sigset_t, | ||
| 764 | }; | ||
| 765 | |||
| 766 | pub const _SIG_WORDS = 4; | ||
| 767 | pub const _SIG_MAXSIG = 128; | ||
| 768 | |||
| 769 | pub inline fn _SIG_IDX(sig: usize) usize { | ||
| 770 | return sig - 1; | ||
| 771 | } | ||
| 772 | pub inline fn _SIG_WORD(sig: usize) usize { | ||
| 773 | return_SIG_IDX(sig) >> 5; | ||
| 774 | } | ||
| 775 | pub inline fn _SIG_BIT(sig: usize) usize { | ||
| 776 | return 1 << (_SIG_IDX(sig) & 31); | ||
| 777 | } | ||
| 778 | pub inline fn _SIG_VALID(sig: usize) usize { | ||
| 779 | return sig <= _SIG_MAXSIG and sig > 0; | ||
| 780 | } | ||
| 781 | |||
| 782 | pub const sigset_t = extern struct { | ||
| 783 | __bits: [_SIG_WORDS]u32, | ||
| 784 | }; | ||
| 785 | |||
| 786 | pub fn raise(sig: i32) usize { | ||
| 787 | return errnoWrap(c.raise(sig)); | ||
| 788 | } | ||
| 789 | |||
| 790 | pub const Stat = c.Stat; | ||
| 791 | pub const dirent = c.dirent; | ||
| 792 | pub const timespec = c.timespec; | ||
| 793 | |||
| 794 | pub fn fstat(fd: i32, buf: *c.Stat) usize { | ||
| 795 | return errnoWrap(c.fstat(fd, buf)); | ||
| 796 | } | ||
| 797 | pub const iovec = extern struct { | ||
| 798 | iov_base: [*]u8, | ||
| 799 | iov_len: usize, | ||
| 800 | }; | ||
| 801 | |||
| 802 | pub const iovec_const = extern struct { | ||
| 803 | iov_base: [*]const u8, | ||
| 804 | iov_len: usize, | ||
| 805 | }; | ||
| 806 | |||
| 807 | // TODO avoid libc dependency | ||
| 808 | pub fn kqueue() usize { | ||
| 809 | return errnoWrap(c.kqueue()); | ||
| 810 | } | ||
| 811 | |||
| 812 | // TODO avoid libc dependency | ||
| 813 | pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { | ||
| 814 | return errnoWrap(c.kevent( | ||
| 815 | kq, | ||
| 816 | changelist.ptr, | ||
| 817 | @intCast(c_int, changelist.len), | ||
| 818 | eventlist.ptr, | ||
| 819 | @intCast(c_int, eventlist.len), | ||
| 820 | timeout, | ||
| 821 | )); | ||
| 822 | } | ||
| 823 | |||
| 824 | // TODO avoid libc dependency | ||
| 825 | pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 826 | return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); | ||
| 827 | } | ||
| 828 | |||
| 829 | // TODO avoid libc dependency | ||
| 830 | pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 831 | return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); | ||
| 832 | } | ||
| 833 | |||
| 834 | // TODO avoid libc dependency | ||
| 835 | pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { | ||
| 836 | return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); | ||
| 837 | } | ||
| 838 | |||
| 839 | // TODO avoid libc dependency | ||
| 840 | |||
| 841 | /// Takes the return value from a syscall and formats it back in the way | ||
| 842 | /// that the kernel represents it to libc. Errno was a mistake, let's make | ||
| 843 | /// it go away forever. | ||
| 844 | fn errnoWrap(value: isize) usize { | ||
| 845 | return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); | ||
| 846 | } | ||
std/os/get_app_data_dir.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const unicode = std.unicode; | 3 | const unicode = std.unicode; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/os/get_user_id.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const Os = builtin.Os; | 2 | const Os = builtin.Os; |
| 3 | const os = @import("index.zig"); | 3 | const os = @import("../os.zig"); |
| 4 | const io = @import("../io.zig"); | 4 | const io = @import("../io.zig"); |
| 5 | 5 | ||
| 6 | pub const UserInfo = struct { | 6 | pub const UserInfo = struct { |
std/os/index.zig deleted-3432| ... | @@ -1,3432 +0,0 @@ | ||
| 1 | const std = @import("../index.zig"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const Os = builtin.Os; | ||
| 4 | const is_windows = builtin.os == Os.windows; | ||
| 5 | const is_posix = switch (builtin.os) { | ||
| 6 | builtin.Os.linux, builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => true, | ||
| 7 | else => false, | ||
| 8 | }; | ||
| 9 | const os = @This(); | ||
| 10 | |||
| 11 | comptime { | ||
| 12 | assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir | ||
| 13 | } | ||
| 14 | |||
| 15 | test "std.os" { | ||
| 16 | _ = @import("child_process.zig"); | ||
| 17 | _ = @import("darwin.zig"); | ||
| 18 | _ = @import("darwin/errno.zig"); | ||
| 19 | _ = @import("get_user_id.zig"); | ||
| 20 | _ = @import("linux/index.zig"); | ||
| 21 | _ = @import("path.zig"); | ||
| 22 | _ = @import("test.zig"); | ||
| 23 | _ = @import("time.zig"); | ||
| 24 | _ = @import("windows/index.zig"); | ||
| 25 | _ = @import("uefi.zig"); | ||
| 26 | _ = @import("get_app_data_dir.zig"); | ||
| 27 | } | ||
| 28 | |||
| 29 | pub const windows = @import("windows/index.zig"); | ||
| 30 | pub const darwin = @import("darwin.zig"); | ||
| 31 | pub const linux = @import("linux/index.zig"); | ||
| 32 | pub const freebsd = @import("freebsd/index.zig"); | ||
| 33 | pub const netbsd = @import("netbsd/index.zig"); | ||
| 34 | pub const zen = @import("zen.zig"); | ||
| 35 | pub const uefi = @import("uefi.zig"); | ||
| 36 | |||
| 37 | pub const posix = switch (builtin.os) { | ||
| 38 | Os.linux => linux, | ||
| 39 | Os.macosx, Os.ios => darwin, | ||
| 40 | Os.freebsd => freebsd, | ||
| 41 | Os.netbsd => netbsd, | ||
| 42 | Os.zen => zen, | ||
| 43 | else => @compileError("Unsupported OS"), | ||
| 44 | }; | ||
| 45 | |||
| 46 | pub const net = @import("net.zig"); | ||
| 47 | |||
| 48 | pub const ChildProcess = @import("child_process.zig").ChildProcess; | ||
| 49 | pub const path = @import("path.zig"); | ||
| 50 | pub const File = @import("file.zig").File; | ||
| 51 | pub const time = @import("time.zig"); | ||
| 52 | |||
| 53 | pub const page_size = 4 * 1024; | ||
| 54 | pub const MAX_PATH_BYTES = switch (builtin.os) { | ||
| 55 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => posix.PATH_MAX, | ||
| 56 | // Each UTF-16LE character may be expanded to 3 UTF-8 bytes. | ||
| 57 | // If it would require 4 UTF-8 bytes, then there would be a surrogate | ||
| 58 | // pair in the UTF-16LE, and we (over)account 3 bytes for it that way. | ||
| 59 | // +1 for the null byte at the end, which can be encoded in 1 byte. | ||
| 60 | Os.windows => windows_util.PATH_MAX_WIDE * 3 + 1, | ||
| 61 | else => @compileError("Unsupported OS"), | ||
| 62 | }; | ||
| 63 | |||
| 64 | pub const UserInfo = @import("get_user_id.zig").UserInfo; | ||
| 65 | pub const getUserInfo = @import("get_user_id.zig").getUserInfo; | ||
| 66 | |||
| 67 | const windows_util = @import("windows/util.zig"); | ||
| 68 | pub const windowsWaitSingle = windows_util.windowsWaitSingle; | ||
| 69 | pub const windowsWrite = windows_util.windowsWrite; | ||
| 70 | pub const windowsIsCygwinPty = windows_util.windowsIsCygwinPty; | ||
| 71 | pub const windowsOpen = windows_util.windowsOpen; | ||
| 72 | pub const windowsOpenW = windows_util.windowsOpenW; | ||
| 73 | pub const createWindowsEnvBlock = windows_util.createWindowsEnvBlock; | ||
| 74 | |||
| 75 | pub const WindowsCreateIoCompletionPortError = windows_util.WindowsCreateIoCompletionPortError; | ||
| 76 | pub const windowsCreateIoCompletionPort = windows_util.windowsCreateIoCompletionPort; | ||
| 77 | |||
| 78 | pub const WindowsPostQueuedCompletionStatusError = windows_util.WindowsPostQueuedCompletionStatusError; | ||
| 79 | pub const windowsPostQueuedCompletionStatus = windows_util.windowsPostQueuedCompletionStatus; | ||
| 80 | |||
| 81 | pub const WindowsWaitResult = windows_util.WindowsWaitResult; | ||
| 82 | pub const windowsGetQueuedCompletionStatus = windows_util.windowsGetQueuedCompletionStatus; | ||
| 83 | |||
| 84 | pub const WindowsWaitError = windows_util.WaitError; | ||
| 85 | pub const WindowsOpenError = windows_util.OpenError; | ||
| 86 | pub const WindowsWriteError = windows_util.WriteError; | ||
| 87 | pub const WindowsReadError = windows_util.ReadError; | ||
| 88 | |||
| 89 | pub const FileHandle = if (is_windows) windows.HANDLE else i32; | ||
| 90 | |||
| 91 | pub const getAppDataDir = @import("get_app_data_dir.zig").getAppDataDir; | ||
| 92 | pub const GetAppDataDirError = @import("get_app_data_dir.zig").GetAppDataDirError; | ||
| 93 | |||
| 94 | const debug = std.debug; | ||
| 95 | const assert = debug.assert; | ||
| 96 | const testing = std.testing; | ||
| 97 | |||
| 98 | const c = std.c; | ||
| 99 | |||
| 100 | const mem = std.mem; | ||
| 101 | const Allocator = mem.Allocator; | ||
| 102 | |||
| 103 | const BufMap = std.BufMap; | ||
| 104 | const cstr = std.cstr; | ||
| 105 | |||
| 106 | const io = std.io; | ||
| 107 | const base64 = std.base64; | ||
| 108 | const ArrayList = std.ArrayList; | ||
| 109 | const Buffer = std.Buffer; | ||
| 110 | const math = std.math; | ||
| 111 | |||
| 112 | /// Fills `buf` with random bytes. If linking against libc, this calls the | ||
| 113 | /// appropriate OS-specific library call. Otherwise it uses the zig standard | ||
| 114 | /// library implementation. | ||
| 115 | pub fn getRandomBytes(buf: []u8) !void { | ||
| 116 | switch (builtin.os) { | ||
| 117 | Os.linux => while (true) { | ||
| 118 | // TODO check libc version and potentially call c.getrandom. | ||
| 119 | // See #397 | ||
| 120 | const errno = posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)); | ||
| 121 | switch (errno) { | ||
| 122 | 0 => return, | ||
| 123 | posix.EINVAL => unreachable, | ||
| 124 | posix.EFAULT => unreachable, | ||
| 125 | posix.EINTR => continue, | ||
| 126 | posix.ENOSYS => return getRandomBytesDevURandom(buf), | ||
| 127 | else => return unexpectedErrorPosix(errno), | ||
| 128 | } | ||
| 129 | }, | ||
| 130 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => return getRandomBytesDevURandom(buf), | ||
| 131 | Os.windows => { | ||
| 132 | // Call RtlGenRandom() instead of CryptGetRandom() on Windows | ||
| 133 | // https://github.com/rust-lang-nursery/rand/issues/111 | ||
| 134 | // https://bugzilla.mozilla.org/show_bug.cgi?id=504270 | ||
| 135 | if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) { | ||
| 136 | const err = windows.GetLastError(); | ||
| 137 | return switch (err) { | ||
| 138 | else => unexpectedErrorWindows(err), | ||
| 139 | }; | ||
| 140 | } | ||
| 141 | }, | ||
| 142 | Os.zen => { | ||
| 143 | const randomness = []u8{ 42, 1, 7, 12, 22, 17, 99, 16, 26, 87, 41, 45 }; | ||
| 144 | var i: usize = 0; | ||
| 145 | while (i < buf.len) : (i += 1) { | ||
| 146 | if (i > randomness.len) return error.Unknown; | ||
| 147 | buf[i] = randomness[i]; | ||
| 148 | } | ||
| 149 | }, | ||
| 150 | else => @compileError("Unsupported OS"), | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | fn getRandomBytesDevURandom(buf: []u8) !void { | ||
| 155 | const fd = try posixOpenC(c"/dev/urandom", posix.O_RDONLY | posix.O_CLOEXEC, 0); | ||
| 156 | defer close(fd); | ||
| 157 | |||
| 158 | const stream = &File.openHandle(fd).inStream().stream; | ||
| 159 | stream.readNoEof(buf) catch |err| switch (err) { | ||
| 160 | error.EndOfStream => unreachable, | ||
| 161 | error.OperationAborted => unreachable, | ||
| 162 | error.BrokenPipe => unreachable, | ||
| 163 | error.Unexpected => return error.Unexpected, | ||
| 164 | error.InputOutput => return error.Unexpected, | ||
| 165 | error.SystemResources => return error.Unexpected, | ||
| 166 | error.IsDir => unreachable, | ||
| 167 | }; | ||
| 168 | } | ||
| 169 | |||
| 170 | test "os.getRandomBytes" { | ||
| 171 | var buf_a: [50]u8 = undefined; | ||
| 172 | var buf_b: [50]u8 = undefined; | ||
| 173 | // Call Twice | ||
| 174 | try getRandomBytes(buf_a[0..]); | ||
| 175 | try getRandomBytes(buf_b[0..]); | ||
| 176 | |||
| 177 | // Check if random (not 100% conclusive) | ||
| 178 | testing.expect(!mem.eql(u8, buf_a, buf_b)); | ||
| 179 | } | ||
| 180 | |||
| 181 | /// Raises a signal in the current kernel thread, ending its execution. | ||
| 182 | /// If linking against libc, this calls the abort() libc function. Otherwise | ||
| 183 | /// it uses the zig standard library implementation. | ||
| 184 | pub fn abort() noreturn { | ||
| 185 | @setCold(true); | ||
| 186 | if (builtin.link_libc) { | ||
| 187 | c.abort(); | ||
| 188 | } | ||
| 189 | switch (builtin.os) { | ||
| 190 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 191 | _ = posix.raise(posix.SIGABRT); | ||
| 192 | _ = posix.raise(posix.SIGKILL); | ||
| 193 | while (true) {} | ||
| 194 | }, | ||
| 195 | Os.windows => { | ||
| 196 | if (builtin.mode == builtin.Mode.Debug) { | ||
| 197 | @breakpoint(); | ||
| 198 | } | ||
| 199 | windows.ExitProcess(3); | ||
| 200 | }, | ||
| 201 | Os.uefi => { | ||
| 202 | // TODO there's gotta be a better thing to do here than loop forever | ||
| 203 | while (true) {} | ||
| 204 | }, | ||
| 205 | else => @compileError("Unsupported OS"), | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Exits the program cleanly with the specified status code. | ||
| 210 | pub fn exit(status: u8) noreturn { | ||
| 211 | @setCold(true); | ||
| 212 | if (builtin.link_libc) { | ||
| 213 | c.exit(status); | ||
| 214 | } | ||
| 215 | switch (builtin.os) { | ||
| 216 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 217 | posix.exit(status); | ||
| 218 | }, | ||
| 219 | Os.windows => { | ||
| 220 | windows.ExitProcess(status); | ||
| 221 | }, | ||
| 222 | else => @compileError("Unsupported OS"), | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | /// When a file descriptor is closed on linux, it pops the first | ||
| 227 | /// node from this queue and resumes it. | ||
| 228 | /// Async functions which get the EMFILE error code can suspend, | ||
| 229 | /// putting their coroutine handle into this list. | ||
| 230 | /// TODO make this an atomic linked list | ||
| 231 | pub var emfile_promise_queue = std.LinkedList(promise).init(); | ||
| 232 | |||
| 233 | /// Closes the file handle. Keeps trying if it gets interrupted by a signal. | ||
| 234 | pub fn close(handle: FileHandle) void { | ||
| 235 | if (is_windows) { | ||
| 236 | windows_util.windowsClose(handle); | ||
| 237 | } else { | ||
| 238 | while (true) { | ||
| 239 | const err = posix.getErrno(posix.close(handle)); | ||
| 240 | switch (err) { | ||
| 241 | posix.EINTR => continue, | ||
| 242 | else => { | ||
| 243 | if (emfile_promise_queue.popFirst()) |p| resume p.data; | ||
| 244 | return; | ||
| 245 | }, | ||
| 246 | } | ||
| 247 | } | ||
| 248 | } | ||
| 249 | } | ||
| 250 | |||
| 251 | pub const PosixReadError = error{ | ||
| 252 | InputOutput, | ||
| 253 | SystemResources, | ||
| 254 | IsDir, | ||
| 255 | Unexpected, | ||
| 256 | }; | ||
| 257 | |||
| 258 | /// Returns the number of bytes that were read, which can be less than | ||
| 259 | /// buf.len. If 0 bytes were read, that means EOF. | ||
| 260 | pub fn posixRead(fd: i32, buf: []u8) PosixReadError!usize { | ||
| 261 | // Linux can return EINVAL when read amount is > 0x7ffff000 | ||
| 262 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363158274 | ||
| 263 | const max_buf_len = 0x7ffff000; | ||
| 264 | |||
| 265 | var index: usize = 0; | ||
| 266 | while (index < buf.len) { | ||
| 267 | const want_to_read = math.min(buf.len - index, usize(max_buf_len)); | ||
| 268 | const rc = posix.read(fd, buf.ptr + index, want_to_read); | ||
| 269 | const err = posix.getErrno(rc); | ||
| 270 | switch (err) { | ||
| 271 | 0 => { | ||
| 272 | index += rc; | ||
| 273 | if (rc == want_to_read) continue; | ||
| 274 | // Read returned less than buf.len. | ||
| 275 | return index; | ||
| 276 | }, | ||
| 277 | posix.EINTR => continue, | ||
| 278 | posix.EINVAL => unreachable, | ||
| 279 | posix.EFAULT => unreachable, | ||
| 280 | posix.EAGAIN => unreachable, | ||
| 281 | posix.EBADF => unreachable, // always a race condition | ||
| 282 | posix.EIO => return error.InputOutput, | ||
| 283 | posix.EISDIR => return error.IsDir, | ||
| 284 | posix.ENOBUFS => return error.SystemResources, | ||
| 285 | posix.ENOMEM => return error.SystemResources, | ||
| 286 | else => return unexpectedErrorPosix(err), | ||
| 287 | } | ||
| 288 | } | ||
| 289 | return index; | ||
| 290 | } | ||
| 291 | |||
| 292 | /// Number of bytes read is returned. Upon reading end-of-file, zero is returned. | ||
| 293 | pub fn posix_preadv(fd: i32, iov: [*]const posix.iovec, count: usize, offset: u64) PosixReadError!usize { | ||
| 294 | switch (builtin.os) { | ||
| 295 | builtin.Os.macosx => { | ||
| 296 | // Darwin does not have preadv but it does have pread. | ||
| 297 | var off: usize = 0; | ||
| 298 | var iov_i: usize = 0; | ||
| 299 | var inner_off: usize = 0; | ||
| 300 | while (true) { | ||
| 301 | const v = iov[iov_i]; | ||
| 302 | const rc = darwin.pread(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 303 | const err = darwin.getErrno(rc); | ||
| 304 | switch (err) { | ||
| 305 | 0 => { | ||
| 306 | off += rc; | ||
| 307 | inner_off += rc; | ||
| 308 | if (inner_off == v.iov_len) { | ||
| 309 | iov_i += 1; | ||
| 310 | inner_off = 0; | ||
| 311 | if (iov_i == count) { | ||
| 312 | return off; | ||
| 313 | } | ||
| 314 | } | ||
| 315 | if (rc == 0) return off; // EOF | ||
| 316 | continue; | ||
| 317 | }, | ||
| 318 | posix.EINTR => continue, | ||
| 319 | posix.EINVAL => unreachable, | ||
| 320 | posix.EFAULT => unreachable, | ||
| 321 | posix.ESPIPE => unreachable, // fd is not seekable | ||
| 322 | posix.EAGAIN => unreachable, // this function is not for non blocking | ||
| 323 | posix.EBADF => unreachable, // always a race condition | ||
| 324 | posix.EIO => return error.InputOutput, | ||
| 325 | posix.EISDIR => return error.IsDir, | ||
| 326 | posix.ENOBUFS => return error.SystemResources, | ||
| 327 | posix.ENOMEM => return error.SystemResources, | ||
| 328 | else => return unexpectedErrorPosix(err), | ||
| 329 | } | ||
| 330 | } | ||
| 331 | }, | ||
| 332 | builtin.Os.linux, builtin.Os.freebsd, Os.netbsd => while (true) { | ||
| 333 | const rc = posix.preadv(fd, iov, count, offset); | ||
| 334 | const err = posix.getErrno(rc); | ||
| 335 | switch (err) { | ||
| 336 | 0 => return rc, | ||
| 337 | posix.EINTR => continue, | ||
| 338 | posix.EINVAL => unreachable, | ||
| 339 | posix.EFAULT => unreachable, | ||
| 340 | posix.EAGAIN => unreachable, // don't call this function for non blocking | ||
| 341 | posix.EBADF => unreachable, // always a race condition | ||
| 342 | posix.EIO => return error.InputOutput, | ||
| 343 | posix.EISDIR => return error.IsDir, | ||
| 344 | posix.ENOBUFS => return error.SystemResources, | ||
| 345 | posix.ENOMEM => return error.SystemResources, | ||
| 346 | else => return unexpectedErrorPosix(err), | ||
| 347 | } | ||
| 348 | }, | ||
| 349 | else => @compileError("Unsupported OS"), | ||
| 350 | } | ||
| 351 | } | ||
| 352 | |||
| 353 | pub const PosixWriteError = error{ | ||
| 354 | DiskQuota, | ||
| 355 | FileTooBig, | ||
| 356 | InputOutput, | ||
| 357 | NoSpaceLeft, | ||
| 358 | AccessDenied, | ||
| 359 | BrokenPipe, | ||
| 360 | |||
| 361 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 362 | Unexpected, | ||
| 363 | }; | ||
| 364 | |||
| 365 | /// Calls POSIX write, and keeps trying if it gets interrupted. | ||
| 366 | pub fn posixWrite(fd: i32, bytes: []const u8) PosixWriteError!void { | ||
| 367 | // Linux can return EINVAL when write amount is > 0x7ffff000 | ||
| 368 | // See https://github.com/ziglang/zig/pull/743#issuecomment-363165856 | ||
| 369 | const max_bytes_len = 0x7ffff000; | ||
| 370 | |||
| 371 | var index: usize = 0; | ||
| 372 | while (index < bytes.len) { | ||
| 373 | const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); | ||
| 374 | const rc = posix.write(fd, bytes.ptr + index, amt_to_write); | ||
| 375 | const write_err = posix.getErrno(rc); | ||
| 376 | switch (write_err) { | ||
| 377 | 0 => { | ||
| 378 | index += rc; | ||
| 379 | continue; | ||
| 380 | }, | ||
| 381 | posix.EINTR => continue, | ||
| 382 | posix.EINVAL => unreachable, | ||
| 383 | posix.EFAULT => unreachable, | ||
| 384 | posix.EAGAIN => unreachable, // use posixAsyncWrite for non-blocking | ||
| 385 | posix.EBADF => unreachable, // always a race condition | ||
| 386 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 387 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 388 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 389 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 390 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 391 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 392 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 393 | else => return unexpectedErrorPosix(write_err), | ||
| 394 | } | ||
| 395 | } | ||
| 396 | } | ||
| 397 | |||
| 398 | pub fn posix_pwritev(fd: i32, iov: [*]const posix.iovec_const, count: usize, offset: u64) PosixWriteError!void { | ||
| 399 | switch (builtin.os) { | ||
| 400 | builtin.Os.macosx => { | ||
| 401 | // Darwin does not have pwritev but it does have pwrite. | ||
| 402 | var off: usize = 0; | ||
| 403 | var iov_i: usize = 0; | ||
| 404 | var inner_off: usize = 0; | ||
| 405 | while (true) { | ||
| 406 | const v = iov[iov_i]; | ||
| 407 | const rc = darwin.pwrite(fd, v.iov_base + inner_off, v.iov_len - inner_off, offset + off); | ||
| 408 | const err = darwin.getErrno(rc); | ||
| 409 | switch (err) { | ||
| 410 | 0 => { | ||
| 411 | off += rc; | ||
| 412 | inner_off += rc; | ||
| 413 | if (inner_off == v.iov_len) { | ||
| 414 | iov_i += 1; | ||
| 415 | inner_off = 0; | ||
| 416 | if (iov_i == count) { | ||
| 417 | return; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | continue; | ||
| 421 | }, | ||
| 422 | posix.EINTR => continue, | ||
| 423 | posix.ESPIPE => unreachable, // fd is not seekable | ||
| 424 | posix.EINVAL => unreachable, | ||
| 425 | posix.EFAULT => unreachable, | ||
| 426 | posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking | ||
| 427 | posix.EBADF => unreachable, // always a race condition | ||
| 428 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 429 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 430 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 431 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 432 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 433 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 434 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 435 | else => return unexpectedErrorPosix(err), | ||
| 436 | } | ||
| 437 | } | ||
| 438 | }, | ||
| 439 | builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => while (true) { | ||
| 440 | const rc = posix.pwritev(fd, iov, count, offset); | ||
| 441 | const err = posix.getErrno(rc); | ||
| 442 | switch (err) { | ||
| 443 | 0 => return, | ||
| 444 | posix.EINTR => continue, | ||
| 445 | posix.EINVAL => unreachable, | ||
| 446 | posix.EFAULT => unreachable, | ||
| 447 | posix.EAGAIN => unreachable, // use posixAsyncPWriteV for non-blocking | ||
| 448 | posix.EBADF => unreachable, // always a race condition | ||
| 449 | posix.EDESTADDRREQ => unreachable, // connect was never called | ||
| 450 | posix.EDQUOT => return PosixWriteError.DiskQuota, | ||
| 451 | posix.EFBIG => return PosixWriteError.FileTooBig, | ||
| 452 | posix.EIO => return PosixWriteError.InputOutput, | ||
| 453 | posix.ENOSPC => return PosixWriteError.NoSpaceLeft, | ||
| 454 | posix.EPERM => return PosixWriteError.AccessDenied, | ||
| 455 | posix.EPIPE => return PosixWriteError.BrokenPipe, | ||
| 456 | else => return unexpectedErrorPosix(err), | ||
| 457 | } | ||
| 458 | }, | ||
| 459 | else => @compileError("Unsupported OS"), | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | pub const PosixOpenError = error{ | ||
| 464 | AccessDenied, | ||
| 465 | FileTooBig, | ||
| 466 | IsDir, | ||
| 467 | SymLinkLoop, | ||
| 468 | ProcessFdQuotaExceeded, | ||
| 469 | NameTooLong, | ||
| 470 | SystemFdQuotaExceeded, | ||
| 471 | NoDevice, | ||
| 472 | FileNotFound, | ||
| 473 | SystemResources, | ||
| 474 | NoSpaceLeft, | ||
| 475 | NotDir, | ||
| 476 | PathAlreadyExists, | ||
| 477 | DeviceBusy, | ||
| 478 | |||
| 479 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 480 | Unexpected, | ||
| 481 | }; | ||
| 482 | |||
| 483 | /// ::file_path needs to be copied in memory to add a null terminating byte. | ||
| 484 | /// Calls POSIX open, keeps trying if it gets interrupted, and translates | ||
| 485 | /// the return value into zig errors. | ||
| 486 | pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize) PosixOpenError!i32 { | ||
| 487 | const file_path_c = try toPosixPath(file_path); | ||
| 488 | return posixOpenC(&file_path_c, flags, perm); | ||
| 489 | } | ||
| 490 | |||
| 491 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 492 | pub fn posixOpenC(file_path: [*]const u8, flags: u32, perm: usize) !i32 { | ||
| 493 | while (true) { | ||
| 494 | const result = posix.open(file_path, flags, perm); | ||
| 495 | const err = posix.getErrno(result); | ||
| 496 | if (err > 0) { | ||
| 497 | switch (err) { | ||
| 498 | posix.EINTR => continue, | ||
| 499 | |||
| 500 | posix.EFAULT => unreachable, | ||
| 501 | posix.EINVAL => unreachable, | ||
| 502 | posix.EACCES => return PosixOpenError.AccessDenied, | ||
| 503 | posix.EFBIG, posix.EOVERFLOW => return PosixOpenError.FileTooBig, | ||
| 504 | posix.EISDIR => return PosixOpenError.IsDir, | ||
| 505 | posix.ELOOP => return PosixOpenError.SymLinkLoop, | ||
| 506 | posix.EMFILE => return PosixOpenError.ProcessFdQuotaExceeded, | ||
| 507 | posix.ENAMETOOLONG => return PosixOpenError.NameTooLong, | ||
| 508 | posix.ENFILE => return PosixOpenError.SystemFdQuotaExceeded, | ||
| 509 | posix.ENODEV => return PosixOpenError.NoDevice, | ||
| 510 | posix.ENOENT => return PosixOpenError.FileNotFound, | ||
| 511 | posix.ENOMEM => return PosixOpenError.SystemResources, | ||
| 512 | posix.ENOSPC => return PosixOpenError.NoSpaceLeft, | ||
| 513 | posix.ENOTDIR => return PosixOpenError.NotDir, | ||
| 514 | posix.EPERM => return PosixOpenError.AccessDenied, | ||
| 515 | posix.EEXIST => return PosixOpenError.PathAlreadyExists, | ||
| 516 | posix.EBUSY => return PosixOpenError.DeviceBusy, | ||
| 517 | else => return unexpectedErrorPosix(err), | ||
| 518 | } | ||
| 519 | } | ||
| 520 | return @intCast(i32, result); | ||
| 521 | } | ||
| 522 | } | ||
| 523 | |||
| 524 | /// Used to convert a slice to a null terminated slice on the stack. | ||
| 525 | /// TODO well defined copy elision | ||
| 526 | pub fn toPosixPath(file_path: []const u8) ![posix.PATH_MAX]u8 { | ||
| 527 | var path_with_null: [posix.PATH_MAX]u8 = undefined; | ||
| 528 | if (file_path.len >= posix.PATH_MAX) return error.NameTooLong; | ||
| 529 | mem.copy(u8, path_with_null[0..], file_path); | ||
| 530 | path_with_null[file_path.len] = 0; | ||
| 531 | return path_with_null; | ||
| 532 | } | ||
| 533 | |||
| 534 | pub fn posixDup2(old_fd: i32, new_fd: i32) !void { | ||
| 535 | while (true) { | ||
| 536 | const err = posix.getErrno(posix.dup2(old_fd, new_fd)); | ||
| 537 | if (err > 0) { | ||
| 538 | return switch (err) { | ||
| 539 | posix.EBUSY, posix.EINTR => continue, | ||
| 540 | posix.EMFILE => error.ProcessFdQuotaExceeded, | ||
| 541 | posix.EINVAL => unreachable, | ||
| 542 | else => unexpectedErrorPosix(err), | ||
| 543 | }; | ||
| 544 | } | ||
| 545 | return; | ||
| 546 | } | ||
| 547 | } | ||
| 548 | |||
| 549 | pub fn createNullDelimitedEnvMap(allocator: *Allocator, env_map: *const BufMap) ![]?[*]u8 { | ||
| 550 | const envp_count = env_map.count(); | ||
| 551 | const envp_buf = try allocator.alloc(?[*]u8, envp_count + 1); | ||
| 552 | mem.set(?[*]u8, envp_buf, null); | ||
| 553 | errdefer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 554 | { | ||
| 555 | var it = env_map.iterator(); | ||
| 556 | var i: usize = 0; | ||
| 557 | while (it.next()) |pair| : (i += 1) { | ||
| 558 | const env_buf = try allocator.alloc(u8, pair.key.len + pair.value.len + 2); | ||
| 559 | @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); | ||
| 560 | env_buf[pair.key.len] = '='; | ||
| 561 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); | ||
| 562 | env_buf[env_buf.len - 1] = 0; | ||
| 563 | |||
| 564 | envp_buf[i] = env_buf.ptr; | ||
| 565 | } | ||
| 566 | assert(i == envp_count); | ||
| 567 | } | ||
| 568 | assert(envp_buf[envp_count] == null); | ||
| 569 | return envp_buf; | ||
| 570 | } | ||
| 571 | |||
| 572 | pub fn freeNullDelimitedEnvMap(allocator: *Allocator, envp_buf: []?[*]u8) void { | ||
| 573 | for (envp_buf) |env| { | ||
| 574 | const env_buf = if (env) |ptr| ptr[0 .. cstr.len(ptr) + 1] else break; | ||
| 575 | allocator.free(env_buf); | ||
| 576 | } | ||
| 577 | allocator.free(envp_buf); | ||
| 578 | } | ||
| 579 | |||
| 580 | /// This function must allocate memory to add a null terminating bytes on path and each arg. | ||
| 581 | /// It must also convert to KEY=VALUE\0 format for environment variables, and include null | ||
| 582 | /// pointers after the args and after the environment variables. | ||
| 583 | /// `argv[0]` is the executable path. | ||
| 584 | /// This function also uses the PATH environment variable to get the full path to the executable. | ||
| 585 | pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator: *Allocator) !void { | ||
| 586 | const argv_buf = try allocator.alloc(?[*]u8, argv.len + 1); | ||
| 587 | mem.set(?[*]u8, argv_buf, null); | ||
| 588 | defer { | ||
| 589 | for (argv_buf) |arg| { | ||
| 590 | const arg_buf = if (arg) |ptr| cstr.toSlice(ptr) else break; | ||
| 591 | allocator.free(arg_buf); | ||
| 592 | } | ||
| 593 | allocator.free(argv_buf); | ||
| 594 | } | ||
| 595 | for (argv) |arg, i| { | ||
| 596 | const arg_buf = try allocator.alloc(u8, arg.len + 1); | ||
| 597 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); | ||
| 598 | arg_buf[arg.len] = 0; | ||
| 599 | |||
| 600 | argv_buf[i] = arg_buf.ptr; | ||
| 601 | } | ||
| 602 | argv_buf[argv.len] = null; | ||
| 603 | |||
| 604 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); | ||
| 605 | defer freeNullDelimitedEnvMap(allocator, envp_buf); | ||
| 606 | |||
| 607 | const exe_path = argv[0]; | ||
| 608 | if (mem.indexOfScalar(u8, exe_path, '/') != null) { | ||
| 609 | return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr))); | ||
| 610 | } | ||
| 611 | |||
| 612 | const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; | ||
| 613 | // PATH.len because it is >= the largest search_path | ||
| 614 | // +1 for the / to join the search path and exe_path | ||
| 615 | // +1 for the null terminating byte | ||
| 616 | const path_buf = try allocator.alloc(u8, PATH.len + exe_path.len + 2); | ||
| 617 | defer allocator.free(path_buf); | ||
| 618 | var it = mem.tokenize(PATH, ":"); | ||
| 619 | var seen_eacces = false; | ||
| 620 | var err: usize = undefined; | ||
| 621 | while (it.next()) |search_path| { | ||
| 622 | mem.copy(u8, path_buf, search_path); | ||
| 623 | path_buf[search_path.len] = '/'; | ||
| 624 | mem.copy(u8, path_buf[search_path.len + 1 ..], exe_path); | ||
| 625 | path_buf[search_path.len + exe_path.len + 1] = 0; | ||
| 626 | err = posix.getErrno(posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr)); | ||
| 627 | assert(err > 0); | ||
| 628 | if (err == posix.EACCES) { | ||
| 629 | seen_eacces = true; | ||
| 630 | } else if (err != posix.ENOENT) { | ||
| 631 | return posixExecveErrnoToErr(err); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | if (seen_eacces) { | ||
| 635 | err = posix.EACCES; | ||
| 636 | } | ||
| 637 | return posixExecveErrnoToErr(err); | ||
| 638 | } | ||
| 639 | |||
| 640 | pub const PosixExecveError = error{ | ||
| 641 | SystemResources, | ||
| 642 | AccessDenied, | ||
| 643 | InvalidExe, | ||
| 644 | FileSystem, | ||
| 645 | IsDir, | ||
| 646 | FileNotFound, | ||
| 647 | NotDir, | ||
| 648 | FileBusy, | ||
| 649 | |||
| 650 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 651 | Unexpected, | ||
| 652 | }; | ||
| 653 | |||
| 654 | fn posixExecveErrnoToErr(err: usize) PosixExecveError { | ||
| 655 | assert(err > 0); | ||
| 656 | switch (err) { | ||
| 657 | posix.EFAULT => unreachable, | ||
| 658 | posix.E2BIG => return error.SystemResources, | ||
| 659 | posix.EMFILE => return error.SystemResources, | ||
| 660 | posix.ENAMETOOLONG => return error.SystemResources, | ||
| 661 | posix.ENFILE => return error.SystemResources, | ||
| 662 | posix.ENOMEM => return error.SystemResources, | ||
| 663 | posix.EACCES => return error.AccessDenied, | ||
| 664 | posix.EPERM => return error.AccessDenied, | ||
| 665 | posix.EINVAL => return error.InvalidExe, | ||
| 666 | posix.ENOEXEC => return error.InvalidExe, | ||
| 667 | posix.EIO => return error.FileSystem, | ||
| 668 | posix.ELOOP => return error.FileSystem, | ||
| 669 | posix.EISDIR => return error.IsDir, | ||
| 670 | posix.ENOENT => return error.FileNotFound, | ||
| 671 | posix.ENOTDIR => return error.NotDir, | ||
| 672 | posix.ETXTBSY => return error.FileBusy, | ||
| 673 | else => return unexpectedErrorPosix(err), | ||
| 674 | } | ||
| 675 | } | ||
| 676 | |||
| 677 | pub var linux_elf_aux_maybe: ?[*]std.elf.Auxv = null; | ||
| 678 | pub var posix_environ_raw: [][*]u8 = undefined; | ||
| 679 | |||
| 680 | /// See std.elf for the constants. | ||
| 681 | pub fn linuxGetAuxVal(index: usize) usize { | ||
| 682 | if (builtin.link_libc) { | ||
| 683 | return usize(std.c.getauxval(index)); | ||
| 684 | } else if (linux_elf_aux_maybe) |auxv| { | ||
| 685 | var i: usize = 0; | ||
| 686 | while (auxv[i].a_type != std.elf.AT_NULL) : (i += 1) { | ||
| 687 | if (auxv[i].a_type == index) | ||
| 688 | return auxv[i].a_un.a_val; | ||
| 689 | } | ||
| 690 | } | ||
| 691 | return 0; | ||
| 692 | } | ||
| 693 | |||
| 694 | pub fn getBaseAddress() usize { | ||
| 695 | switch (builtin.os) { | ||
| 696 | builtin.Os.linux => { | ||
| 697 | const base = linuxGetAuxVal(std.elf.AT_BASE); | ||
| 698 | if (base != 0) { | ||
| 699 | return base; | ||
| 700 | } | ||
| 701 | const phdr = linuxGetAuxVal(std.elf.AT_PHDR); | ||
| 702 | return phdr - @sizeOf(std.elf.Ehdr); | ||
| 703 | }, | ||
| 704 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { | ||
| 705 | return @ptrToInt(&std.c._mh_execute_header); | ||
| 706 | }, | ||
| 707 | builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)), | ||
| 708 | else => @compileError("Unsupported OS"), | ||
| 709 | } | ||
| 710 | } | ||
| 711 | |||
| 712 | /// Caller must free result when done. | ||
| 713 | /// TODO make this go through libc when we have it | ||
| 714 | pub fn getEnvMap(allocator: *Allocator) !BufMap { | ||
| 715 | var result = BufMap.init(allocator); | ||
| 716 | errdefer result.deinit(); | ||
| 717 | |||
| 718 | if (is_windows) { | ||
| 719 | const ptr = windows.GetEnvironmentStringsW() orelse return error.OutOfMemory; | ||
| 720 | defer assert(windows.FreeEnvironmentStringsW(ptr) != 0); | ||
| 721 | |||
| 722 | var i: usize = 0; | ||
| 723 | while (true) { | ||
| 724 | if (ptr[i] == 0) return result; | ||
| 725 | |||
| 726 | const key_start = i; | ||
| 727 | |||
| 728 | while (ptr[i] != 0 and ptr[i] != '=') : (i += 1) {} | ||
| 729 | const key_w = ptr[key_start..i]; | ||
| 730 | const key = try std.unicode.utf16leToUtf8Alloc(allocator, key_w); | ||
| 731 | errdefer allocator.free(key); | ||
| 732 | |||
| 733 | if (ptr[i] == '=') i += 1; | ||
| 734 | |||
| 735 | const value_start = i; | ||
| 736 | while (ptr[i] != 0) : (i += 1) {} | ||
| 737 | const value_w = ptr[value_start..i]; | ||
| 738 | const value = try std.unicode.utf16leToUtf8Alloc(allocator, value_w); | ||
| 739 | errdefer allocator.free(value); | ||
| 740 | |||
| 741 | i += 1; // skip over null byte | ||
| 742 | |||
| 743 | try result.setMove(key, value); | ||
| 744 | } | ||
| 745 | } else { | ||
| 746 | for (posix_environ_raw) |ptr| { | ||
| 747 | var line_i: usize = 0; | ||
| 748 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | ||
| 749 | const key = ptr[0..line_i]; | ||
| 750 | |||
| 751 | var end_i: usize = line_i; | ||
| 752 | while (ptr[end_i] != 0) : (end_i += 1) {} | ||
| 753 | const value = ptr[line_i + 1 .. end_i]; | ||
| 754 | |||
| 755 | try result.set(key, value); | ||
| 756 | } | ||
| 757 | return result; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | test "os.getEnvMap" { | ||
| 762 | var env = try getEnvMap(std.debug.global_allocator); | ||
| 763 | defer env.deinit(); | ||
| 764 | } | ||
| 765 | |||
| 766 | /// TODO make this go through libc when we have it | ||
| 767 | pub fn getEnvPosix(key: []const u8) ?[]const u8 { | ||
| 768 | for (posix_environ_raw) |ptr| { | ||
| 769 | var line_i: usize = 0; | ||
| 770 | while (ptr[line_i] != 0 and ptr[line_i] != '=') : (line_i += 1) {} | ||
| 771 | const this_key = ptr[0..line_i]; | ||
| 772 | if (!mem.eql(u8, key, this_key)) continue; | ||
| 773 | |||
| 774 | var end_i: usize = line_i; | ||
| 775 | while (ptr[end_i] != 0) : (end_i += 1) {} | ||
| 776 | const this_value = ptr[line_i + 1 .. end_i]; | ||
| 777 | |||
| 778 | return this_value; | ||
| 779 | } | ||
| 780 | return null; | ||
| 781 | } | ||
| 782 | |||
| 783 | pub const GetEnvVarOwnedError = error{ | ||
| 784 | OutOfMemory, | ||
| 785 | EnvironmentVariableNotFound, | ||
| 786 | |||
| 787 | /// See https://github.com/ziglang/zig/issues/1774 | ||
| 788 | InvalidUtf8, | ||
| 789 | }; | ||
| 790 | |||
| 791 | /// Caller must free returned memory. | ||
| 792 | /// TODO make this go through libc when we have it | ||
| 793 | pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) GetEnvVarOwnedError![]u8 { | ||
| 794 | if (is_windows) { | ||
| 795 | const key_with_null = try std.unicode.utf8ToUtf16LeWithNull(allocator, key); | ||
| 796 | defer allocator.free(key_with_null); | ||
| 797 | |||
| 798 | var buf = try allocator.alloc(u16, 256); | ||
| 799 | defer allocator.free(buf); | ||
| 800 | |||
| 801 | while (true) { | ||
| 802 | const windows_buf_len = math.cast(windows.DWORD, buf.len) catch return error.OutOfMemory; | ||
| 803 | const result = windows.GetEnvironmentVariableW(key_with_null.ptr, buf.ptr, windows_buf_len); | ||
| 804 | |||
| 805 | if (result == 0) { | ||
| 806 | const err = windows.GetLastError(); | ||
| 807 | return switch (err) { | ||
| 808 | windows.ERROR.ENVVAR_NOT_FOUND => error.EnvironmentVariableNotFound, | ||
| 809 | else => { | ||
| 810 | _ = unexpectedErrorWindows(err); | ||
| 811 | return error.EnvironmentVariableNotFound; | ||
| 812 | }, | ||
| 813 | }; | ||
| 814 | } | ||
| 815 | |||
| 816 | if (result > buf.len) { | ||
| 817 | buf = try allocator.realloc(u16, buf, result); | ||
| 818 | continue; | ||
| 819 | } | ||
| 820 | |||
| 821 | return std.unicode.utf16leToUtf8Alloc(allocator, buf) catch |err| switch (err) { | ||
| 822 | error.DanglingSurrogateHalf => return error.InvalidUtf8, | ||
| 823 | error.ExpectedSecondSurrogateHalf => return error.InvalidUtf8, | ||
| 824 | error.UnexpectedSecondSurrogateHalf => return error.InvalidUtf8, | ||
| 825 | error.OutOfMemory => return error.OutOfMemory, | ||
| 826 | }; | ||
| 827 | } | ||
| 828 | } else { | ||
| 829 | const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound; | ||
| 830 | return mem.dupe(allocator, u8, result); | ||
| 831 | } | ||
| 832 | } | ||
| 833 | |||
| 834 | test "os.getEnvVarOwned" { | ||
| 835 | var ga = debug.global_allocator; | ||
| 836 | testing.expectError(error.EnvironmentVariableNotFound, getEnvVarOwned(ga, "BADENV")); | ||
| 837 | } | ||
| 838 | |||
| 839 | /// Caller must free the returned memory. | ||
| 840 | pub fn getCwdAlloc(allocator: *Allocator) ![]u8 { | ||
| 841 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 842 | return mem.dupe(allocator, u8, try getCwd(&buf)); | ||
| 843 | } | ||
| 844 | |||
| 845 | pub const GetCwdError = error{Unexpected}; | ||
| 846 | |||
| 847 | /// The result is a slice of out_buffer. | ||
| 848 | pub fn getCwd(out_buffer: *[MAX_PATH_BYTES]u8) GetCwdError![]u8 { | ||
| 849 | switch (builtin.os) { | ||
| 850 | Os.windows => { | ||
| 851 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 852 | const casted_len = @intCast(windows.DWORD, utf16le_buf.len); // TODO shouldn't need this cast | ||
| 853 | const casted_ptr = ([*]u16)(&utf16le_buf); // TODO shouldn't need this cast | ||
| 854 | const result = windows.GetCurrentDirectoryW(casted_len, casted_ptr); | ||
| 855 | if (result == 0) { | ||
| 856 | const err = windows.GetLastError(); | ||
| 857 | switch (err) { | ||
| 858 | else => return unexpectedErrorWindows(err), | ||
| 859 | } | ||
| 860 | } | ||
| 861 | assert(result <= utf16le_buf.len); | ||
| 862 | const utf16le_slice = utf16le_buf[0..result]; | ||
| 863 | // Trust that Windows gives us valid UTF-16LE. | ||
| 864 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | ||
| 865 | return out_buffer[0..end_index]; | ||
| 866 | }, | ||
| 867 | else => { | ||
| 868 | const err = posix.getErrno(posix.getcwd(out_buffer, out_buffer.len)); | ||
| 869 | switch (err) { | ||
| 870 | 0 => return cstr.toSlice(out_buffer), | ||
| 871 | posix.ERANGE => unreachable, | ||
| 872 | else => return unexpectedErrorPosix(err), | ||
| 873 | } | ||
| 874 | }, | ||
| 875 | } | ||
| 876 | } | ||
| 877 | |||
| 878 | test "os.getCwd" { | ||
| 879 | // at least call it so it gets compiled | ||
| 880 | _ = getCwdAlloc(debug.global_allocator); | ||
| 881 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 882 | _ = getCwd(&buf); | ||
| 883 | } | ||
| 884 | |||
| 885 | pub const SymLinkError = PosixSymLinkError || WindowsSymLinkError; | ||
| 886 | |||
| 887 | /// TODO add a symLinkC variant | ||
| 888 | pub fn symLink(existing_path: []const u8, new_path: []const u8) SymLinkError!void { | ||
| 889 | if (is_windows) { | ||
| 890 | return symLinkWindows(existing_path, new_path); | ||
| 891 | } else { | ||
| 892 | return symLinkPosix(existing_path, new_path); | ||
| 893 | } | ||
| 894 | } | ||
| 895 | |||
| 896 | pub const WindowsSymLinkError = error{ | ||
| 897 | NameTooLong, | ||
| 898 | InvalidUtf8, | ||
| 899 | BadPathName, | ||
| 900 | |||
| 901 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 902 | Unexpected, | ||
| 903 | }; | ||
| 904 | |||
| 905 | pub fn symLinkW(existing_path_w: [*]const u16, new_path_w: [*]const u16) WindowsSymLinkError!void { | ||
| 906 | if (windows.CreateSymbolicLinkW(existing_path_w, new_path_w, 0) == 0) { | ||
| 907 | const err = windows.GetLastError(); | ||
| 908 | switch (err) { | ||
| 909 | else => return unexpectedErrorWindows(err), | ||
| 910 | } | ||
| 911 | } | ||
| 912 | } | ||
| 913 | |||
| 914 | pub fn symLinkWindows(existing_path: []const u8, new_path: []const u8) WindowsSymLinkError!void { | ||
| 915 | const existing_path_w = try windows_util.sliceToPrefixedFileW(existing_path); | ||
| 916 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 917 | return symLinkW(&existing_path_w, &new_path_w); | ||
| 918 | } | ||
| 919 | |||
| 920 | pub const PosixSymLinkError = error{ | ||
| 921 | AccessDenied, | ||
| 922 | DiskQuota, | ||
| 923 | PathAlreadyExists, | ||
| 924 | FileSystem, | ||
| 925 | SymLinkLoop, | ||
| 926 | NameTooLong, | ||
| 927 | FileNotFound, | ||
| 928 | SystemResources, | ||
| 929 | NoSpaceLeft, | ||
| 930 | ReadOnlyFileSystem, | ||
| 931 | NotDir, | ||
| 932 | |||
| 933 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 934 | Unexpected, | ||
| 935 | }; | ||
| 936 | |||
| 937 | pub fn symLinkPosixC(existing_path: [*]const u8, new_path: [*]const u8) PosixSymLinkError!void { | ||
| 938 | const err = posix.getErrno(posix.symlink(existing_path, new_path)); | ||
| 939 | switch (err) { | ||
| 940 | 0 => return, | ||
| 941 | posix.EFAULT => unreachable, | ||
| 942 | posix.EINVAL => unreachable, | ||
| 943 | posix.EACCES => return error.AccessDenied, | ||
| 944 | posix.EPERM => return error.AccessDenied, | ||
| 945 | posix.EDQUOT => return error.DiskQuota, | ||
| 946 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 947 | posix.EIO => return error.FileSystem, | ||
| 948 | posix.ELOOP => return error.SymLinkLoop, | ||
| 949 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 950 | posix.ENOENT => return error.FileNotFound, | ||
| 951 | posix.ENOTDIR => return error.NotDir, | ||
| 952 | posix.ENOMEM => return error.SystemResources, | ||
| 953 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 954 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 955 | else => return unexpectedErrorPosix(err), | ||
| 956 | } | ||
| 957 | } | ||
| 958 | |||
| 959 | pub fn symLinkPosix(existing_path: []const u8, new_path: []const u8) PosixSymLinkError!void { | ||
| 960 | const existing_path_c = try toPosixPath(existing_path); | ||
| 961 | const new_path_c = try toPosixPath(new_path); | ||
| 962 | return symLinkPosixC(&existing_path_c, &new_path_c); | ||
| 963 | } | ||
| 964 | |||
| 965 | // here we replace the standard +/ with -_ so that it can be used in a file name | ||
| 966 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); | ||
| 967 | |||
| 968 | /// TODO remove the allocator requirement from this API | ||
| 969 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { | ||
| 970 | if (symLink(existing_path, new_path)) { | ||
| 971 | return; | ||
| 972 | } else |err| switch (err) { | ||
| 973 | error.PathAlreadyExists => {}, | ||
| 974 | else => return err, // TODO zig should know this set does not include PathAlreadyExists | ||
| 975 | } | ||
| 976 | |||
| 977 | const dirname = os.path.dirname(new_path) orelse "."; | ||
| 978 | |||
| 979 | var rand_buf: [12]u8 = undefined; | ||
| 980 | const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); | ||
| 981 | defer allocator.free(tmp_path); | ||
| 982 | mem.copy(u8, tmp_path[0..], dirname); | ||
| 983 | tmp_path[dirname.len] = os.path.sep; | ||
| 984 | while (true) { | ||
| 985 | try getRandomBytes(rand_buf[0..]); | ||
| 986 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], rand_buf); | ||
| 987 | |||
| 988 | if (symLink(existing_path, tmp_path)) { | ||
| 989 | return rename(tmp_path, new_path); | ||
| 990 | } else |err| switch (err) { | ||
| 991 | error.PathAlreadyExists => continue, | ||
| 992 | else => return err, // TODO zig should know this set does not include PathAlreadyExists | ||
| 993 | } | ||
| 994 | } | ||
| 995 | } | ||
| 996 | |||
| 997 | pub const DeleteFileError = error{ | ||
| 998 | FileNotFound, | ||
| 999 | AccessDenied, | ||
| 1000 | FileBusy, | ||
| 1001 | FileSystem, | ||
| 1002 | IsDir, | ||
| 1003 | SymLinkLoop, | ||
| 1004 | NameTooLong, | ||
| 1005 | NotDir, | ||
| 1006 | SystemResources, | ||
| 1007 | ReadOnlyFileSystem, | ||
| 1008 | |||
| 1009 | /// On Windows, file paths must be valid Unicode. | ||
| 1010 | InvalidUtf8, | ||
| 1011 | |||
| 1012 | /// On Windows, file paths cannot contain these characters: | ||
| 1013 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 1014 | BadPathName, | ||
| 1015 | |||
| 1016 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1017 | Unexpected, | ||
| 1018 | }; | ||
| 1019 | |||
| 1020 | pub fn deleteFile(file_path: []const u8) DeleteFileError!void { | ||
| 1021 | if (builtin.os == Os.windows) { | ||
| 1022 | const file_path_w = try windows_util.sliceToPrefixedFileW(file_path); | ||
| 1023 | return deleteFileW(&file_path_w); | ||
| 1024 | } else { | ||
| 1025 | const file_path_c = try toPosixPath(file_path); | ||
| 1026 | return deleteFileC(&file_path_c); | ||
| 1027 | } | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | pub fn deleteFileW(file_path: [*]const u16) DeleteFileError!void { | ||
| 1031 | if (windows.DeleteFileW(file_path) == 0) { | ||
| 1032 | const err = windows.GetLastError(); | ||
| 1033 | switch (err) { | ||
| 1034 | windows.ERROR.FILE_NOT_FOUND => return error.FileNotFound, | ||
| 1035 | windows.ERROR.ACCESS_DENIED => return error.AccessDenied, | ||
| 1036 | windows.ERROR.FILENAME_EXCED_RANGE => return error.NameTooLong, | ||
| 1037 | windows.ERROR.INVALID_PARAMETER => return error.NameTooLong, | ||
| 1038 | else => return unexpectedErrorWindows(err), | ||
| 1039 | } | ||
| 1040 | } | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | pub fn deleteFileC(file_path: [*]const u8) DeleteFileError!void { | ||
| 1044 | if (is_windows) { | ||
| 1045 | const file_path_w = try windows_util.cStrToPrefixedFileW(file_path); | ||
| 1046 | return deleteFileW(&file_path_w); | ||
| 1047 | } else { | ||
| 1048 | const err = posix.getErrno(posix.unlink(file_path)); | ||
| 1049 | switch (err) { | ||
| 1050 | 0 => return, | ||
| 1051 | posix.EACCES => return error.AccessDenied, | ||
| 1052 | posix.EPERM => return error.AccessDenied, | ||
| 1053 | posix.EBUSY => return error.FileBusy, | ||
| 1054 | posix.EFAULT => unreachable, | ||
| 1055 | posix.EINVAL => unreachable, | ||
| 1056 | posix.EIO => return error.FileSystem, | ||
| 1057 | posix.EISDIR => return error.IsDir, | ||
| 1058 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1059 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1060 | posix.ENOENT => return error.FileNotFound, | ||
| 1061 | posix.ENOTDIR => return error.NotDir, | ||
| 1062 | posix.ENOMEM => return error.SystemResources, | ||
| 1063 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1064 | else => return unexpectedErrorPosix(err), | ||
| 1065 | } | ||
| 1066 | } | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | ||
| 1070 | /// merged and readily available, | ||
| 1071 | /// there is a possibility of power loss or application termination leaving temporary files present | ||
| 1072 | /// in the same directory as dest_path. | ||
| 1073 | /// Destination file will have the same mode as the source file. | ||
| 1074 | pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void { | ||
| 1075 | var in_file = try os.File.openRead(source_path); | ||
| 1076 | defer in_file.close(); | ||
| 1077 | |||
| 1078 | const mode = try in_file.mode(); | ||
| 1079 | |||
| 1080 | var atomic_file = try AtomicFile.init(dest_path, mode); | ||
| 1081 | defer atomic_file.deinit(); | ||
| 1082 | |||
| 1083 | var buf: [page_size]u8 = undefined; | ||
| 1084 | while (true) { | ||
| 1085 | const amt = try in_file.readFull(buf[0..]); | ||
| 1086 | try atomic_file.file.write(buf[0..amt]); | ||
| 1087 | if (amt != buf.len) { | ||
| 1088 | return atomic_file.finish(); | ||
| 1089 | } | ||
| 1090 | } | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | /// Guaranteed to be atomic. However until https://patchwork.kernel.org/patch/9636735/ is | ||
| 1094 | /// merged and readily available, | ||
| 1095 | /// there is a possibility of power loss or application termination leaving temporary files present | ||
| 1096 | pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void { | ||
| 1097 | var in_file = try os.File.openRead(source_path); | ||
| 1098 | defer in_file.close(); | ||
| 1099 | |||
| 1100 | var atomic_file = try AtomicFile.init(dest_path, mode); | ||
| 1101 | defer atomic_file.deinit(); | ||
| 1102 | |||
| 1103 | var buf: [page_size]u8 = undefined; | ||
| 1104 | while (true) { | ||
| 1105 | const amt = try in_file.read(buf[0..]); | ||
| 1106 | try atomic_file.file.write(buf[0..amt]); | ||
| 1107 | if (amt != buf.len) { | ||
| 1108 | return atomic_file.finish(); | ||
| 1109 | } | ||
| 1110 | } | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | pub const AtomicFile = struct { | ||
| 1114 | file: os.File, | ||
| 1115 | tmp_path_buf: [MAX_PATH_BYTES]u8, | ||
| 1116 | dest_path: []const u8, | ||
| 1117 | finished: bool, | ||
| 1118 | |||
| 1119 | const InitError = os.File.OpenError; | ||
| 1120 | |||
| 1121 | /// dest_path must remain valid for the lifetime of AtomicFile | ||
| 1122 | /// call finish to atomically replace dest_path with contents | ||
| 1123 | /// TODO once we have null terminated pointers, use the | ||
| 1124 | /// openWriteNoClobberN function | ||
| 1125 | pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile { | ||
| 1126 | const dirname = os.path.dirname(dest_path); | ||
| 1127 | var rand_buf: [12]u8 = undefined; | ||
| 1128 | const dirname_component_len = if (dirname) |d| d.len + 1 else 0; | ||
| 1129 | const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len); | ||
| 1130 | const tmp_path_len = dirname_component_len + encoded_rand_len; | ||
| 1131 | var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 1132 | if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong; | ||
| 1133 | |||
| 1134 | if (dirname) |dir| { | ||
| 1135 | mem.copy(u8, tmp_path_buf[0..], dir); | ||
| 1136 | tmp_path_buf[dir.len] = os.path.sep; | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | tmp_path_buf[tmp_path_len] = 0; | ||
| 1140 | |||
| 1141 | while (true) { | ||
| 1142 | try getRandomBytes(rand_buf[0..]); | ||
| 1143 | b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], rand_buf); | ||
| 1144 | |||
| 1145 | const file = os.File.openWriteNoClobberC(&tmp_path_buf, mode) catch |err| switch (err) { | ||
| 1146 | error.PathAlreadyExists => continue, | ||
| 1147 | // TODO zig should figure out that this error set does not include PathAlreadyExists since | ||
| 1148 | // it is handled in the above switch | ||
| 1149 | else => return err, | ||
| 1150 | }; | ||
| 1151 | |||
| 1152 | return AtomicFile{ | ||
| 1153 | .file = file, | ||
| 1154 | .tmp_path_buf = tmp_path_buf, | ||
| 1155 | .dest_path = dest_path, | ||
| 1156 | .finished = false, | ||
| 1157 | }; | ||
| 1158 | } | ||
| 1159 | } | ||
| 1160 | |||
| 1161 | /// always call deinit, even after successful finish() | ||
| 1162 | pub fn deinit(self: *AtomicFile) void { | ||
| 1163 | if (!self.finished) { | ||
| 1164 | self.file.close(); | ||
| 1165 | deleteFileC(&self.tmp_path_buf) catch {}; | ||
| 1166 | self.finished = true; | ||
| 1167 | } | ||
| 1168 | } | ||
| 1169 | |||
| 1170 | pub fn finish(self: *AtomicFile) !void { | ||
| 1171 | assert(!self.finished); | ||
| 1172 | self.file.close(); | ||
| 1173 | self.finished = true; | ||
| 1174 | if (is_posix) { | ||
| 1175 | const dest_path_c = try toPosixPath(self.dest_path); | ||
| 1176 | return renameC(&self.tmp_path_buf, &dest_path_c); | ||
| 1177 | } else if (is_windows) { | ||
| 1178 | const dest_path_w = try windows_util.sliceToPrefixedFileW(self.dest_path); | ||
| 1179 | const tmp_path_w = try windows_util.cStrToPrefixedFileW(&self.tmp_path_buf); | ||
| 1180 | return renameW(&tmp_path_w, &dest_path_w); | ||
| 1181 | } else { | ||
| 1182 | @compileError("Unsupported OS"); | ||
| 1183 | } | ||
| 1184 | } | ||
| 1185 | }; | ||
| 1186 | |||
| 1187 | pub fn renameC(old_path: [*]const u8, new_path: [*]const u8) !void { | ||
| 1188 | if (is_windows) { | ||
| 1189 | const old_path_w = try windows_util.cStrToPrefixedFileW(old_path); | ||
| 1190 | const new_path_w = try windows_util.cStrToPrefixedFileW(new_path); | ||
| 1191 | return renameW(&old_path_w, &new_path_w); | ||
| 1192 | } else { | ||
| 1193 | const err = posix.getErrno(posix.rename(old_path, new_path)); | ||
| 1194 | switch (err) { | ||
| 1195 | 0 => return, | ||
| 1196 | posix.EACCES => return error.AccessDenied, | ||
| 1197 | posix.EPERM => return error.AccessDenied, | ||
| 1198 | posix.EBUSY => return error.FileBusy, | ||
| 1199 | posix.EDQUOT => return error.DiskQuota, | ||
| 1200 | posix.EFAULT => unreachable, | ||
| 1201 | posix.EINVAL => unreachable, | ||
| 1202 | posix.EISDIR => return error.IsDir, | ||
| 1203 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1204 | posix.EMLINK => return error.LinkQuotaExceeded, | ||
| 1205 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1206 | posix.ENOENT => return error.FileNotFound, | ||
| 1207 | posix.ENOTDIR => return error.NotDir, | ||
| 1208 | posix.ENOMEM => return error.SystemResources, | ||
| 1209 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1210 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1211 | posix.ENOTEMPTY => return error.PathAlreadyExists, | ||
| 1212 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1213 | posix.EXDEV => return error.RenameAcrossMountPoints, | ||
| 1214 | else => return unexpectedErrorPosix(err), | ||
| 1215 | } | ||
| 1216 | } | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | pub fn renameW(old_path: [*]const u16, new_path: [*]const u16) !void { | ||
| 1220 | const flags = windows.MOVEFILE_REPLACE_EXISTING | windows.MOVEFILE_WRITE_THROUGH; | ||
| 1221 | if (windows.MoveFileExW(old_path, new_path, flags) == 0) { | ||
| 1222 | const err = windows.GetLastError(); | ||
| 1223 | switch (err) { | ||
| 1224 | else => return unexpectedErrorWindows(err), | ||
| 1225 | } | ||
| 1226 | } | ||
| 1227 | } | ||
| 1228 | |||
| 1229 | pub fn rename(old_path: []const u8, new_path: []const u8) !void { | ||
| 1230 | if (is_windows) { | ||
| 1231 | const old_path_w = try windows_util.sliceToPrefixedFileW(old_path); | ||
| 1232 | const new_path_w = try windows_util.sliceToPrefixedFileW(new_path); | ||
| 1233 | return renameW(&old_path_w, &new_path_w); | ||
| 1234 | } else { | ||
| 1235 | const old_path_c = try toPosixPath(old_path); | ||
| 1236 | const new_path_c = try toPosixPath(new_path); | ||
| 1237 | return renameC(&old_path_c, &new_path_c); | ||
| 1238 | } | ||
| 1239 | } | ||
| 1240 | |||
| 1241 | pub fn makeDir(dir_path: []const u8) !void { | ||
| 1242 | if (is_windows) { | ||
| 1243 | return makeDirWindows(dir_path); | ||
| 1244 | } else { | ||
| 1245 | return makeDirPosix(dir_path); | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | pub fn makeDirWindows(dir_path: []const u8) !void { | ||
| 1250 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | ||
| 1251 | |||
| 1252 | if (windows.CreateDirectoryW(&dir_path_w, null) == 0) { | ||
| 1253 | const err = windows.GetLastError(); | ||
| 1254 | return switch (err) { | ||
| 1255 | windows.ERROR.ALREADY_EXISTS => error.PathAlreadyExists, | ||
| 1256 | windows.ERROR.PATH_NOT_FOUND => error.FileNotFound, | ||
| 1257 | else => unexpectedErrorWindows(err), | ||
| 1258 | }; | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | pub fn makeDirPosixC(dir_path: [*]const u8) !void { | ||
| 1263 | const err = posix.getErrno(posix.mkdir(dir_path, 0o755)); | ||
| 1264 | switch (err) { | ||
| 1265 | 0 => return, | ||
| 1266 | posix.EACCES => return error.AccessDenied, | ||
| 1267 | posix.EPERM => return error.AccessDenied, | ||
| 1268 | posix.EDQUOT => return error.DiskQuota, | ||
| 1269 | posix.EEXIST => return error.PathAlreadyExists, | ||
| 1270 | posix.EFAULT => unreachable, | ||
| 1271 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1272 | posix.EMLINK => return error.LinkQuotaExceeded, | ||
| 1273 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1274 | posix.ENOENT => return error.FileNotFound, | ||
| 1275 | posix.ENOMEM => return error.SystemResources, | ||
| 1276 | posix.ENOSPC => return error.NoSpaceLeft, | ||
| 1277 | posix.ENOTDIR => return error.NotDir, | ||
| 1278 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1279 | else => return unexpectedErrorPosix(err), | ||
| 1280 | } | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | pub fn makeDirPosix(dir_path: []const u8) !void { | ||
| 1284 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1285 | return makeDirPosixC(&dir_path_c); | ||
| 1286 | } | ||
| 1287 | |||
| 1288 | /// Calls makeDir recursively to make an entire path. Returns success if the path | ||
| 1289 | /// already exists and is a directory. | ||
| 1290 | /// TODO determine if we can remove the allocator requirement from this function | ||
| 1291 | pub fn makePath(allocator: *Allocator, full_path: []const u8) !void { | ||
| 1292 | const resolved_path = try path.resolve(allocator, [][]const u8{full_path}); | ||
| 1293 | defer allocator.free(resolved_path); | ||
| 1294 | |||
| 1295 | var end_index: usize = resolved_path.len; | ||
| 1296 | while (true) { | ||
| 1297 | makeDir(resolved_path[0..end_index]) catch |err| switch (err) { | ||
| 1298 | error.PathAlreadyExists => { | ||
| 1299 | // TODO stat the file and return an error if it's not a directory | ||
| 1300 | // this is important because otherwise a dangling symlink | ||
| 1301 | // could cause an infinite loop | ||
| 1302 | if (end_index == resolved_path.len) return; | ||
| 1303 | }, | ||
| 1304 | error.FileNotFound => { | ||
| 1305 | // march end_index backward until next path component | ||
| 1306 | while (true) { | ||
| 1307 | end_index -= 1; | ||
| 1308 | if (os.path.isSep(resolved_path[end_index])) break; | ||
| 1309 | } | ||
| 1310 | continue; | ||
| 1311 | }, | ||
| 1312 | else => return err, | ||
| 1313 | }; | ||
| 1314 | if (end_index == resolved_path.len) return; | ||
| 1315 | // march end_index forward until next path component | ||
| 1316 | while (true) { | ||
| 1317 | end_index += 1; | ||
| 1318 | if (end_index == resolved_path.len or os.path.isSep(resolved_path[end_index])) break; | ||
| 1319 | } | ||
| 1320 | } | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | pub const DeleteDirError = error{ | ||
| 1324 | AccessDenied, | ||
| 1325 | FileBusy, | ||
| 1326 | SymLinkLoop, | ||
| 1327 | NameTooLong, | ||
| 1328 | FileNotFound, | ||
| 1329 | SystemResources, | ||
| 1330 | NotDir, | ||
| 1331 | DirNotEmpty, | ||
| 1332 | ReadOnlyFileSystem, | ||
| 1333 | InvalidUtf8, | ||
| 1334 | BadPathName, | ||
| 1335 | |||
| 1336 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1337 | Unexpected, | ||
| 1338 | }; | ||
| 1339 | |||
| 1340 | pub fn deleteDirC(dir_path: [*]const u8) DeleteDirError!void { | ||
| 1341 | switch (builtin.os) { | ||
| 1342 | Os.windows => { | ||
| 1343 | const dir_path_w = try windows_util.cStrToPrefixedFileW(dir_path); | ||
| 1344 | return deleteDirW(&dir_path_w); | ||
| 1345 | }, | ||
| 1346 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 1347 | const err = posix.getErrno(posix.rmdir(dir_path)); | ||
| 1348 | switch (err) { | ||
| 1349 | 0 => return, | ||
| 1350 | posix.EACCES => return error.AccessDenied, | ||
| 1351 | posix.EPERM => return error.AccessDenied, | ||
| 1352 | posix.EBUSY => return error.FileBusy, | ||
| 1353 | posix.EFAULT => unreachable, | ||
| 1354 | posix.EINVAL => unreachable, | ||
| 1355 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1356 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 1357 | posix.ENOENT => return error.FileNotFound, | ||
| 1358 | posix.ENOMEM => return error.SystemResources, | ||
| 1359 | posix.ENOTDIR => return error.NotDir, | ||
| 1360 | posix.EEXIST => return error.DirNotEmpty, | ||
| 1361 | posix.ENOTEMPTY => return error.DirNotEmpty, | ||
| 1362 | posix.EROFS => return error.ReadOnlyFileSystem, | ||
| 1363 | else => return unexpectedErrorPosix(err), | ||
| 1364 | } | ||
| 1365 | }, | ||
| 1366 | else => @compileError("unimplemented"), | ||
| 1367 | } | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn deleteDirW(dir_path_w: [*]const u16) DeleteDirError!void { | ||
| 1371 | if (windows.RemoveDirectoryW(dir_path_w) == 0) { | ||
| 1372 | const err = windows.GetLastError(); | ||
| 1373 | switch (err) { | ||
| 1374 | windows.ERROR.PATH_NOT_FOUND => return error.FileNotFound, | ||
| 1375 | windows.ERROR.DIR_NOT_EMPTY => return error.DirNotEmpty, | ||
| 1376 | else => return unexpectedErrorWindows(err), | ||
| 1377 | } | ||
| 1378 | } | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | /// Returns ::error.DirNotEmpty if the directory is not empty. | ||
| 1382 | /// To delete a directory recursively, see ::deleteTree | ||
| 1383 | pub fn deleteDir(dir_path: []const u8) DeleteDirError!void { | ||
| 1384 | switch (builtin.os) { | ||
| 1385 | Os.windows => { | ||
| 1386 | const dir_path_w = try windows_util.sliceToPrefixedFileW(dir_path); | ||
| 1387 | return deleteDirW(&dir_path_w); | ||
| 1388 | }, | ||
| 1389 | Os.linux, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 1390 | const dir_path_c = try toPosixPath(dir_path); | ||
| 1391 | return deleteDirC(&dir_path_c); | ||
| 1392 | }, | ||
| 1393 | else => @compileError("unimplemented"), | ||
| 1394 | } | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | /// Whether ::full_path describes a symlink, file, or directory, this function | ||
| 1398 | /// removes it. If it cannot be removed because it is a non-empty directory, | ||
| 1399 | /// this function recursively removes its entries and then tries again. | ||
| 1400 | const DeleteTreeError = error{ | ||
| 1401 | OutOfMemory, | ||
| 1402 | AccessDenied, | ||
| 1403 | FileTooBig, | ||
| 1404 | IsDir, | ||
| 1405 | SymLinkLoop, | ||
| 1406 | ProcessFdQuotaExceeded, | ||
| 1407 | NameTooLong, | ||
| 1408 | SystemFdQuotaExceeded, | ||
| 1409 | NoDevice, | ||
| 1410 | SystemResources, | ||
| 1411 | NoSpaceLeft, | ||
| 1412 | PathAlreadyExists, | ||
| 1413 | ReadOnlyFileSystem, | ||
| 1414 | NotDir, | ||
| 1415 | FileNotFound, | ||
| 1416 | FileSystem, | ||
| 1417 | FileBusy, | ||
| 1418 | DirNotEmpty, | ||
| 1419 | DeviceBusy, | ||
| 1420 | |||
| 1421 | /// On Windows, file paths must be valid Unicode. | ||
| 1422 | InvalidUtf8, | ||
| 1423 | |||
| 1424 | /// On Windows, file paths cannot contain these characters: | ||
| 1425 | /// '/', '*', '?', '"', '<', '>', '|' | ||
| 1426 | BadPathName, | ||
| 1427 | |||
| 1428 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1429 | Unexpected, | ||
| 1430 | }; | ||
| 1431 | |||
| 1432 | /// TODO determine if we can remove the allocator requirement | ||
| 1433 | pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!void { | ||
| 1434 | start_over: while (true) { | ||
| 1435 | var got_access_denied = false; | ||
| 1436 | // First, try deleting the item as a file. This way we don't follow sym links. | ||
| 1437 | if (deleteFile(full_path)) { | ||
| 1438 | return; | ||
| 1439 | } else |err| switch (err) { | ||
| 1440 | error.FileNotFound => return, | ||
| 1441 | error.IsDir => {}, | ||
| 1442 | error.AccessDenied => got_access_denied = true, | ||
| 1443 | |||
| 1444 | error.InvalidUtf8, | ||
| 1445 | error.SymLinkLoop, | ||
| 1446 | error.NameTooLong, | ||
| 1447 | error.SystemResources, | ||
| 1448 | error.ReadOnlyFileSystem, | ||
| 1449 | error.NotDir, | ||
| 1450 | error.FileSystem, | ||
| 1451 | error.FileBusy, | ||
| 1452 | error.BadPathName, | ||
| 1453 | error.Unexpected, | ||
| 1454 | => return err, | ||
| 1455 | } | ||
| 1456 | { | ||
| 1457 | var dir = Dir.open(allocator, full_path) catch |err| switch (err) { | ||
| 1458 | error.NotDir => { | ||
| 1459 | if (got_access_denied) { | ||
| 1460 | return error.AccessDenied; | ||
| 1461 | } | ||
| 1462 | continue :start_over; | ||
| 1463 | }, | ||
| 1464 | |||
| 1465 | error.OutOfMemory, | ||
| 1466 | error.AccessDenied, | ||
| 1467 | error.FileTooBig, | ||
| 1468 | error.IsDir, | ||
| 1469 | error.SymLinkLoop, | ||
| 1470 | error.ProcessFdQuotaExceeded, | ||
| 1471 | error.NameTooLong, | ||
| 1472 | error.SystemFdQuotaExceeded, | ||
| 1473 | error.NoDevice, | ||
| 1474 | error.FileNotFound, | ||
| 1475 | error.SystemResources, | ||
| 1476 | error.NoSpaceLeft, | ||
| 1477 | error.PathAlreadyExists, | ||
| 1478 | error.Unexpected, | ||
| 1479 | error.InvalidUtf8, | ||
| 1480 | error.BadPathName, | ||
| 1481 | error.DeviceBusy, | ||
| 1482 | => return err, | ||
| 1483 | }; | ||
| 1484 | defer dir.close(); | ||
| 1485 | |||
| 1486 | var full_entry_buf = ArrayList(u8).init(allocator); | ||
| 1487 | defer full_entry_buf.deinit(); | ||
| 1488 | |||
| 1489 | while (try dir.next()) |entry| { | ||
| 1490 | try full_entry_buf.resize(full_path.len + entry.name.len + 1); | ||
| 1491 | const full_entry_path = full_entry_buf.toSlice(); | ||
| 1492 | mem.copy(u8, full_entry_path, full_path); | ||
| 1493 | full_entry_path[full_path.len] = path.sep; | ||
| 1494 | mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); | ||
| 1495 | |||
| 1496 | try deleteTree(allocator, full_entry_path); | ||
| 1497 | } | ||
| 1498 | } | ||
| 1499 | return deleteDir(full_path); | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | pub const Dir = struct { | ||
| 1504 | handle: Handle, | ||
| 1505 | allocator: *Allocator, | ||
| 1506 | |||
| 1507 | pub const Handle = switch (builtin.os) { | ||
| 1508 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => struct { | ||
| 1509 | fd: i32, | ||
| 1510 | seek: i64, | ||
| 1511 | buf: []u8, | ||
| 1512 | index: usize, | ||
| 1513 | end_index: usize, | ||
| 1514 | }, | ||
| 1515 | Os.linux => struct { | ||
| 1516 | fd: i32, | ||
| 1517 | buf: []u8, | ||
| 1518 | index: usize, | ||
| 1519 | end_index: usize, | ||
| 1520 | }, | ||
| 1521 | Os.windows => struct { | ||
| 1522 | handle: windows.HANDLE, | ||
| 1523 | find_file_data: windows.WIN32_FIND_DATAW, | ||
| 1524 | first: bool, | ||
| 1525 | name_data: [256]u8, | ||
| 1526 | }, | ||
| 1527 | else => @compileError("unimplemented"), | ||
| 1528 | }; | ||
| 1529 | |||
| 1530 | pub const Entry = struct { | ||
| 1531 | name: []const u8, | ||
| 1532 | kind: Kind, | ||
| 1533 | |||
| 1534 | pub const Kind = enum { | ||
| 1535 | BlockDevice, | ||
| 1536 | CharacterDevice, | ||
| 1537 | Directory, | ||
| 1538 | NamedPipe, | ||
| 1539 | SymLink, | ||
| 1540 | File, | ||
| 1541 | UnixDomainSocket, | ||
| 1542 | Whiteout, | ||
| 1543 | Unknown, | ||
| 1544 | }; | ||
| 1545 | }; | ||
| 1546 | |||
| 1547 | pub const OpenError = error{ | ||
| 1548 | FileNotFound, | ||
| 1549 | NotDir, | ||
| 1550 | AccessDenied, | ||
| 1551 | FileTooBig, | ||
| 1552 | IsDir, | ||
| 1553 | SymLinkLoop, | ||
| 1554 | ProcessFdQuotaExceeded, | ||
| 1555 | NameTooLong, | ||
| 1556 | SystemFdQuotaExceeded, | ||
| 1557 | NoDevice, | ||
| 1558 | SystemResources, | ||
| 1559 | NoSpaceLeft, | ||
| 1560 | PathAlreadyExists, | ||
| 1561 | OutOfMemory, | ||
| 1562 | InvalidUtf8, | ||
| 1563 | BadPathName, | ||
| 1564 | DeviceBusy, | ||
| 1565 | |||
| 1566 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1567 | Unexpected, | ||
| 1568 | }; | ||
| 1569 | |||
| 1570 | /// TODO remove the allocator requirement from this API | ||
| 1571 | pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir { | ||
| 1572 | return Dir{ | ||
| 1573 | .allocator = allocator, | ||
| 1574 | .handle = switch (builtin.os) { | ||
| 1575 | Os.windows => blk: { | ||
| 1576 | var find_file_data: windows.WIN32_FIND_DATAW = undefined; | ||
| 1577 | const handle = try windows_util.windowsFindFirstFile(dir_path, &find_file_data); | ||
| 1578 | break :blk Handle{ | ||
| 1579 | .handle = handle, | ||
| 1580 | .find_file_data = find_file_data, // TODO guaranteed copy elision | ||
| 1581 | .first = true, | ||
| 1582 | .name_data = undefined, | ||
| 1583 | }; | ||
| 1584 | }, | ||
| 1585 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => Handle{ | ||
| 1586 | .fd = try posixOpen( | ||
| 1587 | dir_path, | ||
| 1588 | posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, | ||
| 1589 | 0, | ||
| 1590 | ), | ||
| 1591 | .seek = 0, | ||
| 1592 | .index = 0, | ||
| 1593 | .end_index = 0, | ||
| 1594 | .buf = []u8{}, | ||
| 1595 | }, | ||
| 1596 | Os.linux => Handle{ | ||
| 1597 | .fd = try posixOpen( | ||
| 1598 | dir_path, | ||
| 1599 | posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, | ||
| 1600 | 0, | ||
| 1601 | ), | ||
| 1602 | .index = 0, | ||
| 1603 | .end_index = 0, | ||
| 1604 | .buf = []u8{}, | ||
| 1605 | }, | ||
| 1606 | else => @compileError("unimplemented"), | ||
| 1607 | }, | ||
| 1608 | }; | ||
| 1609 | } | ||
| 1610 | |||
| 1611 | pub fn close(self: *Dir) void { | ||
| 1612 | switch (builtin.os) { | ||
| 1613 | Os.windows => { | ||
| 1614 | _ = windows.FindClose(self.handle.handle); | ||
| 1615 | }, | ||
| 1616 | Os.macosx, Os.ios, Os.linux, Os.freebsd, Os.netbsd => { | ||
| 1617 | self.allocator.free(self.handle.buf); | ||
| 1618 | os.close(self.handle.fd); | ||
| 1619 | }, | ||
| 1620 | else => @compileError("unimplemented"), | ||
| 1621 | } | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | /// Memory such as file names referenced in this returned entry becomes invalid | ||
| 1625 | /// with subsequent calls to next, as well as when this `Dir` is deinitialized. | ||
| 1626 | pub fn next(self: *Dir) !?Entry { | ||
| 1627 | switch (builtin.os) { | ||
| 1628 | Os.linux => return self.nextLinux(), | ||
| 1629 | Os.macosx, Os.ios => return self.nextDarwin(), | ||
| 1630 | Os.windows => return self.nextWindows(), | ||
| 1631 | Os.freebsd => return self.nextFreebsd(), | ||
| 1632 | Os.netbsd => return self.nextFreebsd(), | ||
| 1633 | else => @compileError("unimplemented"), | ||
| 1634 | } | ||
| 1635 | } | ||
| 1636 | |||
| 1637 | fn nextDarwin(self: *Dir) !?Entry { | ||
| 1638 | start_over: while (true) { | ||
| 1639 | if (self.handle.index >= self.handle.end_index) { | ||
| 1640 | if (self.handle.buf.len == 0) { | ||
| 1641 | self.handle.buf = try self.allocator.alloc(u8, page_size); | ||
| 1642 | } | ||
| 1643 | |||
| 1644 | while (true) { | ||
| 1645 | const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); | ||
| 1646 | const err = posix.getErrno(result); | ||
| 1647 | if (err > 0) { | ||
| 1648 | switch (err) { | ||
| 1649 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | ||
| 1650 | posix.EINVAL => { | ||
| 1651 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | ||
| 1652 | continue; | ||
| 1653 | }, | ||
| 1654 | else => return unexpectedErrorPosix(err), | ||
| 1655 | } | ||
| 1656 | } | ||
| 1657 | if (result == 0) return null; | ||
| 1658 | self.handle.index = 0; | ||
| 1659 | self.handle.end_index = result; | ||
| 1660 | break; | ||
| 1661 | } | ||
| 1662 | } | ||
| 1663 | const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); | ||
| 1664 | const next_index = self.handle.index + darwin_entry.d_reclen; | ||
| 1665 | self.handle.index = next_index; | ||
| 1666 | |||
| 1667 | const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; | ||
| 1668 | |||
| 1669 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 1670 | continue :start_over; | ||
| 1671 | } | ||
| 1672 | |||
| 1673 | const entry_kind = switch (darwin_entry.d_type) { | ||
| 1674 | posix.DT_BLK => Entry.Kind.BlockDevice, | ||
| 1675 | posix.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 1676 | posix.DT_DIR => Entry.Kind.Directory, | ||
| 1677 | posix.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 1678 | posix.DT_LNK => Entry.Kind.SymLink, | ||
| 1679 | posix.DT_REG => Entry.Kind.File, | ||
| 1680 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 1681 | posix.DT_WHT => Entry.Kind.Whiteout, | ||
| 1682 | else => Entry.Kind.Unknown, | ||
| 1683 | }; | ||
| 1684 | return Entry{ | ||
| 1685 | .name = name, | ||
| 1686 | .kind = entry_kind, | ||
| 1687 | }; | ||
| 1688 | } | ||
| 1689 | } | ||
| 1690 | |||
| 1691 | fn nextWindows(self: *Dir) !?Entry { | ||
| 1692 | while (true) { | ||
| 1693 | if (self.handle.first) { | ||
| 1694 | self.handle.first = false; | ||
| 1695 | } else { | ||
| 1696 | if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data)) | ||
| 1697 | return null; | ||
| 1698 | } | ||
| 1699 | const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); | ||
| 1700 | if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) | ||
| 1701 | continue; | ||
| 1702 | // Trust that Windows gives us valid UTF-16LE | ||
| 1703 | const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; | ||
| 1704 | const name_utf8 = self.handle.name_data[0..name_utf8_len]; | ||
| 1705 | const kind = blk: { | ||
| 1706 | const attrs = self.handle.find_file_data.dwFileAttributes; | ||
| 1707 | if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory; | ||
| 1708 | if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink; | ||
| 1709 | if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File; | ||
| 1710 | break :blk Entry.Kind.Unknown; | ||
| 1711 | }; | ||
| 1712 | return Entry{ | ||
| 1713 | .name = name_utf8, | ||
| 1714 | .kind = kind, | ||
| 1715 | }; | ||
| 1716 | } | ||
| 1717 | } | ||
| 1718 | |||
| 1719 | fn nextLinux(self: *Dir) !?Entry { | ||
| 1720 | start_over: while (true) { | ||
| 1721 | if (self.handle.index >= self.handle.end_index) { | ||
| 1722 | if (self.handle.buf.len == 0) { | ||
| 1723 | self.handle.buf = try self.allocator.alloc(u8, page_size); | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | while (true) { | ||
| 1727 | const result = posix.getdents64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len); | ||
| 1728 | const err = posix.getErrno(result); | ||
| 1729 | if (err > 0) { | ||
| 1730 | switch (err) { | ||
| 1731 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | ||
| 1732 | posix.EINVAL => { | ||
| 1733 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | ||
| 1734 | continue; | ||
| 1735 | }, | ||
| 1736 | else => return unexpectedErrorPosix(err), | ||
| 1737 | } | ||
| 1738 | } | ||
| 1739 | if (result == 0) return null; | ||
| 1740 | self.handle.index = 0; | ||
| 1741 | self.handle.end_index = result; | ||
| 1742 | break; | ||
| 1743 | } | ||
| 1744 | } | ||
| 1745 | const linux_entry = @ptrCast(*align(1) posix.dirent64, &self.handle.buf[self.handle.index]); | ||
| 1746 | const next_index = self.handle.index + linux_entry.d_reclen; | ||
| 1747 | self.handle.index = next_index; | ||
| 1748 | |||
| 1749 | const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); | ||
| 1750 | |||
| 1751 | // skip . and .. entries | ||
| 1752 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 1753 | continue :start_over; | ||
| 1754 | } | ||
| 1755 | |||
| 1756 | const entry_kind = switch (linux_entry.d_type) { | ||
| 1757 | posix.DT_BLK => Entry.Kind.BlockDevice, | ||
| 1758 | posix.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 1759 | posix.DT_DIR => Entry.Kind.Directory, | ||
| 1760 | posix.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 1761 | posix.DT_LNK => Entry.Kind.SymLink, | ||
| 1762 | posix.DT_REG => Entry.Kind.File, | ||
| 1763 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 1764 | else => Entry.Kind.Unknown, | ||
| 1765 | }; | ||
| 1766 | return Entry{ | ||
| 1767 | .name = name, | ||
| 1768 | .kind = entry_kind, | ||
| 1769 | }; | ||
| 1770 | } | ||
| 1771 | } | ||
| 1772 | |||
| 1773 | fn nextFreebsd(self: *Dir) !?Entry { | ||
| 1774 | start_over: while (true) { | ||
| 1775 | if (self.handle.index >= self.handle.end_index) { | ||
| 1776 | if (self.handle.buf.len == 0) { | ||
| 1777 | self.handle.buf = try self.allocator.alloc(u8, page_size); | ||
| 1778 | } | ||
| 1779 | |||
| 1780 | while (true) { | ||
| 1781 | const result = posix.getdirentries(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek); | ||
| 1782 | const err = posix.getErrno(result); | ||
| 1783 | if (err > 0) { | ||
| 1784 | switch (err) { | ||
| 1785 | posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, | ||
| 1786 | posix.EINVAL => { | ||
| 1787 | self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2); | ||
| 1788 | continue; | ||
| 1789 | }, | ||
| 1790 | else => return unexpectedErrorPosix(err), | ||
| 1791 | } | ||
| 1792 | } | ||
| 1793 | if (result == 0) return null; | ||
| 1794 | self.handle.index = 0; | ||
| 1795 | self.handle.end_index = result; | ||
| 1796 | break; | ||
| 1797 | } | ||
| 1798 | } | ||
| 1799 | const freebsd_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]); | ||
| 1800 | const next_index = self.handle.index + freebsd_entry.d_reclen; | ||
| 1801 | self.handle.index = next_index; | ||
| 1802 | |||
| 1803 | const name = @ptrCast([*]u8, &freebsd_entry.d_name)[0..freebsd_entry.d_namlen]; | ||
| 1804 | |||
| 1805 | if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { | ||
| 1806 | continue :start_over; | ||
| 1807 | } | ||
| 1808 | |||
| 1809 | const entry_kind = switch (freebsd_entry.d_type) { | ||
| 1810 | posix.DT_BLK => Entry.Kind.BlockDevice, | ||
| 1811 | posix.DT_CHR => Entry.Kind.CharacterDevice, | ||
| 1812 | posix.DT_DIR => Entry.Kind.Directory, | ||
| 1813 | posix.DT_FIFO => Entry.Kind.NamedPipe, | ||
| 1814 | posix.DT_LNK => Entry.Kind.SymLink, | ||
| 1815 | posix.DT_REG => Entry.Kind.File, | ||
| 1816 | posix.DT_SOCK => Entry.Kind.UnixDomainSocket, | ||
| 1817 | posix.DT_WHT => Entry.Kind.Whiteout, | ||
| 1818 | else => Entry.Kind.Unknown, | ||
| 1819 | }; | ||
| 1820 | return Entry{ | ||
| 1821 | .name = name, | ||
| 1822 | .kind = entry_kind, | ||
| 1823 | }; | ||
| 1824 | } | ||
| 1825 | } | ||
| 1826 | }; | ||
| 1827 | |||
| 1828 | pub fn changeCurDir(allocator: *Allocator, dir_path: []const u8) !void { | ||
| 1829 | const path_buf = try allocator.alloc(u8, dir_path.len + 1); | ||
| 1830 | defer allocator.free(path_buf); | ||
| 1831 | |||
| 1832 | mem.copy(u8, path_buf, dir_path); | ||
| 1833 | path_buf[dir_path.len] = 0; | ||
| 1834 | |||
| 1835 | const err = posix.getErrno(posix.chdir(path_buf.ptr)); | ||
| 1836 | if (err > 0) { | ||
| 1837 | return switch (err) { | ||
| 1838 | posix.EACCES => error.AccessDenied, | ||
| 1839 | posix.EFAULT => unreachable, | ||
| 1840 | posix.EIO => error.FileSystem, | ||
| 1841 | posix.ELOOP => error.SymLinkLoop, | ||
| 1842 | posix.ENAMETOOLONG => error.NameTooLong, | ||
| 1843 | posix.ENOENT => error.FileNotFound, | ||
| 1844 | posix.ENOMEM => error.SystemResources, | ||
| 1845 | posix.ENOTDIR => error.NotDir, | ||
| 1846 | else => unexpectedErrorPosix(err), | ||
| 1847 | }; | ||
| 1848 | } | ||
| 1849 | } | ||
| 1850 | |||
| 1851 | /// Read value of a symbolic link. | ||
| 1852 | /// The return value is a slice of out_buffer. | ||
| 1853 | pub fn readLinkC(out_buffer: *[posix.PATH_MAX]u8, pathname: [*]const u8) ![]u8 { | ||
| 1854 | const rc = posix.readlink(pathname, out_buffer, out_buffer.len); | ||
| 1855 | const err = posix.getErrno(rc); | ||
| 1856 | switch (err) { | ||
| 1857 | 0 => return out_buffer[0..rc], | ||
| 1858 | posix.EACCES => return error.AccessDenied, | ||
| 1859 | posix.EFAULT => unreachable, | ||
| 1860 | posix.EINVAL => unreachable, | ||
| 1861 | posix.EIO => return error.FileSystem, | ||
| 1862 | posix.ELOOP => return error.SymLinkLoop, | ||
| 1863 | posix.ENAMETOOLONG => unreachable, // out_buffer is at least PATH_MAX | ||
| 1864 | posix.ENOENT => return error.FileNotFound, | ||
| 1865 | posix.ENOMEM => return error.SystemResources, | ||
| 1866 | posix.ENOTDIR => return error.NotDir, | ||
| 1867 | else => return unexpectedErrorPosix(err), | ||
| 1868 | } | ||
| 1869 | } | ||
| 1870 | |||
| 1871 | /// Read value of a symbolic link. | ||
| 1872 | /// The return value is a slice of out_buffer. | ||
| 1873 | pub fn readLink(out_buffer: *[posix.PATH_MAX]u8, file_path: []const u8) ![]u8 { | ||
| 1874 | const file_path_c = try toPosixPath(file_path); | ||
| 1875 | return readLinkC(out_buffer, &file_path_c); | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | pub fn posix_setuid(uid: u32) !void { | ||
| 1879 | const err = posix.getErrno(posix.setuid(uid)); | ||
| 1880 | if (err == 0) return; | ||
| 1881 | return switch (err) { | ||
| 1882 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1883 | posix.EINVAL => error.InvalidUserId, | ||
| 1884 | posix.EPERM => error.PermissionDenied, | ||
| 1885 | else => unexpectedErrorPosix(err), | ||
| 1886 | }; | ||
| 1887 | } | ||
| 1888 | |||
| 1889 | pub fn posix_setreuid(ruid: u32, euid: u32) !void { | ||
| 1890 | const err = posix.getErrno(posix.setreuid(ruid, euid)); | ||
| 1891 | if (err == 0) return; | ||
| 1892 | return switch (err) { | ||
| 1893 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1894 | posix.EINVAL => error.InvalidUserId, | ||
| 1895 | posix.EPERM => error.PermissionDenied, | ||
| 1896 | else => unexpectedErrorPosix(err), | ||
| 1897 | }; | ||
| 1898 | } | ||
| 1899 | |||
| 1900 | pub fn posix_setgid(gid: u32) !void { | ||
| 1901 | const err = posix.getErrno(posix.setgid(gid)); | ||
| 1902 | if (err == 0) return; | ||
| 1903 | return switch (err) { | ||
| 1904 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1905 | posix.EINVAL => error.InvalidUserId, | ||
| 1906 | posix.EPERM => error.PermissionDenied, | ||
| 1907 | else => unexpectedErrorPosix(err), | ||
| 1908 | }; | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | pub fn posix_setregid(rgid: u32, egid: u32) !void { | ||
| 1912 | const err = posix.getErrno(posix.setregid(rgid, egid)); | ||
| 1913 | if (err == 0) return; | ||
| 1914 | return switch (err) { | ||
| 1915 | posix.EAGAIN => error.ResourceLimitReached, | ||
| 1916 | posix.EINVAL => error.InvalidUserId, | ||
| 1917 | posix.EPERM => error.PermissionDenied, | ||
| 1918 | else => unexpectedErrorPosix(err), | ||
| 1919 | }; | ||
| 1920 | } | ||
| 1921 | |||
| 1922 | pub const WindowsGetStdHandleErrs = error{ | ||
| 1923 | NoStdHandles, | ||
| 1924 | |||
| 1925 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 1926 | Unexpected, | ||
| 1927 | }; | ||
| 1928 | |||
| 1929 | pub fn windowsGetStdHandle(handle_id: windows.DWORD) WindowsGetStdHandleErrs!windows.HANDLE { | ||
| 1930 | if (windows.GetStdHandle(handle_id)) |handle| { | ||
| 1931 | if (handle == windows.INVALID_HANDLE_VALUE) { | ||
| 1932 | const err = windows.GetLastError(); | ||
| 1933 | return switch (err) { | ||
| 1934 | else => os.unexpectedErrorWindows(err), | ||
| 1935 | }; | ||
| 1936 | } | ||
| 1937 | return handle; | ||
| 1938 | } else { | ||
| 1939 | return error.NoStdHandles; | ||
| 1940 | } | ||
| 1941 | } | ||
| 1942 | |||
| 1943 | pub const ArgIteratorPosix = struct { | ||
| 1944 | index: usize, | ||
| 1945 | count: usize, | ||
| 1946 | |||
| 1947 | pub fn init() ArgIteratorPosix { | ||
| 1948 | return ArgIteratorPosix{ | ||
| 1949 | .index = 0, | ||
| 1950 | .count = raw.len, | ||
| 1951 | }; | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | pub fn next(self: *ArgIteratorPosix) ?[]const u8 { | ||
| 1955 | if (self.index == self.count) return null; | ||
| 1956 | |||
| 1957 | const s = raw[self.index]; | ||
| 1958 | self.index += 1; | ||
| 1959 | return cstr.toSlice(s); | ||
| 1960 | } | ||
| 1961 | |||
| 1962 | pub fn skip(self: *ArgIteratorPosix) bool { | ||
| 1963 | if (self.index == self.count) return false; | ||
| 1964 | |||
| 1965 | self.index += 1; | ||
| 1966 | return true; | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | /// This is marked as public but actually it's only meant to be used | ||
| 1970 | /// internally by zig's startup code. | ||
| 1971 | pub var raw: [][*]u8 = undefined; | ||
| 1972 | }; | ||
| 1973 | |||
| 1974 | pub const ArgIteratorWindows = struct { | ||
| 1975 | index: usize, | ||
| 1976 | cmd_line: [*]const u8, | ||
| 1977 | in_quote: bool, | ||
| 1978 | quote_count: usize, | ||
| 1979 | seen_quote_count: usize, | ||
| 1980 | |||
| 1981 | pub const NextError = error{OutOfMemory}; | ||
| 1982 | |||
| 1983 | pub fn init() ArgIteratorWindows { | ||
| 1984 | return initWithCmdLine(windows.GetCommandLineA()); | ||
| 1985 | } | ||
| 1986 | |||
| 1987 | pub fn initWithCmdLine(cmd_line: [*]const u8) ArgIteratorWindows { | ||
| 1988 | return ArgIteratorWindows{ | ||
| 1989 | .index = 0, | ||
| 1990 | .cmd_line = cmd_line, | ||
| 1991 | .in_quote = false, | ||
| 1992 | .quote_count = countQuotes(cmd_line), | ||
| 1993 | .seen_quote_count = 0, | ||
| 1994 | }; | ||
| 1995 | } | ||
| 1996 | |||
| 1997 | /// You must free the returned memory when done. | ||
| 1998 | pub fn next(self: *ArgIteratorWindows, allocator: *Allocator) ?(NextError![]u8) { | ||
| 1999 | // march forward over whitespace | ||
| 2000 | while (true) : (self.index += 1) { | ||
| 2001 | const byte = self.cmd_line[self.index]; | ||
| 2002 | switch (byte) { | ||
| 2003 | 0 => return null, | ||
| 2004 | ' ', '\t' => continue, | ||
| 2005 | else => break, | ||
| 2006 | } | ||
| 2007 | } | ||
| 2008 | |||
| 2009 | return self.internalNext(allocator); | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | pub fn skip(self: *ArgIteratorWindows) bool { | ||
| 2013 | // march forward over whitespace | ||
| 2014 | while (true) : (self.index += 1) { | ||
| 2015 | const byte = self.cmd_line[self.index]; | ||
| 2016 | switch (byte) { | ||
| 2017 | 0 => return false, | ||
| 2018 | ' ', '\t' => continue, | ||
| 2019 | else => break, | ||
| 2020 | } | ||
| 2021 | } | ||
| 2022 | |||
| 2023 | var backslash_count: usize = 0; | ||
| 2024 | while (true) : (self.index += 1) { | ||
| 2025 | const byte = self.cmd_line[self.index]; | ||
| 2026 | switch (byte) { | ||
| 2027 | 0 => return true, | ||
| 2028 | '"' => { | ||
| 2029 | const quote_is_real = backslash_count % 2 == 0; | ||
| 2030 | if (quote_is_real) { | ||
| 2031 | self.seen_quote_count += 1; | ||
| 2032 | } | ||
| 2033 | }, | ||
| 2034 | '\\' => { | ||
| 2035 | backslash_count += 1; | ||
| 2036 | }, | ||
| 2037 | ' ', '\t' => { | ||
| 2038 | if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) { | ||
| 2039 | return true; | ||
| 2040 | } | ||
| 2041 | backslash_count = 0; | ||
| 2042 | }, | ||
| 2043 | else => { | ||
| 2044 | backslash_count = 0; | ||
| 2045 | continue; | ||
| 2046 | }, | ||
| 2047 | } | ||
| 2048 | } | ||
| 2049 | } | ||
| 2050 | |||
| 2051 | fn internalNext(self: *ArgIteratorWindows, allocator: *Allocator) NextError![]u8 { | ||
| 2052 | var buf = try Buffer.initSize(allocator, 0); | ||
| 2053 | defer buf.deinit(); | ||
| 2054 | |||
| 2055 | var backslash_count: usize = 0; | ||
| 2056 | while (true) : (self.index += 1) { | ||
| 2057 | const byte = self.cmd_line[self.index]; | ||
| 2058 | switch (byte) { | ||
| 2059 | 0 => return buf.toOwnedSlice(), | ||
| 2060 | '"' => { | ||
| 2061 | const quote_is_real = backslash_count % 2 == 0; | ||
| 2062 | try self.emitBackslashes(&buf, backslash_count / 2); | ||
| 2063 | backslash_count = 0; | ||
| 2064 | |||
| 2065 | if (quote_is_real) { | ||
| 2066 | self.seen_quote_count += 1; | ||
| 2067 | if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) { | ||
| 2068 | try buf.appendByte('"'); | ||
| 2069 | } | ||
| 2070 | } else { | ||
| 2071 | try buf.appendByte('"'); | ||
| 2072 | } | ||
| 2073 | }, | ||
| 2074 | '\\' => { | ||
| 2075 | backslash_count += 1; | ||
| 2076 | }, | ||
| 2077 | ' ', '\t' => { | ||
| 2078 | try self.emitBackslashes(&buf, backslash_count); | ||
| 2079 | backslash_count = 0; | ||
| 2080 | if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) { | ||
| 2081 | try buf.appendByte(byte); | ||
| 2082 | } else { | ||
| 2083 | return buf.toOwnedSlice(); | ||
| 2084 | } | ||
| 2085 | }, | ||
| 2086 | else => { | ||
| 2087 | try self.emitBackslashes(&buf, backslash_count); | ||
| 2088 | backslash_count = 0; | ||
| 2089 | try buf.appendByte(byte); | ||
| 2090 | }, | ||
| 2091 | } | ||
| 2092 | } | ||
| 2093 | } | ||
| 2094 | |||
| 2095 | fn emitBackslashes(self: *ArgIteratorWindows, buf: *Buffer, emit_count: usize) !void { | ||
| 2096 | var i: usize = 0; | ||
| 2097 | while (i < emit_count) : (i += 1) { | ||
| 2098 | try buf.appendByte('\\'); | ||
| 2099 | } | ||
| 2100 | } | ||
| 2101 | |||
| 2102 | fn countQuotes(cmd_line: [*]const u8) usize { | ||
| 2103 | var result: usize = 0; | ||
| 2104 | var backslash_count: usize = 0; | ||
| 2105 | var index: usize = 0; | ||
| 2106 | while (true) : (index += 1) { | ||
| 2107 | const byte = cmd_line[index]; | ||
| 2108 | switch (byte) { | ||
| 2109 | 0 => return result, | ||
| 2110 | '\\' => backslash_count += 1, | ||
| 2111 | '"' => { | ||
| 2112 | result += 1 - (backslash_count % 2); | ||
| 2113 | backslash_count = 0; | ||
| 2114 | }, | ||
| 2115 | else => { | ||
| 2116 | backslash_count = 0; | ||
| 2117 | }, | ||
| 2118 | } | ||
| 2119 | } | ||
| 2120 | } | ||
| 2121 | }; | ||
| 2122 | |||
| 2123 | pub const ArgIterator = struct { | ||
| 2124 | const InnerType = if (builtin.os == Os.windows) ArgIteratorWindows else ArgIteratorPosix; | ||
| 2125 | |||
| 2126 | inner: InnerType, | ||
| 2127 | |||
| 2128 | pub fn init() ArgIterator { | ||
| 2129 | return ArgIterator{ .inner = InnerType.init() }; | ||
| 2130 | } | ||
| 2131 | |||
| 2132 | pub const NextError = ArgIteratorWindows.NextError; | ||
| 2133 | |||
| 2134 | /// You must free the returned memory when done. | ||
| 2135 | pub fn next(self: *ArgIterator, allocator: *Allocator) ?(NextError![]u8) { | ||
| 2136 | if (builtin.os == Os.windows) { | ||
| 2137 | return self.inner.next(allocator); | ||
| 2138 | } else { | ||
| 2139 | return mem.dupe(allocator, u8, self.inner.next() orelse return null); | ||
| 2140 | } | ||
| 2141 | } | ||
| 2142 | |||
| 2143 | /// If you only are targeting posix you can call this and not need an allocator. | ||
| 2144 | pub fn nextPosix(self: *ArgIterator) ?[]const u8 { | ||
| 2145 | return self.inner.next(); | ||
| 2146 | } | ||
| 2147 | |||
| 2148 | /// Parse past 1 argument without capturing it. | ||
| 2149 | /// Returns `true` if skipped an arg, `false` if we are at the end. | ||
| 2150 | pub fn skip(self: *ArgIterator) bool { | ||
| 2151 | return self.inner.skip(); | ||
| 2152 | } | ||
| 2153 | }; | ||
| 2154 | |||
| 2155 | pub fn args() ArgIterator { | ||
| 2156 | return ArgIterator.init(); | ||
| 2157 | } | ||
| 2158 | |||
| 2159 | /// Caller must call argsFree on result. | ||
| 2160 | pub fn argsAlloc(allocator: *mem.Allocator) ![]const []u8 { | ||
| 2161 | // TODO refactor to only make 1 allocation. | ||
| 2162 | var it = args(); | ||
| 2163 | var contents = try Buffer.initSize(allocator, 0); | ||
| 2164 | defer contents.deinit(); | ||
| 2165 | |||
| 2166 | var slice_list = ArrayList(usize).init(allocator); | ||
| 2167 | defer slice_list.deinit(); | ||
| 2168 | |||
| 2169 | while (it.next(allocator)) |arg_or_err| { | ||
| 2170 | const arg = try arg_or_err; | ||
| 2171 | defer allocator.free(arg); | ||
| 2172 | try contents.append(arg); | ||
| 2173 | try slice_list.append(arg.len); | ||
| 2174 | } | ||
| 2175 | |||
| 2176 | const contents_slice = contents.toSliceConst(); | ||
| 2177 | const slice_sizes = slice_list.toSliceConst(); | ||
| 2178 | const slice_list_bytes = try math.mul(usize, @sizeOf([]u8), slice_sizes.len); | ||
| 2179 | const total_bytes = try math.add(usize, slice_list_bytes, contents_slice.len); | ||
| 2180 | const buf = try allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes); | ||
| 2181 | errdefer allocator.free(buf); | ||
| 2182 | |||
| 2183 | const result_slice_list = @bytesToSlice([]u8, buf[0..slice_list_bytes]); | ||
| 2184 | const result_contents = buf[slice_list_bytes..]; | ||
| 2185 | mem.copy(u8, result_contents, contents_slice); | ||
| 2186 | |||
| 2187 | var contents_index: usize = 0; | ||
| 2188 | for (slice_sizes) |len, i| { | ||
| 2189 | const new_index = contents_index + len; | ||
| 2190 | result_slice_list[i] = result_contents[contents_index..new_index]; | ||
| 2191 | contents_index = new_index; | ||
| 2192 | } | ||
| 2193 | |||
| 2194 | return result_slice_list; | ||
| 2195 | } | ||
| 2196 | |||
| 2197 | pub fn argsFree(allocator: *mem.Allocator, args_alloc: []const []u8) void { | ||
| 2198 | var total_bytes: usize = 0; | ||
| 2199 | for (args_alloc) |arg| { | ||
| 2200 | total_bytes += @sizeOf([]u8) + arg.len; | ||
| 2201 | } | ||
| 2202 | const unaligned_allocated_buf = @ptrCast([*]const u8, args_alloc.ptr)[0..total_bytes]; | ||
| 2203 | const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); | ||
| 2204 | return allocator.free(aligned_allocated_buf); | ||
| 2205 | } | ||
| 2206 | |||
| 2207 | test "windows arg parsing" { | ||
| 2208 | testWindowsCmdLine(c"a b\tc d", [][]const u8{ "a", "b", "c", "d" }); | ||
| 2209 | testWindowsCmdLine(c"\"abc\" d e", [][]const u8{ "abc", "d", "e" }); | ||
| 2210 | testWindowsCmdLine(c"a\\\\\\b d\"e f\"g h", [][]const u8{ "a\\\\\\b", "de fg", "h" }); | ||
| 2211 | testWindowsCmdLine(c"a\\\\\\\"b c d", [][]const u8{ "a\\\"b", "c", "d" }); | ||
| 2212 | testWindowsCmdLine(c"a\\\\\\\\\"b c\" d e", [][]const u8{ "a\\\\b c", "d", "e" }); | ||
| 2213 | testWindowsCmdLine(c"a b\tc \"d f", [][]const u8{ "a", "b", "c", "\"d", "f" }); | ||
| 2214 | |||
| 2215 | testWindowsCmdLine(c"\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", [][]const u8{ | ||
| 2216 | ".\\..\\zig-cache\\build", | ||
| 2217 | "bin\\zig.exe", | ||
| 2218 | ".\\..", | ||
| 2219 | ".\\..\\zig-cache", | ||
| 2220 | "--help", | ||
| 2221 | }); | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { | ||
| 2225 | var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); | ||
| 2226 | for (expected_args) |expected_arg| { | ||
| 2227 | const arg = it.next(debug.global_allocator).? catch unreachable; | ||
| 2228 | testing.expectEqualSlices(u8, expected_arg, arg); | ||
| 2229 | } | ||
| 2230 | testing.expect(it.next(debug.global_allocator) == null); | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | // TODO make this a build variable that you can set | ||
| 2234 | const unexpected_error_tracing = false; | ||
| 2235 | const UnexpectedError = error{ | ||
| 2236 | /// The Operating System returned an undocumented error code. | ||
| 2237 | Unexpected, | ||
| 2238 | }; | ||
| 2239 | |||
| 2240 | /// Call this when you made a syscall or something that sets errno | ||
| 2241 | /// and you get an unexpected error. | ||
| 2242 | pub fn unexpectedErrorPosix(errno: usize) UnexpectedError { | ||
| 2243 | if (unexpected_error_tracing) { | ||
| 2244 | debug.warn("unexpected errno: {}\n", errno); | ||
| 2245 | debug.dumpCurrentStackTrace(null); | ||
| 2246 | } | ||
| 2247 | return error.Unexpected; | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | /// Call this when you made a windows DLL call or something that does SetLastError | ||
| 2251 | /// and you get an unexpected error. | ||
| 2252 | pub fn unexpectedErrorWindows(err: windows.DWORD) UnexpectedError { | ||
| 2253 | if (unexpected_error_tracing) { | ||
| 2254 | debug.warn("unexpected GetLastError(): {}\n", err); | ||
| 2255 | @breakpoint(); | ||
| 2256 | debug.dumpCurrentStackTrace(null); | ||
| 2257 | } | ||
| 2258 | return error.Unexpected; | ||
| 2259 | } | ||
| 2260 | |||
| 2261 | pub fn openSelfExe() !os.File { | ||
| 2262 | switch (builtin.os) { | ||
| 2263 | Os.linux => return os.File.openReadC(c"/proc/self/exe"), | ||
| 2264 | Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 2265 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 2266 | const self_exe_path = try selfExePath(&buf); | ||
| 2267 | buf[self_exe_path.len] = 0; | ||
| 2268 | return os.File.openReadC(self_exe_path.ptr); | ||
| 2269 | }, | ||
| 2270 | Os.windows => { | ||
| 2271 | var buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 2272 | const wide_slice = try selfExePathW(&buf); | ||
| 2273 | return os.File.openReadW(wide_slice.ptr); | ||
| 2274 | }, | ||
| 2275 | else => @compileError("Unsupported OS"), | ||
| 2276 | } | ||
| 2277 | } | ||
| 2278 | |||
| 2279 | test "openSelfExe" { | ||
| 2280 | switch (builtin.os) { | ||
| 2281 | Os.linux, Os.macosx, Os.ios, Os.windows, Os.freebsd => (try openSelfExe()).close(), | ||
| 2282 | else => return error.SkipZigTest, // Unsupported OS. | ||
| 2283 | } | ||
| 2284 | } | ||
| 2285 | |||
| 2286 | pub fn selfExePathW(out_buffer: *[windows_util.PATH_MAX_WIDE]u16) ![]u16 { | ||
| 2287 | const casted_len = @intCast(windows.DWORD, out_buffer.len); // TODO shouldn't need this cast | ||
| 2288 | const rc = windows.GetModuleFileNameW(null, out_buffer, casted_len); | ||
| 2289 | assert(rc <= out_buffer.len); | ||
| 2290 | if (rc == 0) { | ||
| 2291 | const err = windows.GetLastError(); | ||
| 2292 | switch (err) { | ||
| 2293 | else => return unexpectedErrorWindows(err), | ||
| 2294 | } | ||
| 2295 | } | ||
| 2296 | return out_buffer[0..rc]; | ||
| 2297 | } | ||
| 2298 | |||
| 2299 | /// Get the path to the current executable. | ||
| 2300 | /// If you only need the directory, use selfExeDirPath. | ||
| 2301 | /// If you only want an open file handle, use openSelfExe. | ||
| 2302 | /// This function may return an error if the current executable | ||
| 2303 | /// was deleted after spawning. | ||
| 2304 | /// Returned value is a slice of out_buffer. | ||
| 2305 | /// | ||
| 2306 | /// On Linux, depends on procfs being mounted. If the currently executing binary has | ||
| 2307 | /// been deleted, the file path looks something like `/a/b/c/exe (deleted)`. | ||
| 2308 | /// TODO make the return type of this a null terminated pointer | ||
| 2309 | pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 { | ||
| 2310 | switch (builtin.os) { | ||
| 2311 | Os.linux => return readLink(out_buffer, "/proc/self/exe"), | ||
| 2312 | Os.freebsd => { | ||
| 2313 | var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 }; | ||
| 2314 | var out_len: usize = out_buffer.len; | ||
| 2315 | const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); | ||
| 2316 | |||
| 2317 | if (err == 0) return mem.toSlice(u8, out_buffer); | ||
| 2318 | |||
| 2319 | return switch (err) { | ||
| 2320 | posix.EFAULT => error.BadAdress, | ||
| 2321 | posix.EPERM => error.PermissionDenied, | ||
| 2322 | else => unexpectedErrorPosix(err), | ||
| 2323 | }; | ||
| 2324 | }, | ||
| 2325 | Os.netbsd => { | ||
| 2326 | var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC_ARGS, -1, posix.KERN_PROC_PATHNAME }; | ||
| 2327 | var out_len: usize = out_buffer.len; | ||
| 2328 | const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0)); | ||
| 2329 | |||
| 2330 | if (err == 0) return mem.toSlice(u8, out_buffer); | ||
| 2331 | |||
| 2332 | return switch (err) { | ||
| 2333 | posix.EFAULT => error.BadAdress, | ||
| 2334 | posix.EPERM => error.PermissionDenied, | ||
| 2335 | else => unexpectedErrorPosix(err), | ||
| 2336 | }; | ||
| 2337 | }, | ||
| 2338 | Os.windows => { | ||
| 2339 | var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined; | ||
| 2340 | const utf16le_slice = try selfExePathW(&utf16le_buf); | ||
| 2341 | // Trust that Windows gives us valid UTF-16LE. | ||
| 2342 | const end_index = std.unicode.utf16leToUtf8(out_buffer, utf16le_slice) catch unreachable; | ||
| 2343 | return out_buffer[0..end_index]; | ||
| 2344 | }, | ||
| 2345 | Os.macosx, Os.ios => { | ||
| 2346 | var u32_len: u32 = @intCast(u32, out_buffer.len); // TODO shouldn't need this cast | ||
| 2347 | const rc = c._NSGetExecutablePath(out_buffer, &u32_len); | ||
| 2348 | if (rc != 0) return error.NameTooLong; | ||
| 2349 | return mem.toSlice(u8, out_buffer); | ||
| 2350 | }, | ||
| 2351 | else => @compileError("Unsupported OS"), | ||
| 2352 | } | ||
| 2353 | } | ||
| 2354 | |||
| 2355 | /// `selfExeDirPath` except allocates the result on the heap. | ||
| 2356 | /// Caller owns returned memory. | ||
| 2357 | pub fn selfExeDirPathAlloc(allocator: *Allocator) ![]u8 { | ||
| 2358 | var buf: [MAX_PATH_BYTES]u8 = undefined; | ||
| 2359 | return mem.dupe(allocator, u8, try selfExeDirPath(&buf)); | ||
| 2360 | } | ||
| 2361 | |||
| 2362 | /// Get the directory path that contains the current executable. | ||
| 2363 | /// Returned value is a slice of out_buffer. | ||
| 2364 | pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) ![]const u8 { | ||
| 2365 | switch (builtin.os) { | ||
| 2366 | Os.linux => { | ||
| 2367 | // If the currently executing binary has been deleted, | ||
| 2368 | // the file path looks something like `/a/b/c/exe (deleted)` | ||
| 2369 | // This path cannot be opened, but it's valid for determining the directory | ||
| 2370 | // the executable was in when it was run. | ||
| 2371 | const full_exe_path = try readLinkC(out_buffer, c"/proc/self/exe"); | ||
| 2372 | // Assume that /proc/self/exe has an absolute path, and therefore dirname | ||
| 2373 | // will not return null. | ||
| 2374 | return path.dirname(full_exe_path).?; | ||
| 2375 | }, | ||
| 2376 | Os.windows, Os.macosx, Os.ios, Os.freebsd, Os.netbsd => { | ||
| 2377 | const self_exe_path = try selfExePath(out_buffer); | ||
| 2378 | // Assume that the OS APIs return absolute paths, and therefore dirname | ||
| 2379 | // will not return null. | ||
| 2380 | return path.dirname(self_exe_path).?; | ||
| 2381 | }, | ||
| 2382 | else => @compileError("Unsupported OS"), | ||
| 2383 | } | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | pub fn isTty(handle: FileHandle) bool { | ||
| 2387 | if (is_windows) { | ||
| 2388 | return windows_util.windowsIsTty(handle); | ||
| 2389 | } else { | ||
| 2390 | if (builtin.link_libc) { | ||
| 2391 | return c.isatty(handle) != 0; | ||
| 2392 | } else { | ||
| 2393 | return posix.isatty(handle); | ||
| 2394 | } | ||
| 2395 | } | ||
| 2396 | } | ||
| 2397 | |||
| 2398 | pub fn supportsAnsiEscapeCodes(handle: FileHandle) bool { | ||
| 2399 | if (is_windows) { | ||
| 2400 | return windows_util.windowsIsCygwinPty(handle); | ||
| 2401 | } else { | ||
| 2402 | if (builtin.link_libc) { | ||
| 2403 | return c.isatty(handle) != 0; | ||
| 2404 | } else { | ||
| 2405 | return posix.isatty(handle); | ||
| 2406 | } | ||
| 2407 | } | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | pub const PosixSocketError = error{ | ||
| 2411 | /// Permission to create a socket of the specified type and/or | ||
| 2412 | /// pro‐tocol is denied. | ||
| 2413 | PermissionDenied, | ||
| 2414 | |||
| 2415 | /// The implementation does not support the specified address family. | ||
| 2416 | AddressFamilyNotSupported, | ||
| 2417 | |||
| 2418 | /// Unknown protocol, or protocol family not available. | ||
| 2419 | ProtocolFamilyNotAvailable, | ||
| 2420 | |||
| 2421 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 2422 | ProcessFdQuotaExceeded, | ||
| 2423 | |||
| 2424 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2425 | SystemFdQuotaExceeded, | ||
| 2426 | |||
| 2427 | /// Insufficient memory is available. The socket cannot be created until sufficient | ||
| 2428 | /// resources are freed. | ||
| 2429 | SystemResources, | ||
| 2430 | |||
| 2431 | /// The protocol type or the specified protocol is not supported within this domain. | ||
| 2432 | ProtocolNotSupported, | ||
| 2433 | }; | ||
| 2434 | |||
| 2435 | pub fn posixSocket(domain: u32, socket_type: u32, protocol: u32) !i32 { | ||
| 2436 | const rc = posix.socket(domain, socket_type, protocol); | ||
| 2437 | const err = posix.getErrno(rc); | ||
| 2438 | switch (err) { | ||
| 2439 | 0 => return @intCast(i32, rc), | ||
| 2440 | posix.EACCES => return PosixSocketError.PermissionDenied, | ||
| 2441 | posix.EAFNOSUPPORT => return PosixSocketError.AddressFamilyNotSupported, | ||
| 2442 | posix.EINVAL => return PosixSocketError.ProtocolFamilyNotAvailable, | ||
| 2443 | posix.EMFILE => return PosixSocketError.ProcessFdQuotaExceeded, | ||
| 2444 | posix.ENFILE => return PosixSocketError.SystemFdQuotaExceeded, | ||
| 2445 | posix.ENOBUFS, posix.ENOMEM => return PosixSocketError.SystemResources, | ||
| 2446 | posix.EPROTONOSUPPORT => return PosixSocketError.ProtocolNotSupported, | ||
| 2447 | else => return unexpectedErrorPosix(err), | ||
| 2448 | } | ||
| 2449 | } | ||
| 2450 | |||
| 2451 | pub const PosixBindError = error{ | ||
| 2452 | /// The address is protected, and the user is not the superuser. | ||
| 2453 | /// For UNIX domain sockets: Search permission is denied on a component | ||
| 2454 | /// of the path prefix. | ||
| 2455 | AccessDenied, | ||
| 2456 | |||
| 2457 | /// The given address is already in use, or in the case of Internet domain sockets, | ||
| 2458 | /// The port number was specified as zero in the socket | ||
| 2459 | /// address structure, but, upon attempting to bind to an ephemeral port, it was | ||
| 2460 | /// determined that all port numbers in the ephemeral port range are currently in | ||
| 2461 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range ip(7). | ||
| 2462 | AddressInUse, | ||
| 2463 | |||
| 2464 | /// A nonexistent interface was requested or the requested address was not local. | ||
| 2465 | AddressNotAvailable, | ||
| 2466 | |||
| 2467 | /// Too many symbolic links were encountered in resolving addr. | ||
| 2468 | SymLinkLoop, | ||
| 2469 | |||
| 2470 | /// addr is too long. | ||
| 2471 | NameTooLong, | ||
| 2472 | |||
| 2473 | /// A component in the directory prefix of the socket pathname does not exist. | ||
| 2474 | FileNotFound, | ||
| 2475 | |||
| 2476 | /// Insufficient kernel memory was available. | ||
| 2477 | SystemResources, | ||
| 2478 | |||
| 2479 | /// A component of the path prefix is not a directory. | ||
| 2480 | NotDir, | ||
| 2481 | |||
| 2482 | /// The socket inode would reside on a read-only filesystem. | ||
| 2483 | ReadOnlyFileSystem, | ||
| 2484 | |||
| 2485 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2486 | Unexpected, | ||
| 2487 | }; | ||
| 2488 | |||
| 2489 | /// addr is `&const T` where T is one of the sockaddr | ||
| 2490 | pub fn posixBind(fd: i32, addr: *const posix.sockaddr) PosixBindError!void { | ||
| 2491 | const rc = posix.bind(fd, addr, @sizeOf(posix.sockaddr)); | ||
| 2492 | const err = posix.getErrno(rc); | ||
| 2493 | switch (err) { | ||
| 2494 | 0 => return, | ||
| 2495 | posix.EACCES => return PosixBindError.AccessDenied, | ||
| 2496 | posix.EADDRINUSE => return PosixBindError.AddressInUse, | ||
| 2497 | posix.EBADF => unreachable, // always a race condition if this error is returned | ||
| 2498 | posix.EINVAL => unreachable, | ||
| 2499 | posix.ENOTSOCK => unreachable, | ||
| 2500 | posix.EADDRNOTAVAIL => return PosixBindError.AddressNotAvailable, | ||
| 2501 | posix.EFAULT => unreachable, | ||
| 2502 | posix.ELOOP => return PosixBindError.SymLinkLoop, | ||
| 2503 | posix.ENAMETOOLONG => return PosixBindError.NameTooLong, | ||
| 2504 | posix.ENOENT => return PosixBindError.FileNotFound, | ||
| 2505 | posix.ENOMEM => return PosixBindError.SystemResources, | ||
| 2506 | posix.ENOTDIR => return PosixBindError.NotDir, | ||
| 2507 | posix.EROFS => return PosixBindError.ReadOnlyFileSystem, | ||
| 2508 | else => return unexpectedErrorPosix(err), | ||
| 2509 | } | ||
| 2510 | } | ||
| 2511 | |||
| 2512 | const PosixListenError = error{ | ||
| 2513 | /// Another socket is already listening on the same port. | ||
| 2514 | /// For Internet domain sockets, the socket referred to by sockfd had not previously | ||
| 2515 | /// been bound to an address and, upon attempting to bind it to an ephemeral port, it | ||
| 2516 | /// was determined that all port numbers in the ephemeral port range are currently in | ||
| 2517 | /// use. See the discussion of /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2518 | AddressInUse, | ||
| 2519 | |||
| 2520 | /// The file descriptor sockfd does not refer to a socket. | ||
| 2521 | FileDescriptorNotASocket, | ||
| 2522 | |||
| 2523 | /// The socket is not of a type that supports the listen() operation. | ||
| 2524 | OperationNotSupported, | ||
| 2525 | |||
| 2526 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2527 | Unexpected, | ||
| 2528 | }; | ||
| 2529 | |||
| 2530 | pub fn posixListen(sockfd: i32, backlog: u32) PosixListenError!void { | ||
| 2531 | const rc = posix.listen(sockfd, backlog); | ||
| 2532 | const err = posix.getErrno(rc); | ||
| 2533 | switch (err) { | ||
| 2534 | 0 => return, | ||
| 2535 | posix.EADDRINUSE => return PosixListenError.AddressInUse, | ||
| 2536 | posix.EBADF => unreachable, | ||
| 2537 | posix.ENOTSOCK => return PosixListenError.FileDescriptorNotASocket, | ||
| 2538 | posix.EOPNOTSUPP => return PosixListenError.OperationNotSupported, | ||
| 2539 | else => return unexpectedErrorPosix(err), | ||
| 2540 | } | ||
| 2541 | } | ||
| 2542 | |||
| 2543 | pub const PosixAcceptError = error{ | ||
| 2544 | ConnectionAborted, | ||
| 2545 | |||
| 2546 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 2547 | ProcessFdQuotaExceeded, | ||
| 2548 | |||
| 2549 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2550 | SystemFdQuotaExceeded, | ||
| 2551 | |||
| 2552 | /// Not enough free memory. This often means that the memory allocation is limited | ||
| 2553 | /// by the socket buffer limits, not by the system memory. | ||
| 2554 | SystemResources, | ||
| 2555 | |||
| 2556 | /// The file descriptor sockfd does not refer to a socket. | ||
| 2557 | FileDescriptorNotASocket, | ||
| 2558 | |||
| 2559 | /// The referenced socket is not of type SOCK_STREAM. | ||
| 2560 | OperationNotSupported, | ||
| 2561 | |||
| 2562 | ProtocolFailure, | ||
| 2563 | |||
| 2564 | /// Firewall rules forbid connection. | ||
| 2565 | BlockedByFirewall, | ||
| 2566 | |||
| 2567 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2568 | Unexpected, | ||
| 2569 | }; | ||
| 2570 | |||
| 2571 | pub fn posixAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { | ||
| 2572 | while (true) { | ||
| 2573 | var sockaddr_size = u32(@sizeOf(posix.sockaddr)); | ||
| 2574 | const rc = posix.accept4(fd, addr, &sockaddr_size, flags); | ||
| 2575 | const err = posix.getErrno(rc); | ||
| 2576 | switch (err) { | ||
| 2577 | 0 => return @intCast(i32, rc), | ||
| 2578 | posix.EINTR => continue, | ||
| 2579 | else => return unexpectedErrorPosix(err), | ||
| 2580 | |||
| 2581 | posix.EAGAIN => unreachable, // use posixAsyncAccept for non-blocking | ||
| 2582 | posix.EBADF => unreachable, // always a race condition | ||
| 2583 | posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, | ||
| 2584 | posix.EFAULT => unreachable, | ||
| 2585 | posix.EINVAL => unreachable, | ||
| 2586 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, | ||
| 2587 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, | ||
| 2588 | posix.ENOBUFS => return PosixAcceptError.SystemResources, | ||
| 2589 | posix.ENOMEM => return PosixAcceptError.SystemResources, | ||
| 2590 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, | ||
| 2591 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, | ||
| 2592 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, | ||
| 2593 | posix.EPERM => return PosixAcceptError.BlockedByFirewall, | ||
| 2594 | } | ||
| 2595 | } | ||
| 2596 | } | ||
| 2597 | |||
| 2598 | /// Returns -1 if would block. | ||
| 2599 | pub fn posixAsyncAccept(fd: i32, addr: *posix.sockaddr, flags: u32) PosixAcceptError!i32 { | ||
| 2600 | while (true) { | ||
| 2601 | var sockaddr_size = u32(@sizeOf(posix.sockaddr)); | ||
| 2602 | const rc = posix.accept4(fd, addr, &sockaddr_size, flags); | ||
| 2603 | const err = posix.getErrno(rc); | ||
| 2604 | switch (err) { | ||
| 2605 | 0 => return @intCast(i32, rc), | ||
| 2606 | posix.EINTR => continue, | ||
| 2607 | else => return unexpectedErrorPosix(err), | ||
| 2608 | |||
| 2609 | posix.EAGAIN => return -1, | ||
| 2610 | posix.EBADF => unreachable, // always a race condition | ||
| 2611 | posix.ECONNABORTED => return PosixAcceptError.ConnectionAborted, | ||
| 2612 | posix.EFAULT => unreachable, | ||
| 2613 | posix.EINVAL => unreachable, | ||
| 2614 | posix.EMFILE => return PosixAcceptError.ProcessFdQuotaExceeded, | ||
| 2615 | posix.ENFILE => return PosixAcceptError.SystemFdQuotaExceeded, | ||
| 2616 | posix.ENOBUFS => return PosixAcceptError.SystemResources, | ||
| 2617 | posix.ENOMEM => return PosixAcceptError.SystemResources, | ||
| 2618 | posix.ENOTSOCK => return PosixAcceptError.FileDescriptorNotASocket, | ||
| 2619 | posix.EOPNOTSUPP => return PosixAcceptError.OperationNotSupported, | ||
| 2620 | posix.EPROTO => return PosixAcceptError.ProtocolFailure, | ||
| 2621 | posix.EPERM => return PosixAcceptError.BlockedByFirewall, | ||
| 2622 | } | ||
| 2623 | } | ||
| 2624 | } | ||
| 2625 | |||
| 2626 | pub const LinuxEpollCreateError = error{ | ||
| 2627 | /// The per-user limit on the number of epoll instances imposed by | ||
| 2628 | /// /proc/sys/fs/epoll/max_user_instances was encountered. See epoll(7) for further | ||
| 2629 | /// details. | ||
| 2630 | /// Or, The per-process limit on the number of open file descriptors has been reached. | ||
| 2631 | ProcessFdQuotaExceeded, | ||
| 2632 | |||
| 2633 | /// The system-wide limit on the total number of open files has been reached. | ||
| 2634 | SystemFdQuotaExceeded, | ||
| 2635 | |||
| 2636 | /// There was insufficient memory to create the kernel object. | ||
| 2637 | SystemResources, | ||
| 2638 | |||
| 2639 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2640 | Unexpected, | ||
| 2641 | }; | ||
| 2642 | |||
| 2643 | pub fn linuxEpollCreate(flags: u32) LinuxEpollCreateError!i32 { | ||
| 2644 | const rc = posix.epoll_create1(flags); | ||
| 2645 | const err = posix.getErrno(rc); | ||
| 2646 | switch (err) { | ||
| 2647 | 0 => return @intCast(i32, rc), | ||
| 2648 | else => return unexpectedErrorPosix(err), | ||
| 2649 | |||
| 2650 | posix.EINVAL => unreachable, | ||
| 2651 | posix.EMFILE => return LinuxEpollCreateError.ProcessFdQuotaExceeded, | ||
| 2652 | posix.ENFILE => return LinuxEpollCreateError.SystemFdQuotaExceeded, | ||
| 2653 | posix.ENOMEM => return LinuxEpollCreateError.SystemResources, | ||
| 2654 | } | ||
| 2655 | } | ||
| 2656 | |||
| 2657 | pub const LinuxEpollCtlError = error{ | ||
| 2658 | /// op was EPOLL_CTL_ADD, and the supplied file descriptor fd is already registered | ||
| 2659 | /// with this epoll instance. | ||
| 2660 | FileDescriptorAlreadyPresentInSet, | ||
| 2661 | |||
| 2662 | /// fd refers to an epoll instance and this EPOLL_CTL_ADD operation would result in a | ||
| 2663 | /// circular loop of epoll instances monitoring one another. | ||
| 2664 | OperationCausesCircularLoop, | ||
| 2665 | |||
| 2666 | /// op was EPOLL_CTL_MOD or EPOLL_CTL_DEL, and fd is not registered with this epoll | ||
| 2667 | /// instance. | ||
| 2668 | FileDescriptorNotRegistered, | ||
| 2669 | |||
| 2670 | /// There was insufficient memory to handle the requested op control operation. | ||
| 2671 | SystemResources, | ||
| 2672 | |||
| 2673 | /// The limit imposed by /proc/sys/fs/epoll/max_user_watches was encountered while | ||
| 2674 | /// trying to register (EPOLL_CTL_ADD) a new file descriptor on an epoll instance. | ||
| 2675 | /// See epoll(7) for further details. | ||
| 2676 | UserResourceLimitReached, | ||
| 2677 | |||
| 2678 | /// The target file fd does not support epoll. This error can occur if fd refers to, | ||
| 2679 | /// for example, a regular file or a directory. | ||
| 2680 | FileDescriptorIncompatibleWithEpoll, | ||
| 2681 | |||
| 2682 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2683 | Unexpected, | ||
| 2684 | }; | ||
| 2685 | |||
| 2686 | pub fn linuxEpollCtl(epfd: i32, op: u32, fd: i32, event: *linux.epoll_event) LinuxEpollCtlError!void { | ||
| 2687 | const rc = posix.epoll_ctl(epfd, op, fd, event); | ||
| 2688 | const err = posix.getErrno(rc); | ||
| 2689 | switch (err) { | ||
| 2690 | 0 => return, | ||
| 2691 | else => return unexpectedErrorPosix(err), | ||
| 2692 | |||
| 2693 | posix.EBADF => unreachable, // always a race condition if this happens | ||
| 2694 | posix.EEXIST => return LinuxEpollCtlError.FileDescriptorAlreadyPresentInSet, | ||
| 2695 | posix.EINVAL => unreachable, | ||
| 2696 | posix.ELOOP => return LinuxEpollCtlError.OperationCausesCircularLoop, | ||
| 2697 | posix.ENOENT => return LinuxEpollCtlError.FileDescriptorNotRegistered, | ||
| 2698 | posix.ENOMEM => return LinuxEpollCtlError.SystemResources, | ||
| 2699 | posix.ENOSPC => return LinuxEpollCtlError.UserResourceLimitReached, | ||
| 2700 | posix.EPERM => return LinuxEpollCtlError.FileDescriptorIncompatibleWithEpoll, | ||
| 2701 | } | ||
| 2702 | } | ||
| 2703 | |||
| 2704 | pub fn linuxEpollWait(epfd: i32, events: []linux.epoll_event, timeout: i32) usize { | ||
| 2705 | while (true) { | ||
| 2706 | const rc = posix.epoll_wait(epfd, events.ptr, @intCast(u32, events.len), timeout); | ||
| 2707 | const err = posix.getErrno(rc); | ||
| 2708 | switch (err) { | ||
| 2709 | 0 => return rc, | ||
| 2710 | posix.EINTR => continue, | ||
| 2711 | posix.EBADF => unreachable, | ||
| 2712 | posix.EFAULT => unreachable, | ||
| 2713 | posix.EINVAL => unreachable, | ||
| 2714 | else => unreachable, | ||
| 2715 | } | ||
| 2716 | } | ||
| 2717 | } | ||
| 2718 | |||
| 2719 | pub const LinuxEventFdError = error{ | ||
| 2720 | InvalidFlagValue, | ||
| 2721 | SystemResources, | ||
| 2722 | ProcessFdQuotaExceeded, | ||
| 2723 | SystemFdQuotaExceeded, | ||
| 2724 | |||
| 2725 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2726 | Unexpected, | ||
| 2727 | }; | ||
| 2728 | |||
| 2729 | pub fn linuxEventFd(initval: u32, flags: u32) LinuxEventFdError!i32 { | ||
| 2730 | const rc = posix.eventfd(initval, flags); | ||
| 2731 | const err = posix.getErrno(rc); | ||
| 2732 | switch (err) { | ||
| 2733 | 0 => return @intCast(i32, rc), | ||
| 2734 | else => return unexpectedErrorPosix(err), | ||
| 2735 | |||
| 2736 | posix.EINVAL => return LinuxEventFdError.InvalidFlagValue, | ||
| 2737 | posix.EMFILE => return LinuxEventFdError.ProcessFdQuotaExceeded, | ||
| 2738 | posix.ENFILE => return LinuxEventFdError.SystemFdQuotaExceeded, | ||
| 2739 | posix.ENODEV => return LinuxEventFdError.SystemResources, | ||
| 2740 | posix.ENOMEM => return LinuxEventFdError.SystemResources, | ||
| 2741 | } | ||
| 2742 | } | ||
| 2743 | |||
| 2744 | pub const PosixGetSockNameError = error{ | ||
| 2745 | /// Insufficient resources were available in the system to perform the operation. | ||
| 2746 | SystemResources, | ||
| 2747 | |||
| 2748 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2749 | Unexpected, | ||
| 2750 | }; | ||
| 2751 | |||
| 2752 | pub fn posixGetSockName(sockfd: i32) PosixGetSockNameError!posix.sockaddr { | ||
| 2753 | var addr: posix.sockaddr = undefined; | ||
| 2754 | var addrlen: posix.socklen_t = @sizeOf(posix.sockaddr); | ||
| 2755 | const rc = posix.getsockname(sockfd, &addr, &addrlen); | ||
| 2756 | const err = posix.getErrno(rc); | ||
| 2757 | switch (err) { | ||
| 2758 | 0 => return addr, | ||
| 2759 | else => return unexpectedErrorPosix(err), | ||
| 2760 | |||
| 2761 | posix.EBADF => unreachable, | ||
| 2762 | posix.EFAULT => unreachable, | ||
| 2763 | posix.EINVAL => unreachable, | ||
| 2764 | posix.ENOTSOCK => unreachable, | ||
| 2765 | posix.ENOBUFS => return PosixGetSockNameError.SystemResources, | ||
| 2766 | } | ||
| 2767 | } | ||
| 2768 | |||
| 2769 | pub const PosixConnectError = error{ | ||
| 2770 | /// For UNIX domain sockets, which are identified by pathname: Write permission is denied on the socket | ||
| 2771 | /// file, or search permission is denied for one of the directories in the path prefix. | ||
| 2772 | /// or | ||
| 2773 | /// The user tried to connect to a broadcast address without having the socket broadcast flag enabled or | ||
| 2774 | /// the connection request failed because of a local firewall rule. | ||
| 2775 | PermissionDenied, | ||
| 2776 | |||
| 2777 | /// Local address is already in use. | ||
| 2778 | AddressInUse, | ||
| 2779 | |||
| 2780 | /// (Internet domain sockets) The socket referred to by sockfd had not previously been bound to an | ||
| 2781 | /// address and, upon attempting to bind it to an ephemeral port, it was determined that all port numbers | ||
| 2782 | /// in the ephemeral port range are currently in use. See the discussion of | ||
| 2783 | /// /proc/sys/net/ipv4/ip_local_port_range in ip(7). | ||
| 2784 | AddressNotAvailable, | ||
| 2785 | |||
| 2786 | /// The passed address didn't have the correct address family in its sa_family field. | ||
| 2787 | AddressFamilyNotSupported, | ||
| 2788 | |||
| 2789 | /// Insufficient entries in the routing cache. | ||
| 2790 | SystemResources, | ||
| 2791 | |||
| 2792 | /// A connect() on a stream socket found no one listening on the remote address. | ||
| 2793 | ConnectionRefused, | ||
| 2794 | |||
| 2795 | /// Network is unreachable. | ||
| 2796 | NetworkUnreachable, | ||
| 2797 | |||
| 2798 | /// Timeout while attempting connection. The server may be too busy to accept new connections. Note | ||
| 2799 | /// that for IP sockets the timeout may be very long when syncookies are enabled on the server. | ||
| 2800 | ConnectionTimedOut, | ||
| 2801 | |||
| 2802 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 2803 | Unexpected, | ||
| 2804 | }; | ||
| 2805 | |||
| 2806 | pub fn posixConnect(sockfd: i32, sockaddr: *const posix.sockaddr) PosixConnectError!void { | ||
| 2807 | while (true) { | ||
| 2808 | const rc = posix.connect(sockfd, sockaddr, @sizeOf(posix.sockaddr)); | ||
| 2809 | const err = posix.getErrno(rc); | ||
| 2810 | switch (err) { | ||
| 2811 | 0 => return, | ||
| 2812 | else => return unexpectedErrorPosix(err), | ||
| 2813 | |||
| 2814 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2815 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2816 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2817 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2818 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2819 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2820 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2821 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2822 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2823 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2824 | posix.EINPROGRESS => unreachable, // The socket is nonblocking and the connection cannot be completed immediately. | ||
| 2825 | posix.EINTR => continue, | ||
| 2826 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2827 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2828 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2829 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2830 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2831 | } | ||
| 2832 | } | ||
| 2833 | } | ||
| 2834 | |||
| 2835 | /// Same as posixConnect except it is for blocking socket file descriptors. | ||
| 2836 | /// It expects to receive EINPROGRESS. | ||
| 2837 | pub fn posixConnectAsync(sockfd: i32, sockaddr: *const c_void, len: u32) PosixConnectError!void { | ||
| 2838 | while (true) { | ||
| 2839 | const rc = posix.connect(sockfd, sockaddr, len); | ||
| 2840 | const err = posix.getErrno(rc); | ||
| 2841 | switch (err) { | ||
| 2842 | 0, posix.EINPROGRESS => return, | ||
| 2843 | else => return unexpectedErrorPosix(err), | ||
| 2844 | |||
| 2845 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2846 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2847 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2848 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2849 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2850 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2851 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2852 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2853 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2854 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2855 | posix.EINTR => continue, | ||
| 2856 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2857 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2858 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2859 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2860 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2861 | } | ||
| 2862 | } | ||
| 2863 | } | ||
| 2864 | |||
| 2865 | pub fn posixGetSockOptConnectError(sockfd: i32) PosixConnectError!void { | ||
| 2866 | var err_code: i32 = undefined; | ||
| 2867 | var size: u32 = @sizeOf(i32); | ||
| 2868 | const rc = posix.getsockopt(sockfd, posix.SOL_SOCKET, posix.SO_ERROR, @ptrCast([*]u8, &err_code), &size); | ||
| 2869 | assert(size == 4); | ||
| 2870 | const err = posix.getErrno(rc); | ||
| 2871 | switch (err) { | ||
| 2872 | 0 => switch (err_code) { | ||
| 2873 | 0 => return, | ||
| 2874 | else => return unexpectedErrorPosix(err), | ||
| 2875 | |||
| 2876 | posix.EACCES => return PosixConnectError.PermissionDenied, | ||
| 2877 | posix.EPERM => return PosixConnectError.PermissionDenied, | ||
| 2878 | posix.EADDRINUSE => return PosixConnectError.AddressInUse, | ||
| 2879 | posix.EADDRNOTAVAIL => return PosixConnectError.AddressNotAvailable, | ||
| 2880 | posix.EAFNOSUPPORT => return PosixConnectError.AddressFamilyNotSupported, | ||
| 2881 | posix.EAGAIN => return PosixConnectError.SystemResources, | ||
| 2882 | posix.EALREADY => unreachable, // The socket is nonblocking and a previous connection attempt has not yet been completed. | ||
| 2883 | posix.EBADF => unreachable, // sockfd is not a valid open file descriptor. | ||
| 2884 | posix.ECONNREFUSED => return PosixConnectError.ConnectionRefused, | ||
| 2885 | posix.EFAULT => unreachable, // The socket structure address is outside the user's address space. | ||
| 2886 | posix.EISCONN => unreachable, // The socket is already connected. | ||
| 2887 | posix.ENETUNREACH => return PosixConnectError.NetworkUnreachable, | ||
| 2888 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2889 | posix.EPROTOTYPE => unreachable, // The socket type does not support the requested communications protocol. | ||
| 2890 | posix.ETIMEDOUT => return PosixConnectError.ConnectionTimedOut, | ||
| 2891 | }, | ||
| 2892 | else => return unexpectedErrorPosix(err), | ||
| 2893 | posix.EBADF => unreachable, // The argument sockfd is not a valid file descriptor. | ||
| 2894 | posix.EFAULT => unreachable, // The address pointed to by optval or optlen is not in a valid part of the process address space. | ||
| 2895 | posix.EINVAL => unreachable, | ||
| 2896 | posix.ENOPROTOOPT => unreachable, // The option is unknown at the level indicated. | ||
| 2897 | posix.ENOTSOCK => unreachable, // The file descriptor sockfd does not refer to a socket. | ||
| 2898 | } | ||
| 2899 | } | ||
| 2900 | |||
| 2901 | pub const Thread = struct { | ||
| 2902 | data: Data, | ||
| 2903 | |||
| 2904 | pub const use_pthreads = is_posix and builtin.link_libc; | ||
| 2905 | |||
| 2906 | /// Represents a kernel thread handle. | ||
| 2907 | /// May be an integer or a pointer depending on the platform. | ||
| 2908 | /// On Linux and POSIX, this is the same as Id. | ||
| 2909 | pub const Handle = if (use_pthreads) | ||
| 2910 | c.pthread_t | ||
| 2911 | else switch (builtin.os) { | ||
| 2912 | builtin.Os.linux => i32, | ||
| 2913 | builtin.Os.windows => windows.HANDLE, | ||
| 2914 | else => @compileError("Unsupported OS"), | ||
| 2915 | }; | ||
| 2916 | |||
| 2917 | /// Represents a unique ID per thread. | ||
| 2918 | /// May be an integer or pointer depending on the platform. | ||
| 2919 | /// On Linux and POSIX, this is the same as Handle. | ||
| 2920 | pub const Id = switch (builtin.os) { | ||
| 2921 | builtin.Os.windows => windows.DWORD, | ||
| 2922 | else => Handle, | ||
| 2923 | }; | ||
| 2924 | |||
| 2925 | pub const Data = if (use_pthreads) | ||
| 2926 | struct { | ||
| 2927 | handle: Thread.Handle, | ||
| 2928 | mmap_addr: usize, | ||
| 2929 | mmap_len: usize, | ||
| 2930 | } | ||
| 2931 | else switch (builtin.os) { | ||
| 2932 | builtin.Os.linux => struct { | ||
| 2933 | handle: Thread.Handle, | ||
| 2934 | mmap_addr: usize, | ||
| 2935 | mmap_len: usize, | ||
| 2936 | tls_end_addr: usize, | ||
| 2937 | }, | ||
| 2938 | builtin.Os.windows => struct { | ||
| 2939 | handle: Thread.Handle, | ||
| 2940 | alloc_start: *c_void, | ||
| 2941 | heap_handle: windows.HANDLE, | ||
| 2942 | }, | ||
| 2943 | else => @compileError("Unsupported OS"), | ||
| 2944 | }; | ||
| 2945 | |||
| 2946 | /// Returns the ID of the calling thread. | ||
| 2947 | /// Makes a syscall every time the function is called. | ||
| 2948 | /// On Linux and POSIX, this Id is the same as a Handle. | ||
| 2949 | pub fn getCurrentId() Id { | ||
| 2950 | if (use_pthreads) { | ||
| 2951 | return c.pthread_self(); | ||
| 2952 | } else | ||
| 2953 | return switch (builtin.os) { | ||
| 2954 | builtin.Os.linux => linux.gettid(), | ||
| 2955 | builtin.Os.windows => windows.GetCurrentThreadId(), | ||
| 2956 | else => @compileError("Unsupported OS"), | ||
| 2957 | }; | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | /// Returns the handle of this thread. | ||
| 2961 | /// On Linux and POSIX, this is the same as Id. | ||
| 2962 | pub fn handle(self: Thread) Handle { | ||
| 2963 | return self.data.handle; | ||
| 2964 | } | ||
| 2965 | |||
| 2966 | pub fn wait(self: *const Thread) void { | ||
| 2967 | if (use_pthreads) { | ||
| 2968 | const err = c.pthread_join(self.data.handle, null); | ||
| 2969 | switch (err) { | ||
| 2970 | 0 => {}, | ||
| 2971 | posix.EINVAL => unreachable, | ||
| 2972 | posix.ESRCH => unreachable, | ||
| 2973 | posix.EDEADLK => unreachable, | ||
| 2974 | else => unreachable, | ||
| 2975 | } | ||
| 2976 | assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); | ||
| 2977 | } else switch (builtin.os) { | ||
| 2978 | builtin.Os.linux => { | ||
| 2979 | while (true) { | ||
| 2980 | const pid_value = @atomicLoad(i32, &self.data.handle, builtin.AtomicOrder.SeqCst); | ||
| 2981 | if (pid_value == 0) break; | ||
| 2982 | const rc = linux.futex_wait(&self.data.handle, linux.FUTEX_WAIT, pid_value, null); | ||
| 2983 | switch (linux.getErrno(rc)) { | ||
| 2984 | 0 => continue, | ||
| 2985 | posix.EINTR => continue, | ||
| 2986 | posix.EAGAIN => continue, | ||
| 2987 | else => unreachable, | ||
| 2988 | } | ||
| 2989 | } | ||
| 2990 | assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0); | ||
| 2991 | }, | ||
| 2992 | builtin.Os.windows => { | ||
| 2993 | assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0); | ||
| 2994 | assert(windows.CloseHandle(self.data.handle) != 0); | ||
| 2995 | assert(windows.HeapFree(self.data.heap_handle, 0, self.data.alloc_start) != 0); | ||
| 2996 | }, | ||
| 2997 | else => @compileError("Unsupported OS"), | ||
| 2998 | } | ||
| 2999 | } | ||
| 3000 | }; | ||
| 3001 | |||
| 3002 | pub const SpawnThreadError = error{ | ||
| 3003 | /// A system-imposed limit on the number of threads was encountered. | ||
| 3004 | /// There are a number of limits that may trigger this error: | ||
| 3005 | /// * the RLIMIT_NPROC soft resource limit (set via setrlimit(2)), | ||
| 3006 | /// which limits the number of processes and threads for a real | ||
| 3007 | /// user ID, was reached; | ||
| 3008 | /// * the kernel's system-wide limit on the number of processes and | ||
| 3009 | /// threads, /proc/sys/kernel/threads-max, was reached (see | ||
| 3010 | /// proc(5)); | ||
| 3011 | /// * the maximum number of PIDs, /proc/sys/kernel/pid_max, was | ||
| 3012 | /// reached (see proc(5)); or | ||
| 3013 | /// * the PID limit (pids.max) imposed by the cgroup "process num‐ | ||
| 3014 | /// ber" (PIDs) controller was reached. | ||
| 3015 | ThreadQuotaExceeded, | ||
| 3016 | |||
| 3017 | /// The kernel cannot allocate sufficient memory to allocate a task structure | ||
| 3018 | /// for the child, or to copy those parts of the caller's context that need to | ||
| 3019 | /// be copied. | ||
| 3020 | SystemResources, | ||
| 3021 | |||
| 3022 | /// Not enough userland memory to spawn the thread. | ||
| 3023 | OutOfMemory, | ||
| 3024 | |||
| 3025 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3026 | Unexpected, | ||
| 3027 | }; | ||
| 3028 | |||
| 3029 | pub var linux_tls_phdr: ?*std.elf.Phdr = null; | ||
| 3030 | pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is | ||
| 3031 | |||
| 3032 | /// caller must call wait on the returned thread | ||
| 3033 | /// fn startFn(@typeOf(context)) T | ||
| 3034 | /// where T is u8, noreturn, void, or !void | ||
| 3035 | /// caller must call wait on the returned thread | ||
| 3036 | pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread { | ||
| 3037 | if (builtin.single_threaded) @compileError("cannot spawn thread when building in single-threaded mode"); | ||
| 3038 | // TODO compile-time call graph analysis to determine stack upper bound | ||
| 3039 | // https://github.com/ziglang/zig/issues/157 | ||
| 3040 | const default_stack_size = 8 * 1024 * 1024; | ||
| 3041 | |||
| 3042 | const Context = @typeOf(context); | ||
| 3043 | comptime assert(@ArgType(@typeOf(startFn), 0) == Context); | ||
| 3044 | |||
| 3045 | if (builtin.os == builtin.Os.windows) { | ||
| 3046 | const WinThread = struct { | ||
| 3047 | const OuterContext = struct { | ||
| 3048 | thread: Thread, | ||
| 3049 | inner: Context, | ||
| 3050 | }; | ||
| 3051 | extern fn threadMain(raw_arg: windows.LPVOID) windows.DWORD { | ||
| 3052 | const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*; | ||
| 3053 | switch (@typeId(@typeOf(startFn).ReturnType)) { | ||
| 3054 | builtin.TypeId.Int => { | ||
| 3055 | return startFn(arg); | ||
| 3056 | }, | ||
| 3057 | builtin.TypeId.Void => { | ||
| 3058 | startFn(arg); | ||
| 3059 | return 0; | ||
| 3060 | }, | ||
| 3061 | else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), | ||
| 3062 | } | ||
| 3063 | } | ||
| 3064 | }; | ||
| 3065 | |||
| 3066 | const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory; | ||
| 3067 | const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); | ||
| 3068 | const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory; | ||
| 3069 | errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); | ||
| 3070 | const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; | ||
| 3071 | const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; | ||
| 3072 | outer_context.* = WinThread.OuterContext{ | ||
| 3073 | .thread = Thread{ | ||
| 3074 | .data = Thread.Data{ | ||
| 3075 | .heap_handle = heap_handle, | ||
| 3076 | .alloc_start = bytes_ptr, | ||
| 3077 | .handle = undefined, | ||
| 3078 | }, | ||
| 3079 | }, | ||
| 3080 | .inner = context, | ||
| 3081 | }; | ||
| 3082 | |||
| 3083 | const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); | ||
| 3084 | outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse { | ||
| 3085 | const err = windows.GetLastError(); | ||
| 3086 | return switch (err) { | ||
| 3087 | else => os.unexpectedErrorWindows(err), | ||
| 3088 | }; | ||
| 3089 | }; | ||
| 3090 | return &outer_context.thread; | ||
| 3091 | } | ||
| 3092 | |||
| 3093 | const MainFuncs = struct { | ||
| 3094 | extern fn linuxThreadMain(ctx_addr: usize) u8 { | ||
| 3095 | const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*; | ||
| 3096 | |||
| 3097 | switch (@typeId(@typeOf(startFn).ReturnType)) { | ||
| 3098 | builtin.TypeId.Int => { | ||
| 3099 | return startFn(arg); | ||
| 3100 | }, | ||
| 3101 | builtin.TypeId.Void => { | ||
| 3102 | startFn(arg); | ||
| 3103 | return 0; | ||
| 3104 | }, | ||
| 3105 | else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"), | ||
| 3106 | } | ||
| 3107 | } | ||
| 3108 | extern fn posixThreadMain(ctx: ?*c_void) ?*c_void { | ||
| 3109 | if (@sizeOf(Context) == 0) { | ||
| 3110 | _ = startFn({}); | ||
| 3111 | return null; | ||
| 3112 | } else { | ||
| 3113 | _ = startFn(@ptrCast(*const Context, @alignCast(@alignOf(Context), ctx)).*); | ||
| 3114 | return null; | ||
| 3115 | } | ||
| 3116 | } | ||
| 3117 | }; | ||
| 3118 | |||
| 3119 | const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0; | ||
| 3120 | |||
| 3121 | var stack_end_offset: usize = undefined; | ||
| 3122 | var thread_start_offset: usize = undefined; | ||
| 3123 | var context_start_offset: usize = undefined; | ||
| 3124 | var tls_start_offset: usize = undefined; | ||
| 3125 | const mmap_len = blk: { | ||
| 3126 | // First in memory will be the stack, which grows downwards. | ||
| 3127 | var l: usize = mem.alignForward(default_stack_size, os.page_size); | ||
| 3128 | stack_end_offset = l; | ||
| 3129 | // Above the stack, so that it can be in the same mmap call, put the Thread object. | ||
| 3130 | l = mem.alignForward(l, @alignOf(Thread)); | ||
| 3131 | thread_start_offset = l; | ||
| 3132 | l += @sizeOf(Thread); | ||
| 3133 | // Next, the Context object. | ||
| 3134 | if (@sizeOf(Context) != 0) { | ||
| 3135 | l = mem.alignForward(l, @alignOf(Context)); | ||
| 3136 | context_start_offset = l; | ||
| 3137 | l += @sizeOf(Context); | ||
| 3138 | } | ||
| 3139 | // Finally, the Thread Local Storage, if any. | ||
| 3140 | if (!Thread.use_pthreads) { | ||
| 3141 | if (linux_tls_phdr) |tls_phdr| { | ||
| 3142 | l = mem.alignForward(l, tls_phdr.p_align); | ||
| 3143 | tls_start_offset = l; | ||
| 3144 | l += tls_phdr.p_memsz; | ||
| 3145 | } | ||
| 3146 | } | ||
| 3147 | break :blk l; | ||
| 3148 | }; | ||
| 3149 | const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0); | ||
| 3150 | if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory; | ||
| 3151 | errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0); | ||
| 3152 | |||
| 3153 | const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset)); | ||
| 3154 | thread_ptr.data.mmap_addr = mmap_addr; | ||
| 3155 | thread_ptr.data.mmap_len = mmap_len; | ||
| 3156 | |||
| 3157 | var arg: usize = undefined; | ||
| 3158 | if (@sizeOf(Context) != 0) { | ||
| 3159 | arg = mmap_addr + context_start_offset; | ||
| 3160 | const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg)); | ||
| 3161 | context_ptr.* = context; | ||
| 3162 | } | ||
| 3163 | |||
| 3164 | if (Thread.use_pthreads) { | ||
| 3165 | // use pthreads | ||
| 3166 | var attr: c.pthread_attr_t = undefined; | ||
| 3167 | if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources; | ||
| 3168 | defer assert(c.pthread_attr_destroy(&attr) == 0); | ||
| 3169 | |||
| 3170 | assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0); | ||
| 3171 | |||
| 3172 | const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); | ||
| 3173 | switch (err) { | ||
| 3174 | 0 => return thread_ptr, | ||
| 3175 | posix.EAGAIN => return SpawnThreadError.SystemResources, | ||
| 3176 | posix.EPERM => unreachable, | ||
| 3177 | posix.EINVAL => unreachable, | ||
| 3178 | else => return unexpectedErrorPosix(@intCast(usize, err)), | ||
| 3179 | } | ||
| 3180 | } else if (builtin.os == builtin.Os.linux) { | ||
| 3181 | var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | | ||
| 3182 | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | | ||
| 3183 | posix.CLONE_DETACHED; | ||
| 3184 | var newtls: usize = undefined; | ||
| 3185 | if (linux_tls_phdr) |tls_phdr| { | ||
| 3186 | @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz); | ||
| 3187 | thread_ptr.data.tls_end_addr = mmap_addr + mmap_len; | ||
| 3188 | newtls = @ptrToInt(&thread_ptr.data.tls_end_addr); | ||
| 3189 | flags |= posix.CLONE_SETTLS; | ||
| 3190 | } | ||
| 3191 | const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle); | ||
| 3192 | const err = posix.getErrno(rc); | ||
| 3193 | switch (err) { | ||
| 3194 | 0 => return thread_ptr, | ||
| 3195 | posix.EAGAIN => return SpawnThreadError.ThreadQuotaExceeded, | ||
| 3196 | posix.EINVAL => unreachable, | ||
| 3197 | posix.ENOMEM => return SpawnThreadError.SystemResources, | ||
| 3198 | posix.ENOSPC => unreachable, | ||
| 3199 | posix.EPERM => unreachable, | ||
| 3200 | posix.EUSERS => unreachable, | ||
| 3201 | else => return unexpectedErrorPosix(err), | ||
| 3202 | } | ||
| 3203 | } else { | ||
| 3204 | @compileError("Unsupported OS"); | ||
| 3205 | } | ||
| 3206 | } | ||
| 3207 | |||
| 3208 | pub fn posixWait(pid: i32) i32 { | ||
| 3209 | var status: i32 = undefined; | ||
| 3210 | while (true) { | ||
| 3211 | const err = posix.getErrno(posix.waitpid(pid, &status, 0)); | ||
| 3212 | switch (err) { | ||
| 3213 | 0 => return status, | ||
| 3214 | posix.EINTR => continue, | ||
| 3215 | posix.ECHILD => unreachable, // The process specified does not exist. It would be a race condition to handle this error. | ||
| 3216 | posix.EINVAL => unreachable, // The options argument was invalid | ||
| 3217 | else => unreachable, | ||
| 3218 | } | ||
| 3219 | } | ||
| 3220 | } | ||
| 3221 | |||
| 3222 | pub fn posixFStat(fd: i32) !posix.Stat { | ||
| 3223 | var stat: posix.Stat = undefined; | ||
| 3224 | const err = posix.getErrno(posix.fstat(fd, &stat)); | ||
| 3225 | if (err > 0) { | ||
| 3226 | return switch (err) { | ||
| 3227 | // We do not make this an error code because if you get EBADF it's always a bug, | ||
| 3228 | // since the fd could have been reused. | ||
| 3229 | posix.EBADF => unreachable, | ||
| 3230 | posix.ENOMEM => error.SystemResources, | ||
| 3231 | else => os.unexpectedErrorPosix(err), | ||
| 3232 | }; | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | return stat; | ||
| 3236 | } | ||
| 3237 | |||
| 3238 | pub const CpuCountError = error{ | ||
| 3239 | OutOfMemory, | ||
| 3240 | PermissionDenied, | ||
| 3241 | |||
| 3242 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3243 | Unexpected, | ||
| 3244 | }; | ||
| 3245 | |||
| 3246 | pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize { | ||
| 3247 | switch (builtin.os) { | ||
| 3248 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { | ||
| 3249 | var count: c_int = undefined; | ||
| 3250 | var count_len: usize = @sizeOf(c_int); | ||
| 3251 | const rc = posix.sysctlbyname(switch (builtin.os) { | ||
| 3252 | builtin.Os.macosx => c"hw.logicalcpu", | ||
| 3253 | else => c"hw.ncpu", | ||
| 3254 | }, @ptrCast(*c_void, &count), &count_len, null, 0); | ||
| 3255 | const err = posix.getErrno(rc); | ||
| 3256 | switch (err) { | ||
| 3257 | 0 => return @intCast(usize, count), | ||
| 3258 | posix.EFAULT => unreachable, | ||
| 3259 | posix.EINVAL => unreachable, | ||
| 3260 | posix.ENOMEM => return CpuCountError.OutOfMemory, | ||
| 3261 | posix.ENOTDIR => unreachable, | ||
| 3262 | posix.EISDIR => unreachable, | ||
| 3263 | posix.ENOENT => unreachable, | ||
| 3264 | posix.EPERM => unreachable, | ||
| 3265 | else => return os.unexpectedErrorPosix(err), | ||
| 3266 | } | ||
| 3267 | }, | ||
| 3268 | builtin.Os.linux => { | ||
| 3269 | const usize_count = 16; | ||
| 3270 | const allocator = std.heap.stackFallback(usize_count * @sizeOf(usize), fallback_allocator).get(); | ||
| 3271 | |||
| 3272 | var set = try allocator.alloc(usize, usize_count); | ||
| 3273 | defer allocator.free(set); | ||
| 3274 | |||
| 3275 | while (true) { | ||
| 3276 | const rc = posix.sched_getaffinity(0, set); | ||
| 3277 | const err = posix.getErrno(rc); | ||
| 3278 | switch (err) { | ||
| 3279 | 0 => { | ||
| 3280 | if (rc < set.len * @sizeOf(usize)) { | ||
| 3281 | const result = set[0 .. rc / @sizeOf(usize)]; | ||
| 3282 | var sum: usize = 0; | ||
| 3283 | for (result) |x| { | ||
| 3284 | sum += @popCount(x); | ||
| 3285 | } | ||
| 3286 | return sum; | ||
| 3287 | } else { | ||
| 3288 | set = try allocator.realloc(usize, set, set.len * 2); | ||
| 3289 | continue; | ||
| 3290 | } | ||
| 3291 | }, | ||
| 3292 | posix.EFAULT => unreachable, | ||
| 3293 | posix.EINVAL => unreachable, | ||
| 3294 | posix.EPERM => return CpuCountError.PermissionDenied, | ||
| 3295 | posix.ESRCH => unreachable, | ||
| 3296 | else => return os.unexpectedErrorPosix(err), | ||
| 3297 | } | ||
| 3298 | } | ||
| 3299 | }, | ||
| 3300 | builtin.Os.windows => { | ||
| 3301 | var system_info: windows.SYSTEM_INFO = undefined; | ||
| 3302 | windows.GetSystemInfo(&system_info); | ||
| 3303 | return @intCast(usize, system_info.dwNumberOfProcessors); | ||
| 3304 | }, | ||
| 3305 | else => @compileError("unsupported OS"), | ||
| 3306 | } | ||
| 3307 | } | ||
| 3308 | |||
| 3309 | pub const BsdKQueueError = error{ | ||
| 3310 | /// The per-process limit on the number of open file descriptors has been reached. | ||
| 3311 | ProcessFdQuotaExceeded, | ||
| 3312 | |||
| 3313 | /// The system-wide limit on the total number of open files has been reached. | ||
| 3314 | SystemFdQuotaExceeded, | ||
| 3315 | |||
| 3316 | /// See https://github.com/ziglang/zig/issues/1396 | ||
| 3317 | Unexpected, | ||
| 3318 | }; | ||
| 3319 | |||
| 3320 | pub fn bsdKQueue() BsdKQueueError!i32 { | ||
| 3321 | const rc = posix.kqueue(); | ||
| 3322 | const err = posix.getErrno(rc); | ||
| 3323 | switch (err) { | ||
| 3324 | 0 => return @intCast(i32, rc), | ||
| 3325 | posix.EMFILE => return BsdKQueueError.ProcessFdQuotaExceeded, | ||
| 3326 | posix.ENFILE => return BsdKQueueError.SystemFdQuotaExceeded, | ||
| 3327 | else => return unexpectedErrorPosix(err), | ||
| 3328 | } | ||
| 3329 | } | ||
| 3330 | |||
| 3331 | pub const BsdKEventError = error{ | ||
| 3332 | /// The process does not have permission to register a filter. | ||
| 3333 | AccessDenied, | ||
| 3334 | |||
| 3335 | /// The event could not be found to be modified or deleted. | ||
| 3336 | EventNotFound, | ||
| 3337 | |||
| 3338 | /// No memory was available to register the event. | ||
| 3339 | SystemResources, | ||
| 3340 | |||
| 3341 | /// The specified process to attach to does not exist. | ||
| 3342 | ProcessNotFound, | ||
| 3343 | }; | ||
| 3344 | |||
| 3345 | pub fn bsdKEvent( | ||
| 3346 | kq: i32, | ||
| 3347 | changelist: []const posix.Kevent, | ||
| 3348 | eventlist: []posix.Kevent, | ||
| 3349 | timeout: ?*const posix.timespec, | ||
| 3350 | ) BsdKEventError!usize { | ||
| 3351 | while (true) { | ||
| 3352 | const rc = posix.kevent(kq, changelist, eventlist, timeout); | ||
| 3353 | const err = posix.getErrno(rc); | ||
| 3354 | switch (err) { | ||
| 3355 | 0 => return rc, | ||
| 3356 | posix.EACCES => return BsdKEventError.AccessDenied, | ||
| 3357 | posix.EFAULT => unreachable, | ||
| 3358 | posix.EBADF => unreachable, | ||
| 3359 | posix.EINTR => continue, | ||
| 3360 | posix.EINVAL => unreachable, | ||
| 3361 | posix.ENOENT => return BsdKEventError.EventNotFound, | ||
| 3362 | posix.ENOMEM => return BsdKEventError.SystemResources, | ||
| 3363 | posix.ESRCH => return BsdKEventError.ProcessNotFound, | ||
| 3364 | else => unreachable, | ||
| 3365 | } | ||
| 3366 | } | ||
| 3367 | } | ||
| 3368 | |||
| 3369 | pub fn linuxINotifyInit1(flags: u32) !i32 { | ||
| 3370 | const rc = linux.inotify_init1(flags); | ||
| 3371 | const err = posix.getErrno(rc); | ||
| 3372 | switch (err) { | ||
| 3373 | 0 => return @intCast(i32, rc), | ||
| 3374 | posix.EINVAL => unreachable, | ||
| 3375 | posix.EMFILE => return error.ProcessFdQuotaExceeded, | ||
| 3376 | posix.ENFILE => return error.SystemFdQuotaExceeded, | ||
| 3377 | posix.ENOMEM => return error.SystemResources, | ||
| 3378 | else => return unexpectedErrorPosix(err), | ||
| 3379 | } | ||
| 3380 | } | ||
| 3381 | |||
| 3382 | pub fn linuxINotifyAddWatchC(inotify_fd: i32, pathname: [*]const u8, mask: u32) !i32 { | ||
| 3383 | const rc = linux.inotify_add_watch(inotify_fd, pathname, mask); | ||
| 3384 | const err = posix.getErrno(rc); | ||
| 3385 | switch (err) { | ||
| 3386 | 0 => return @intCast(i32, rc), | ||
| 3387 | posix.EACCES => return error.AccessDenied, | ||
| 3388 | posix.EBADF => unreachable, | ||
| 3389 | posix.EFAULT => unreachable, | ||
| 3390 | posix.EINVAL => unreachable, | ||
| 3391 | posix.ENAMETOOLONG => return error.NameTooLong, | ||
| 3392 | posix.ENOENT => return error.FileNotFound, | ||
| 3393 | posix.ENOMEM => return error.SystemResources, | ||
| 3394 | posix.ENOSPC => return error.UserResourceLimitReached, | ||
| 3395 | else => return unexpectedErrorPosix(err), | ||
| 3396 | } | ||
| 3397 | } | ||
| 3398 | |||
| 3399 | pub fn linuxINotifyRmWatch(inotify_fd: i32, wd: i32) !void { | ||
| 3400 | const rc = linux.inotify_rm_watch(inotify_fd, wd); | ||
| 3401 | const err = posix.getErrno(rc); | ||
| 3402 | switch (err) { | ||
| 3403 | 0 => return rc, | ||
| 3404 | posix.EBADF => unreachable, | ||
| 3405 | posix.EINVAL => unreachable, | ||
| 3406 | else => unreachable, | ||
| 3407 | } | ||
| 3408 | } | ||
| 3409 | |||
| 3410 | pub const MProtectError = error{ | ||
| 3411 | AccessDenied, | ||
| 3412 | OutOfMemory, | ||
| 3413 | Unexpected, | ||
| 3414 | }; | ||
| 3415 | |||
| 3416 | /// address and length must be page-aligned | ||
| 3417 | pub fn posixMProtect(address: usize, length: usize, protection: u32) MProtectError!void { | ||
| 3418 | const negative_page_size = @bitCast(usize, -isize(page_size)); | ||
| 3419 | const aligned_address = address & negative_page_size; | ||
| 3420 | const aligned_end = (address + length + page_size - 1) & negative_page_size; | ||
| 3421 | assert(address == aligned_address); | ||
| 3422 | assert(length == aligned_end - aligned_address); | ||
| 3423 | const rc = posix.mprotect(address, length, protection); | ||
| 3424 | const err = posix.getErrno(rc); | ||
| 3425 | switch (err) { | ||
| 3426 | 0 => return, | ||
| 3427 | posix.EINVAL => unreachable, | ||
| 3428 | posix.EACCES => return error.AccessDenied, | ||
| 3429 | posix.ENOMEM => return error.OutOfMemory, | ||
| 3430 | else => return unexpectedErrorPosix(err), | ||
| 3431 | } | ||
| 3432 | } | ||
std/os/linux.zig created+1541| ... | @@ -0,0 +1,1541 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const builtin = @import("builtin"); | ||
| 4 | const maxInt = std.math.maxInt; | ||
| 5 | const vdso = @import("linux/vdso.zig"); | ||
| 6 | pub use switch (builtin.arch) { | ||
| 7 | builtin.Arch.x86_64 => @import("linux/x86_64.zig"), | ||
| 8 | builtin.Arch.i386 => @import("linux/i386.zig"), | ||
| 9 | builtin.Arch.aarch64 => @import("linux/arm64.zig"), | ||
| 10 | else => @compileError("unsupported arch"), | ||
| 11 | }; | ||
| 12 | pub use @import("linux/errno.zig"); | ||
| 13 | |||
| 14 | pub const PATH_MAX = 4096; | ||
| 15 | |||
| 16 | pub const STDIN_FILENO = 0; | ||
| 17 | pub const STDOUT_FILENO = 1; | ||
| 18 | pub const STDERR_FILENO = 2; | ||
| 19 | |||
| 20 | pub const FUTEX_WAIT = 0; | ||
| 21 | pub const FUTEX_WAKE = 1; | ||
| 22 | pub const FUTEX_FD = 2; | ||
| 23 | pub const FUTEX_REQUEUE = 3; | ||
| 24 | pub const FUTEX_CMP_REQUEUE = 4; | ||
| 25 | pub const FUTEX_WAKE_OP = 5; | ||
| 26 | pub const FUTEX_LOCK_PI = 6; | ||
| 27 | pub const FUTEX_UNLOCK_PI = 7; | ||
| 28 | pub const FUTEX_TRYLOCK_PI = 8; | ||
| 29 | pub const FUTEX_WAIT_BITSET = 9; | ||
| 30 | |||
| 31 | pub const FUTEX_PRIVATE_FLAG = 128; | ||
| 32 | |||
| 33 | pub const FUTEX_CLOCK_REALTIME = 256; | ||
| 34 | |||
| 35 | pub const PROT_NONE = 0; | ||
| 36 | pub const PROT_READ = 1; | ||
| 37 | pub const PROT_WRITE = 2; | ||
| 38 | pub const PROT_EXEC = 4; | ||
| 39 | pub const PROT_GROWSDOWN = 0x01000000; | ||
| 40 | pub const PROT_GROWSUP = 0x02000000; | ||
| 41 | |||
| 42 | pub const MAP_FAILED = maxInt(usize); | ||
| 43 | pub const MAP_SHARED = 0x01; | ||
| 44 | pub const MAP_PRIVATE = 0x02; | ||
| 45 | pub const MAP_TYPE = 0x0f; | ||
| 46 | pub const MAP_FIXED = 0x10; | ||
| 47 | pub const MAP_ANONYMOUS = 0x20; | ||
| 48 | pub const MAP_NORESERVE = 0x4000; | ||
| 49 | pub const MAP_GROWSDOWN = 0x0100; | ||
| 50 | pub const MAP_DENYWRITE = 0x0800; | ||
| 51 | pub const MAP_EXECUTABLE = 0x1000; | ||
| 52 | pub const MAP_LOCKED = 0x2000; | ||
| 53 | pub const MAP_POPULATE = 0x8000; | ||
| 54 | pub const MAP_NONBLOCK = 0x10000; | ||
| 55 | pub const MAP_STACK = 0x20000; | ||
| 56 | pub const MAP_HUGETLB = 0x40000; | ||
| 57 | pub const MAP_FILE = 0; | ||
| 58 | |||
| 59 | pub const F_OK = 0; | ||
| 60 | pub const X_OK = 1; | ||
| 61 | pub const W_OK = 2; | ||
| 62 | pub const R_OK = 4; | ||
| 63 | |||
| 64 | pub const WNOHANG = 1; | ||
| 65 | pub const WUNTRACED = 2; | ||
| 66 | pub const WSTOPPED = 2; | ||
| 67 | pub const WEXITED = 4; | ||
| 68 | pub const WCONTINUED = 8; | ||
| 69 | pub const WNOWAIT = 0x1000000; | ||
| 70 | |||
| 71 | pub const SA_NOCLDSTOP = 1; | ||
| 72 | pub const SA_NOCLDWAIT = 2; | ||
| 73 | pub const SA_SIGINFO = 4; | ||
| 74 | pub const SA_ONSTACK = 0x08000000; | ||
| 75 | pub const SA_RESTART = 0x10000000; | ||
| 76 | pub const SA_NODEFER = 0x40000000; | ||
| 77 | pub const SA_RESETHAND = 0x80000000; | ||
| 78 | pub const SA_RESTORER = 0x04000000; | ||
| 79 | |||
| 80 | pub const SIGHUP = 1; | ||
| 81 | pub const SIGINT = 2; | ||
| 82 | pub const SIGQUIT = 3; | ||
| 83 | pub const SIGILL = 4; | ||
| 84 | pub const SIGTRAP = 5; | ||
| 85 | pub const SIGABRT = 6; | ||
| 86 | pub const SIGIOT = SIGABRT; | ||
| 87 | pub const SIGBUS = 7; | ||
| 88 | pub const SIGFPE = 8; | ||
| 89 | pub const SIGKILL = 9; | ||
| 90 | pub const SIGUSR1 = 10; | ||
| 91 | pub const SIGSEGV = 11; | ||
| 92 | pub const SIGUSR2 = 12; | ||
| 93 | pub const SIGPIPE = 13; | ||
| 94 | pub const SIGALRM = 14; | ||
| 95 | pub const SIGTERM = 15; | ||
| 96 | pub const SIGSTKFLT = 16; | ||
| 97 | pub const SIGCHLD = 17; | ||
| 98 | pub const SIGCONT = 18; | ||
| 99 | pub const SIGSTOP = 19; | ||
| 100 | pub const SIGTSTP = 20; | ||
| 101 | pub const SIGTTIN = 21; | ||
| 102 | pub const SIGTTOU = 22; | ||
| 103 | pub const SIGURG = 23; | ||
| 104 | pub const SIGXCPU = 24; | ||
| 105 | pub const SIGXFSZ = 25; | ||
| 106 | pub const SIGVTALRM = 26; | ||
| 107 | pub const SIGPROF = 27; | ||
| 108 | pub const SIGWINCH = 28; | ||
| 109 | pub const SIGIO = 29; | ||
| 110 | pub const SIGPOLL = 29; | ||
| 111 | pub const SIGPWR = 30; | ||
| 112 | pub const SIGSYS = 31; | ||
| 113 | pub const SIGUNUSED = SIGSYS; | ||
| 114 | |||
| 115 | pub const O_RDONLY = 0o0; | ||
| 116 | pub const O_WRONLY = 0o1; | ||
| 117 | pub const O_RDWR = 0o2; | ||
| 118 | |||
| 119 | pub const SEEK_SET = 0; | ||
| 120 | pub const SEEK_CUR = 1; | ||
| 121 | pub const SEEK_END = 2; | ||
| 122 | |||
| 123 | pub const SIG_BLOCK = 0; | ||
| 124 | pub const SIG_UNBLOCK = 1; | ||
| 125 | pub const SIG_SETMASK = 2; | ||
| 126 | |||
| 127 | pub const PROTO_ip = 0o000; | ||
| 128 | pub const PROTO_icmp = 0o001; | ||
| 129 | pub const PROTO_igmp = 0o002; | ||
| 130 | pub const PROTO_ggp = 0o003; | ||
| 131 | pub const PROTO_ipencap = 0o004; | ||
| 132 | pub const PROTO_st = 0o005; | ||
| 133 | pub const PROTO_tcp = 0o006; | ||
| 134 | pub const PROTO_egp = 0o010; | ||
| 135 | pub const PROTO_pup = 0o014; | ||
| 136 | pub const PROTO_udp = 0o021; | ||
| 137 | pub const PROTO_hmp = 0o024; | ||
| 138 | pub const PROTO_xns_idp = 0o026; | ||
| 139 | pub const PROTO_rdp = 0o033; | ||
| 140 | pub const PROTO_iso_tp4 = 0o035; | ||
| 141 | pub const PROTO_xtp = 0o044; | ||
| 142 | pub const PROTO_ddp = 0o045; | ||
| 143 | pub const PROTO_idpr_cmtp = 0o046; | ||
| 144 | pub const PROTO_ipv6 = 0o051; | ||
| 145 | pub const PROTO_ipv6_route = 0o053; | ||
| 146 | pub const PROTO_ipv6_frag = 0o054; | ||
| 147 | pub const PROTO_idrp = 0o055; | ||
| 148 | pub const PROTO_rsvp = 0o056; | ||
| 149 | pub const PROTO_gre = 0o057; | ||
| 150 | pub const PROTO_esp = 0o062; | ||
| 151 | pub const PROTO_ah = 0o063; | ||
| 152 | pub const PROTO_skip = 0o071; | ||
| 153 | pub const PROTO_ipv6_icmp = 0o072; | ||
| 154 | pub const PROTO_ipv6_nonxt = 0o073; | ||
| 155 | pub const PROTO_ipv6_opts = 0o074; | ||
| 156 | pub const PROTO_rspf = 0o111; | ||
| 157 | pub const PROTO_vmtp = 0o121; | ||
| 158 | pub const PROTO_ospf = 0o131; | ||
| 159 | pub const PROTO_ipip = 0o136; | ||
| 160 | pub const PROTO_encap = 0o142; | ||
| 161 | pub const PROTO_pim = 0o147; | ||
| 162 | pub const PROTO_raw = 0o377; | ||
| 163 | |||
| 164 | pub const SHUT_RD = 0; | ||
| 165 | pub const SHUT_WR = 1; | ||
| 166 | pub const SHUT_RDWR = 2; | ||
| 167 | |||
| 168 | pub const SOCK_STREAM = 1; | ||
| 169 | pub const SOCK_DGRAM = 2; | ||
| 170 | pub const SOCK_RAW = 3; | ||
| 171 | pub const SOCK_RDM = 4; | ||
| 172 | pub const SOCK_SEQPACKET = 5; | ||
| 173 | pub const SOCK_DCCP = 6; | ||
| 174 | pub const SOCK_PACKET = 10; | ||
| 175 | pub const SOCK_CLOEXEC = 0o2000000; | ||
| 176 | pub const SOCK_NONBLOCK = 0o4000; | ||
| 177 | |||
| 178 | pub const PF_UNSPEC = 0; | ||
| 179 | pub const PF_LOCAL = 1; | ||
| 180 | pub const PF_UNIX = PF_LOCAL; | ||
| 181 | pub const PF_FILE = PF_LOCAL; | ||
| 182 | pub const PF_INET = 2; | ||
| 183 | pub const PF_AX25 = 3; | ||
| 184 | pub const PF_IPX = 4; | ||
| 185 | pub const PF_APPLETALK = 5; | ||
| 186 | pub const PF_NETROM = 6; | ||
| 187 | pub const PF_BRIDGE = 7; | ||
| 188 | pub const PF_ATMPVC = 8; | ||
| 189 | pub const PF_X25 = 9; | ||
| 190 | pub const PF_INET6 = 10; | ||
| 191 | pub const PF_ROSE = 11; | ||
| 192 | pub const PF_DECnet = 12; | ||
| 193 | pub const PF_NETBEUI = 13; | ||
| 194 | pub const PF_SECURITY = 14; | ||
| 195 | pub const PF_KEY = 15; | ||
| 196 | pub const PF_NETLINK = 16; | ||
| 197 | pub const PF_ROUTE = PF_NETLINK; | ||
| 198 | pub const PF_PACKET = 17; | ||
| 199 | pub const PF_ASH = 18; | ||
| 200 | pub const PF_ECONET = 19; | ||
| 201 | pub const PF_ATMSVC = 20; | ||
| 202 | pub const PF_RDS = 21; | ||
| 203 | pub const PF_SNA = 22; | ||
| 204 | pub const PF_IRDA = 23; | ||
| 205 | pub const PF_PPPOX = 24; | ||
| 206 | pub const PF_WANPIPE = 25; | ||
| 207 | pub const PF_LLC = 26; | ||
| 208 | pub const PF_IB = 27; | ||
| 209 | pub const PF_MPLS = 28; | ||
| 210 | pub const PF_CAN = 29; | ||
| 211 | pub const PF_TIPC = 30; | ||
| 212 | pub const PF_BLUETOOTH = 31; | ||
| 213 | pub const PF_IUCV = 32; | ||
| 214 | pub const PF_RXRPC = 33; | ||
| 215 | pub const PF_ISDN = 34; | ||
| 216 | pub const PF_PHONET = 35; | ||
| 217 | pub const PF_IEEE802154 = 36; | ||
| 218 | pub const PF_CAIF = 37; | ||
| 219 | pub const PF_ALG = 38; | ||
| 220 | pub const PF_NFC = 39; | ||
| 221 | pub const PF_VSOCK = 40; | ||
| 222 | pub const PF_KCM = 41; | ||
| 223 | pub const PF_QIPCRTR = 42; | ||
| 224 | pub const PF_SMC = 43; | ||
| 225 | pub const PF_MAX = 44; | ||
| 226 | |||
| 227 | pub const AF_UNSPEC = PF_UNSPEC; | ||
| 228 | pub const AF_LOCAL = PF_LOCAL; | ||
| 229 | pub const AF_UNIX = AF_LOCAL; | ||
| 230 | pub const AF_FILE = AF_LOCAL; | ||
| 231 | pub const AF_INET = PF_INET; | ||
| 232 | pub const AF_AX25 = PF_AX25; | ||
| 233 | pub const AF_IPX = PF_IPX; | ||
| 234 | pub const AF_APPLETALK = PF_APPLETALK; | ||
| 235 | pub const AF_NETROM = PF_NETROM; | ||
| 236 | pub const AF_BRIDGE = PF_BRIDGE; | ||
| 237 | pub const AF_ATMPVC = PF_ATMPVC; | ||
| 238 | pub const AF_X25 = PF_X25; | ||
| 239 | pub const AF_INET6 = PF_INET6; | ||
| 240 | pub const AF_ROSE = PF_ROSE; | ||
| 241 | pub const AF_DECnet = PF_DECnet; | ||
| 242 | pub const AF_NETBEUI = PF_NETBEUI; | ||
| 243 | pub const AF_SECURITY = PF_SECURITY; | ||
| 244 | pub const AF_KEY = PF_KEY; | ||
| 245 | pub const AF_NETLINK = PF_NETLINK; | ||
| 246 | pub const AF_ROUTE = PF_ROUTE; | ||
| 247 | pub const AF_PACKET = PF_PACKET; | ||
| 248 | pub const AF_ASH = PF_ASH; | ||
| 249 | pub const AF_ECONET = PF_ECONET; | ||
| 250 | pub const AF_ATMSVC = PF_ATMSVC; | ||
| 251 | pub const AF_RDS = PF_RDS; | ||
| 252 | pub const AF_SNA = PF_SNA; | ||
| 253 | pub const AF_IRDA = PF_IRDA; | ||
| 254 | pub const AF_PPPOX = PF_PPPOX; | ||
| 255 | pub const AF_WANPIPE = PF_WANPIPE; | ||
| 256 | pub const AF_LLC = PF_LLC; | ||
| 257 | pub const AF_IB = PF_IB; | ||
| 258 | pub const AF_MPLS = PF_MPLS; | ||
| 259 | pub const AF_CAN = PF_CAN; | ||
| 260 | pub const AF_TIPC = PF_TIPC; | ||
| 261 | pub const AF_BLUETOOTH = PF_BLUETOOTH; | ||
| 262 | pub const AF_IUCV = PF_IUCV; | ||
| 263 | pub const AF_RXRPC = PF_RXRPC; | ||
| 264 | pub const AF_ISDN = PF_ISDN; | ||
| 265 | pub const AF_PHONET = PF_PHONET; | ||
| 266 | pub const AF_IEEE802154 = PF_IEEE802154; | ||
| 267 | pub const AF_CAIF = PF_CAIF; | ||
| 268 | pub const AF_ALG = PF_ALG; | ||
| 269 | pub const AF_NFC = PF_NFC; | ||
| 270 | pub const AF_VSOCK = PF_VSOCK; | ||
| 271 | pub const AF_KCM = PF_KCM; | ||
| 272 | pub const AF_QIPCRTR = PF_QIPCRTR; | ||
| 273 | pub const AF_SMC = PF_SMC; | ||
| 274 | pub const AF_MAX = PF_MAX; | ||
| 275 | |||
| 276 | pub const SO_DEBUG = 1; | ||
| 277 | pub const SO_REUSEADDR = 2; | ||
| 278 | pub const SO_TYPE = 3; | ||
| 279 | pub const SO_ERROR = 4; | ||
| 280 | pub const SO_DONTROUTE = 5; | ||
| 281 | pub const SO_BROADCAST = 6; | ||
| 282 | pub const SO_SNDBUF = 7; | ||
| 283 | pub const SO_RCVBUF = 8; | ||
| 284 | pub const SO_KEEPALIVE = 9; | ||
| 285 | pub const SO_OOBINLINE = 10; | ||
| 286 | pub const SO_NO_CHECK = 11; | ||
| 287 | pub const SO_PRIORITY = 12; | ||
| 288 | pub const SO_LINGER = 13; | ||
| 289 | pub const SO_BSDCOMPAT = 14; | ||
| 290 | pub const SO_REUSEPORT = 15; | ||
| 291 | pub const SO_PASSCRED = 16; | ||
| 292 | pub const SO_PEERCRED = 17; | ||
| 293 | pub const SO_RCVLOWAT = 18; | ||
| 294 | pub const SO_SNDLOWAT = 19; | ||
| 295 | pub const SO_RCVTIMEO = 20; | ||
| 296 | pub const SO_SNDTIMEO = 21; | ||
| 297 | pub const SO_ACCEPTCONN = 30; | ||
| 298 | pub const SO_SNDBUFFORCE = 32; | ||
| 299 | pub const SO_RCVBUFFORCE = 33; | ||
| 300 | pub const SO_PROTOCOL = 38; | ||
| 301 | pub const SO_DOMAIN = 39; | ||
| 302 | |||
| 303 | pub const SO_SECURITY_AUTHENTICATION = 22; | ||
| 304 | pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; | ||
| 305 | pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; | ||
| 306 | |||
| 307 | pub const SO_BINDTODEVICE = 25; | ||
| 308 | |||
| 309 | pub const SO_ATTACH_FILTER = 26; | ||
| 310 | pub const SO_DETACH_FILTER = 27; | ||
| 311 | pub const SO_GET_FILTER = SO_ATTACH_FILTER; | ||
| 312 | |||
| 313 | pub const SO_PEERNAME = 28; | ||
| 314 | pub const SO_TIMESTAMP = 29; | ||
| 315 | pub const SCM_TIMESTAMP = SO_TIMESTAMP; | ||
| 316 | |||
| 317 | pub const SO_PEERSEC = 31; | ||
| 318 | pub const SO_PASSSEC = 34; | ||
| 319 | pub const SO_TIMESTAMPNS = 35; | ||
| 320 | pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; | ||
| 321 | pub const SO_MARK = 36; | ||
| 322 | pub const SO_TIMESTAMPING = 37; | ||
| 323 | pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; | ||
| 324 | pub const SO_RXQ_OVFL = 40; | ||
| 325 | pub const SO_WIFI_STATUS = 41; | ||
| 326 | pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; | ||
| 327 | pub const SO_PEEK_OFF = 42; | ||
| 328 | pub const SO_NOFCS = 43; | ||
| 329 | pub const SO_LOCK_FILTER = 44; | ||
| 330 | pub const SO_SELECT_ERR_QUEUE = 45; | ||
| 331 | pub const SO_BUSY_POLL = 46; | ||
| 332 | pub const SO_MAX_PACING_RATE = 47; | ||
| 333 | pub const SO_BPF_EXTENSIONS = 48; | ||
| 334 | pub const SO_INCOMING_CPU = 49; | ||
| 335 | pub const SO_ATTACH_BPF = 50; | ||
| 336 | pub const SO_DETACH_BPF = SO_DETACH_FILTER; | ||
| 337 | pub const SO_ATTACH_REUSEPORT_CBPF = 51; | ||
| 338 | pub const SO_ATTACH_REUSEPORT_EBPF = 52; | ||
| 339 | pub const SO_CNX_ADVICE = 53; | ||
| 340 | pub const SCM_TIMESTAMPING_OPT_STATS = 54; | ||
| 341 | pub const SO_MEMINFO = 55; | ||
| 342 | pub const SO_INCOMING_NAPI_ID = 56; | ||
| 343 | pub const SO_COOKIE = 57; | ||
| 344 | pub const SCM_TIMESTAMPING_PKTINFO = 58; | ||
| 345 | pub const SO_PEERGROUPS = 59; | ||
| 346 | pub const SO_ZEROCOPY = 60; | ||
| 347 | |||
| 348 | pub const SOL_SOCKET = 1; | ||
| 349 | |||
| 350 | pub const SOL_IP = 0; | ||
| 351 | pub const SOL_IPV6 = 41; | ||
| 352 | pub const SOL_ICMPV6 = 58; | ||
| 353 | |||
| 354 | pub const SOL_RAW = 255; | ||
| 355 | pub const SOL_DECNET = 261; | ||
| 356 | pub const SOL_X25 = 262; | ||
| 357 | pub const SOL_PACKET = 263; | ||
| 358 | pub const SOL_ATM = 264; | ||
| 359 | pub const SOL_AAL = 265; | ||
| 360 | pub const SOL_IRDA = 266; | ||
| 361 | pub const SOL_NETBEUI = 267; | ||
| 362 | pub const SOL_LLC = 268; | ||
| 363 | pub const SOL_DCCP = 269; | ||
| 364 | pub const SOL_NETLINK = 270; | ||
| 365 | pub const SOL_TIPC = 271; | ||
| 366 | pub const SOL_RXRPC = 272; | ||
| 367 | pub const SOL_PPPOL2TP = 273; | ||
| 368 | pub const SOL_BLUETOOTH = 274; | ||
| 369 | pub const SOL_PNPIPE = 275; | ||
| 370 | pub const SOL_RDS = 276; | ||
| 371 | pub const SOL_IUCV = 277; | ||
| 372 | pub const SOL_CAIF = 278; | ||
| 373 | pub const SOL_ALG = 279; | ||
| 374 | pub const SOL_NFC = 280; | ||
| 375 | pub const SOL_KCM = 281; | ||
| 376 | pub const SOL_TLS = 282; | ||
| 377 | |||
| 378 | pub const SOMAXCONN = 128; | ||
| 379 | |||
| 380 | pub const MSG_OOB = 0x0001; | ||
| 381 | pub const MSG_PEEK = 0x0002; | ||
| 382 | pub const MSG_DONTROUTE = 0x0004; | ||
| 383 | pub const MSG_CTRUNC = 0x0008; | ||
| 384 | pub const MSG_PROXY = 0x0010; | ||
| 385 | pub const MSG_TRUNC = 0x0020; | ||
| 386 | pub const MSG_DONTWAIT = 0x0040; | ||
| 387 | pub const MSG_EOR = 0x0080; | ||
| 388 | pub const MSG_WAITALL = 0x0100; | ||
| 389 | pub const MSG_FIN = 0x0200; | ||
| 390 | pub const MSG_SYN = 0x0400; | ||
| 391 | pub const MSG_CONFIRM = 0x0800; | ||
| 392 | pub const MSG_RST = 0x1000; | ||
| 393 | pub const MSG_ERRQUEUE = 0x2000; | ||
| 394 | pub const MSG_NOSIGNAL = 0x4000; | ||
| 395 | pub const MSG_MORE = 0x8000; | ||
| 396 | pub const MSG_WAITFORONE = 0x10000; | ||
| 397 | pub const MSG_BATCH = 0x40000; | ||
| 398 | pub const MSG_ZEROCOPY = 0x4000000; | ||
| 399 | pub const MSG_FASTOPEN = 0x20000000; | ||
| 400 | pub const MSG_CMSG_CLOEXEC = 0x40000000; | ||
| 401 | |||
| 402 | pub const DT_UNKNOWN = 0; | ||
| 403 | pub const DT_FIFO = 1; | ||
| 404 | pub const DT_CHR = 2; | ||
| 405 | pub const DT_DIR = 4; | ||
| 406 | pub const DT_BLK = 6; | ||
| 407 | pub const DT_REG = 8; | ||
| 408 | pub const DT_LNK = 10; | ||
| 409 | pub const DT_SOCK = 12; | ||
| 410 | pub const DT_WHT = 14; | ||
| 411 | |||
| 412 | pub const TCGETS = 0x5401; | ||
| 413 | pub const TCSETS = 0x5402; | ||
| 414 | pub const TCSETSW = 0x5403; | ||
| 415 | pub const TCSETSF = 0x5404; | ||
| 416 | pub const TCGETA = 0x5405; | ||
| 417 | pub const TCSETA = 0x5406; | ||
| 418 | pub const TCSETAW = 0x5407; | ||
| 419 | pub const TCSETAF = 0x5408; | ||
| 420 | pub const TCSBRK = 0x5409; | ||
| 421 | pub const TCXONC = 0x540A; | ||
| 422 | pub const TCFLSH = 0x540B; | ||
| 423 | pub const TIOCEXCL = 0x540C; | ||
| 424 | pub const TIOCNXCL = 0x540D; | ||
| 425 | pub const TIOCSCTTY = 0x540E; | ||
| 426 | pub const TIOCGPGRP = 0x540F; | ||
| 427 | pub const TIOCSPGRP = 0x5410; | ||
| 428 | pub const TIOCOUTQ = 0x5411; | ||
| 429 | pub const TIOCSTI = 0x5412; | ||
| 430 | pub const TIOCGWINSZ = 0x5413; | ||
| 431 | pub const TIOCSWINSZ = 0x5414; | ||
| 432 | pub const TIOCMGET = 0x5415; | ||
| 433 | pub const TIOCMBIS = 0x5416; | ||
| 434 | pub const TIOCMBIC = 0x5417; | ||
| 435 | pub const TIOCMSET = 0x5418; | ||
| 436 | pub const TIOCGSOFTCAR = 0x5419; | ||
| 437 | pub const TIOCSSOFTCAR = 0x541A; | ||
| 438 | pub const FIONREAD = 0x541B; | ||
| 439 | pub const TIOCINQ = FIONREAD; | ||
| 440 | pub const TIOCLINUX = 0x541C; | ||
| 441 | pub const TIOCCONS = 0x541D; | ||
| 442 | pub const TIOCGSERIAL = 0x541E; | ||
| 443 | pub const TIOCSSERIAL = 0x541F; | ||
| 444 | pub const TIOCPKT = 0x5420; | ||
| 445 | pub const FIONBIO = 0x5421; | ||
| 446 | pub const TIOCNOTTY = 0x5422; | ||
| 447 | pub const TIOCSETD = 0x5423; | ||
| 448 | pub const TIOCGETD = 0x5424; | ||
| 449 | pub const TCSBRKP = 0x5425; | ||
| 450 | pub const TIOCSBRK = 0x5427; | ||
| 451 | pub const TIOCCBRK = 0x5428; | ||
| 452 | pub const TIOCGSID = 0x5429; | ||
| 453 | pub const TIOCGRS485 = 0x542E; | ||
| 454 | pub const TIOCSRS485 = 0x542F; | ||
| 455 | pub const TIOCGPTN = 0x80045430; | ||
| 456 | pub const TIOCSPTLCK = 0x40045431; | ||
| 457 | pub const TIOCGDEV = 0x80045432; | ||
| 458 | pub const TCGETX = 0x5432; | ||
| 459 | pub const TCSETX = 0x5433; | ||
| 460 | pub const TCSETXF = 0x5434; | ||
| 461 | pub const TCSETXW = 0x5435; | ||
| 462 | pub const TIOCSIG = 0x40045436; | ||
| 463 | pub const TIOCVHANGUP = 0x5437; | ||
| 464 | pub const TIOCGPKT = 0x80045438; | ||
| 465 | pub const TIOCGPTLCK = 0x80045439; | ||
| 466 | pub const TIOCGEXCL = 0x80045440; | ||
| 467 | |||
| 468 | pub const EPOLL_CLOEXEC = O_CLOEXEC; | ||
| 469 | |||
| 470 | pub const EPOLL_CTL_ADD = 1; | ||
| 471 | pub const EPOLL_CTL_DEL = 2; | ||
| 472 | pub const EPOLL_CTL_MOD = 3; | ||
| 473 | |||
| 474 | pub const EPOLLIN = 0x001; | ||
| 475 | pub const EPOLLPRI = 0x002; | ||
| 476 | pub const EPOLLOUT = 0x004; | ||
| 477 | pub const EPOLLRDNORM = 0x040; | ||
| 478 | pub const EPOLLRDBAND = 0x080; | ||
| 479 | pub const EPOLLWRNORM = 0x100; | ||
| 480 | pub const EPOLLWRBAND = 0x200; | ||
| 481 | pub const EPOLLMSG = 0x400; | ||
| 482 | pub const EPOLLERR = 0x008; | ||
| 483 | pub const EPOLLHUP = 0x010; | ||
| 484 | pub const EPOLLRDHUP = 0x2000; | ||
| 485 | pub const EPOLLEXCLUSIVE = (u32(1) << 28); | ||
| 486 | pub const EPOLLWAKEUP = (u32(1) << 29); | ||
| 487 | pub const EPOLLONESHOT = (u32(1) << 30); | ||
| 488 | pub const EPOLLET = (u32(1) << 31); | ||
| 489 | |||
| 490 | pub const CLOCK_REALTIME = 0; | ||
| 491 | pub const CLOCK_MONOTONIC = 1; | ||
| 492 | pub const CLOCK_PROCESS_CPUTIME_ID = 2; | ||
| 493 | pub const CLOCK_THREAD_CPUTIME_ID = 3; | ||
| 494 | pub const CLOCK_MONOTONIC_RAW = 4; | ||
| 495 | pub const CLOCK_REALTIME_COARSE = 5; | ||
| 496 | pub const CLOCK_MONOTONIC_COARSE = 6; | ||
| 497 | pub const CLOCK_BOOTTIME = 7; | ||
| 498 | pub const CLOCK_REALTIME_ALARM = 8; | ||
| 499 | pub const CLOCK_BOOTTIME_ALARM = 9; | ||
| 500 | pub const CLOCK_SGI_CYCLE = 10; | ||
| 501 | pub const CLOCK_TAI = 11; | ||
| 502 | |||
| 503 | pub const CSIGNAL = 0x000000ff; | ||
| 504 | pub const CLONE_VM = 0x00000100; | ||
| 505 | pub const CLONE_FS = 0x00000200; | ||
| 506 | pub const CLONE_FILES = 0x00000400; | ||
| 507 | pub const CLONE_SIGHAND = 0x00000800; | ||
| 508 | pub const CLONE_PTRACE = 0x00002000; | ||
| 509 | pub const CLONE_VFORK = 0x00004000; | ||
| 510 | pub const CLONE_PARENT = 0x00008000; | ||
| 511 | pub const CLONE_THREAD = 0x00010000; | ||
| 512 | pub const CLONE_NEWNS = 0x00020000; | ||
| 513 | pub const CLONE_SYSVSEM = 0x00040000; | ||
| 514 | pub const CLONE_SETTLS = 0x00080000; | ||
| 515 | pub const CLONE_PARENT_SETTID = 0x00100000; | ||
| 516 | pub const CLONE_CHILD_CLEARTID = 0x00200000; | ||
| 517 | pub const CLONE_DETACHED = 0x00400000; | ||
| 518 | pub const CLONE_UNTRACED = 0x00800000; | ||
| 519 | pub const CLONE_CHILD_SETTID = 0x01000000; | ||
| 520 | pub const CLONE_NEWCGROUP = 0x02000000; | ||
| 521 | pub const CLONE_NEWUTS = 0x04000000; | ||
| 522 | pub const CLONE_NEWIPC = 0x08000000; | ||
| 523 | pub const CLONE_NEWUSER = 0x10000000; | ||
| 524 | pub const CLONE_NEWPID = 0x20000000; | ||
| 525 | pub const CLONE_NEWNET = 0x40000000; | ||
| 526 | pub const CLONE_IO = 0x80000000; | ||
| 527 | |||
| 528 | pub const EFD_SEMAPHORE = 1; | ||
| 529 | pub const EFD_CLOEXEC = O_CLOEXEC; | ||
| 530 | pub const EFD_NONBLOCK = O_NONBLOCK; | ||
| 531 | |||
| 532 | pub const MS_RDONLY = 1; | ||
| 533 | pub const MS_NOSUID = 2; | ||
| 534 | pub const MS_NODEV = 4; | ||
| 535 | pub const MS_NOEXEC = 8; | ||
| 536 | pub const MS_SYNCHRONOUS = 16; | ||
| 537 | pub const MS_REMOUNT = 32; | ||
| 538 | pub const MS_MANDLOCK = 64; | ||
| 539 | pub const MS_DIRSYNC = 128; | ||
| 540 | pub const MS_NOATIME = 1024; | ||
| 541 | pub const MS_NODIRATIME = 2048; | ||
| 542 | pub const MS_BIND = 4096; | ||
| 543 | pub const MS_MOVE = 8192; | ||
| 544 | pub const MS_REC = 16384; | ||
| 545 | pub const MS_SILENT = 32768; | ||
| 546 | pub const MS_POSIXACL = (1 << 16); | ||
| 547 | pub const MS_UNBINDABLE = (1 << 17); | ||
| 548 | pub const MS_PRIVATE = (1 << 18); | ||
| 549 | pub const MS_SLAVE = (1 << 19); | ||
| 550 | pub const MS_SHARED = (1 << 20); | ||
| 551 | pub const MS_RELATIME = (1 << 21); | ||
| 552 | pub const MS_KERNMOUNT = (1 << 22); | ||
| 553 | pub const MS_I_VERSION = (1 << 23); | ||
| 554 | pub const MS_STRICTATIME = (1 << 24); | ||
| 555 | pub const MS_LAZYTIME = (1 << 25); | ||
| 556 | pub const MS_NOREMOTELOCK = (1 << 27); | ||
| 557 | pub const MS_NOSEC = (1 << 28); | ||
| 558 | pub const MS_BORN = (1 << 29); | ||
| 559 | pub const MS_ACTIVE = (1 << 30); | ||
| 560 | pub const MS_NOUSER = (1 << 31); | ||
| 561 | |||
| 562 | pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); | ||
| 563 | |||
| 564 | pub const MS_MGC_VAL = 0xc0ed0000; | ||
| 565 | pub const MS_MGC_MSK = 0xffff0000; | ||
| 566 | |||
| 567 | pub const MNT_FORCE = 1; | ||
| 568 | pub const MNT_DETACH = 2; | ||
| 569 | pub const MNT_EXPIRE = 4; | ||
| 570 | pub const UMOUNT_NOFOLLOW = 8; | ||
| 571 | |||
| 572 | pub const IN_CLOEXEC = O_CLOEXEC; | ||
| 573 | pub const IN_NONBLOCK = O_NONBLOCK; | ||
| 574 | |||
| 575 | pub const IN_ACCESS = 0x00000001; | ||
| 576 | pub const IN_MODIFY = 0x00000002; | ||
| 577 | pub const IN_ATTRIB = 0x00000004; | ||
| 578 | pub const IN_CLOSE_WRITE = 0x00000008; | ||
| 579 | pub const IN_CLOSE_NOWRITE = 0x00000010; | ||
| 580 | pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; | ||
| 581 | pub const IN_OPEN = 0x00000020; | ||
| 582 | pub const IN_MOVED_FROM = 0x00000040; | ||
| 583 | pub const IN_MOVED_TO = 0x00000080; | ||
| 584 | pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; | ||
| 585 | pub const IN_CREATE = 0x00000100; | ||
| 586 | pub const IN_DELETE = 0x00000200; | ||
| 587 | pub const IN_DELETE_SELF = 0x00000400; | ||
| 588 | pub const IN_MOVE_SELF = 0x00000800; | ||
| 589 | pub const IN_ALL_EVENTS = 0x00000fff; | ||
| 590 | |||
| 591 | pub const IN_UNMOUNT = 0x00002000; | ||
| 592 | pub const IN_Q_OVERFLOW = 0x00004000; | ||
| 593 | pub const IN_IGNORED = 0x00008000; | ||
| 594 | |||
| 595 | pub const IN_ONLYDIR = 0x01000000; | ||
| 596 | pub const IN_DONT_FOLLOW = 0x02000000; | ||
| 597 | pub const IN_EXCL_UNLINK = 0x04000000; | ||
| 598 | pub const IN_MASK_ADD = 0x20000000; | ||
| 599 | |||
| 600 | pub const IN_ISDIR = 0x40000000; | ||
| 601 | pub const IN_ONESHOT = 0x80000000; | ||
| 602 | |||
| 603 | pub const S_IFMT = 0o170000; | ||
| 604 | |||
| 605 | pub const S_IFDIR = 0o040000; | ||
| 606 | pub const S_IFCHR = 0o020000; | ||
| 607 | pub const S_IFBLK = 0o060000; | ||
| 608 | pub const S_IFREG = 0o100000; | ||
| 609 | pub const S_IFIFO = 0o010000; | ||
| 610 | pub const S_IFLNK = 0o120000; | ||
| 611 | pub const S_IFSOCK = 0o140000; | ||
| 612 | |||
| 613 | pub const S_ISUID = 0o4000; | ||
| 614 | pub const S_ISGID = 0o2000; | ||
| 615 | pub const S_ISVTX = 0o1000; | ||
| 616 | pub const S_IRUSR = 0o400; | ||
| 617 | pub const S_IWUSR = 0o200; | ||
| 618 | pub const S_IXUSR = 0o100; | ||
| 619 | pub const S_IRWXU = 0o700; | ||
| 620 | pub const S_IRGRP = 0o040; | ||
| 621 | pub const S_IWGRP = 0o020; | ||
| 622 | pub const S_IXGRP = 0o010; | ||
| 623 | pub const S_IRWXG = 0o070; | ||
| 624 | pub const S_IROTH = 0o004; | ||
| 625 | pub const S_IWOTH = 0o002; | ||
| 626 | pub const S_IXOTH = 0o001; | ||
| 627 | pub const S_IRWXO = 0o007; | ||
| 628 | |||
| 629 | pub fn S_ISREG(m: u32) bool { | ||
| 630 | return m & S_IFMT == S_IFREG; | ||
| 631 | } | ||
| 632 | |||
| 633 | pub fn S_ISDIR(m: u32) bool { | ||
| 634 | return m & S_IFMT == S_IFDIR; | ||
| 635 | } | ||
| 636 | |||
| 637 | pub fn S_ISCHR(m: u32) bool { | ||
| 638 | return m & S_IFMT == S_IFCHR; | ||
| 639 | } | ||
| 640 | |||
| 641 | pub fn S_ISBLK(m: u32) bool { | ||
| 642 | return m & S_IFMT == S_IFBLK; | ||
| 643 | } | ||
| 644 | |||
| 645 | pub fn S_ISFIFO(m: u32) bool { | ||
| 646 | return m & S_IFMT == S_IFIFO; | ||
| 647 | } | ||
| 648 | |||
| 649 | pub fn S_ISLNK(m: u32) bool { | ||
| 650 | return m & S_IFMT == S_IFLNK; | ||
| 651 | } | ||
| 652 | |||
| 653 | pub fn S_ISSOCK(m: u32) bool { | ||
| 654 | return m & S_IFMT == S_IFSOCK; | ||
| 655 | } | ||
| 656 | |||
| 657 | pub const TFD_NONBLOCK = O_NONBLOCK; | ||
| 658 | pub const TFD_CLOEXEC = O_CLOEXEC; | ||
| 659 | |||
| 660 | pub const TFD_TIMER_ABSTIME = 1; | ||
| 661 | pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); | ||
| 662 | |||
| 663 | fn unsigned(s: i32) u32 { | ||
| 664 | return @bitCast(u32, s); | ||
| 665 | } | ||
| 666 | fn signed(s: u32) i32 { | ||
| 667 | return @bitCast(i32, s); | ||
| 668 | } | ||
| 669 | pub fn WEXITSTATUS(s: i32) i32 { | ||
| 670 | return signed((unsigned(s) & 0xff00) >> 8); | ||
| 671 | } | ||
| 672 | pub fn WTERMSIG(s: i32) i32 { | ||
| 673 | return signed(unsigned(s) & 0x7f); | ||
| 674 | } | ||
| 675 | pub fn WSTOPSIG(s: i32) i32 { | ||
| 676 | return WEXITSTATUS(s); | ||
| 677 | } | ||
| 678 | pub fn WIFEXITED(s: i32) bool { | ||
| 679 | return WTERMSIG(s) == 0; | ||
| 680 | } | ||
| 681 | pub fn WIFSTOPPED(s: i32) bool { | ||
| 682 | return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; | ||
| 683 | } | ||
| 684 | pub fn WIFSIGNALED(s: i32) bool { | ||
| 685 | return (unsigned(s) & 0xffff) -% 1 < 0xff; | ||
| 686 | } | ||
| 687 | |||
| 688 | pub const winsize = extern struct { | ||
| 689 | ws_row: u16, | ||
| 690 | ws_col: u16, | ||
| 691 | ws_xpixel: u16, | ||
| 692 | ws_ypixel: u16, | ||
| 693 | }; | ||
| 694 | |||
| 695 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 696 | pub fn getErrno(r: usize) usize { | ||
| 697 | const signed_r = @bitCast(isize, r); | ||
| 698 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 699 | } | ||
| 700 | |||
| 701 | pub fn dup2(old: i32, new: i32) usize { | ||
| 702 | return dup3(old, new, 0); | ||
| 703 | } | ||
| 704 | |||
| 705 | pub fn dup3(old: i32, new: i32, flags: u32) usize { | ||
| 706 | return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); | ||
| 707 | } | ||
| 708 | |||
| 709 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 710 | pub fn chdir(path: [*]const u8) usize { | ||
| 711 | return syscall1(SYS_chdir, @ptrToInt(path)); | ||
| 712 | } | ||
| 713 | |||
| 714 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 715 | pub fn chroot(path: [*]const u8) usize { | ||
| 716 | return syscall1(SYS_chroot, @ptrToInt(path)); | ||
| 717 | } | ||
| 718 | |||
| 719 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 720 | pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { | ||
| 721 | return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); | ||
| 722 | } | ||
| 723 | |||
| 724 | pub fn fork() usize { | ||
| 725 | return clone2(SIGCHLD, 0); | ||
| 726 | } | ||
| 727 | |||
| 728 | /// This must be inline, and inline call the syscall function, because if the | ||
| 729 | /// child does a return it will clobber the parent's stack. | ||
| 730 | /// It is advised to avoid this function and use clone instead, because | ||
| 731 | /// the compiler is not aware of how vfork affects control flow and you may | ||
| 732 | /// see different results in optimized builds. | ||
| 733 | pub inline fn vfork() usize { | ||
| 734 | return @inlineCall(syscall0, SYS_vfork); | ||
| 735 | } | ||
| 736 | |||
| 737 | pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { | ||
| 738 | return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout)); | ||
| 739 | } | ||
| 740 | |||
| 741 | pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize { | ||
| 742 | return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val)); | ||
| 743 | } | ||
| 744 | |||
| 745 | pub fn getcwd(buf: [*]u8, size: usize) usize { | ||
| 746 | return syscall2(SYS_getcwd, @ptrToInt(buf), size); | ||
| 747 | } | ||
| 748 | |||
| 749 | pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { | ||
| 750 | return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); | ||
| 751 | } | ||
| 752 | |||
| 753 | pub fn inotify_init1(flags: u32) usize { | ||
| 754 | return syscall1(SYS_inotify_init1, flags); | ||
| 755 | } | ||
| 756 | |||
| 757 | pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { | ||
| 758 | return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); | ||
| 759 | } | ||
| 760 | |||
| 761 | pub fn inotify_rm_watch(fd: i32, wd: i32) usize { | ||
| 762 | return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); | ||
| 763 | } | ||
| 764 | |||
| 765 | pub fn isatty(fd: i32) bool { | ||
| 766 | var wsz: winsize = undefined; | ||
| 767 | return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; | ||
| 768 | } | ||
| 769 | |||
| 770 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 771 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 772 | return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); | ||
| 773 | } | ||
| 774 | |||
| 775 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 776 | pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 777 | return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); | ||
| 778 | } | ||
| 779 | |||
| 780 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 781 | pub fn mkdir(path: [*]const u8, mode: u32) usize { | ||
| 782 | return mkdirat(AT_FDCWD, path, mode); | ||
| 783 | } | ||
| 784 | |||
| 785 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 786 | pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { | ||
| 787 | return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); | ||
| 788 | } | ||
| 789 | |||
| 790 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 791 | pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { | ||
| 792 | return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); | ||
| 793 | } | ||
| 794 | |||
| 795 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 796 | pub fn umount(special: [*]const u8) usize { | ||
| 797 | return syscall2(SYS_umount2, @ptrToInt(special), 0); | ||
| 798 | } | ||
| 799 | |||
| 800 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 801 | pub fn umount2(special: [*]const u8, flags: u32) usize { | ||
| 802 | return syscall2(SYS_umount2, @ptrToInt(special), flags); | ||
| 803 | } | ||
| 804 | |||
| 805 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | ||
| 806 | return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); | ||
| 807 | } | ||
| 808 | |||
| 809 | pub fn mprotect(address: usize, length: usize, protection: usize) usize { | ||
| 810 | return syscall3(SYS_mprotect, address, length, protection); | ||
| 811 | } | ||
| 812 | |||
| 813 | pub fn munmap(address: usize, length: usize) usize { | ||
| 814 | return syscall2(SYS_munmap, address, length); | ||
| 815 | } | ||
| 816 | |||
| 817 | pub fn read(fd: i32, buf: [*]u8, count: usize) usize { | ||
| 818 | return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | ||
| 819 | } | ||
| 820 | |||
| 821 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { | ||
| 822 | return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { | ||
| 826 | return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { | ||
| 830 | return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); | ||
| 831 | } | ||
| 832 | |||
| 833 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { | ||
| 834 | return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); | ||
| 835 | } | ||
| 836 | |||
| 837 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 838 | pub fn rmdir(path: [*]const u8) usize { | ||
| 839 | return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); | ||
| 840 | } | ||
| 841 | |||
| 842 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 843 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | ||
| 844 | return symlinkat(existing, AT_FDCWD, new); | ||
| 845 | } | ||
| 846 | |||
| 847 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 848 | pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { | ||
| 849 | return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); | ||
| 850 | } | ||
| 851 | |||
| 852 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 853 | pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { | ||
| 854 | return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | ||
| 855 | } | ||
| 856 | |||
| 857 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 858 | pub fn access(path: [*]const u8, mode: u32) usize { | ||
| 859 | return faccessat(AT_FDCWD, path, mode); | ||
| 860 | } | ||
| 861 | |||
| 862 | pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { | ||
| 863 | return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); | ||
| 864 | } | ||
| 865 | |||
| 866 | pub fn pipe(fd: *[2]i32) usize { | ||
| 867 | return pipe2(fd, 0); | ||
| 868 | } | ||
| 869 | |||
| 870 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | ||
| 871 | return syscall2(SYS_pipe2, @ptrToInt(fd), flags); | ||
| 872 | } | ||
| 873 | |||
| 874 | pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { | ||
| 875 | return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | ||
| 876 | } | ||
| 877 | |||
| 878 | pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { | ||
| 879 | return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | ||
| 880 | } | ||
| 881 | |||
| 882 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 883 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | ||
| 884 | return renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); | ||
| 885 | } | ||
| 886 | |||
| 887 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 888 | pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { | ||
| 889 | return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); | ||
| 890 | } | ||
| 891 | |||
| 892 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 893 | pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { | ||
| 894 | return openat(AT_FDCWD, path, flags, perm); | ||
| 895 | } | ||
| 896 | |||
| 897 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 898 | pub fn create(path: [*]const u8, perm: usize) usize { | ||
| 899 | return syscall2(SYS_creat, @ptrToInt(path), perm); | ||
| 900 | } | ||
| 901 | |||
| 902 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 903 | pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { | ||
| 904 | // dirfd could be negative, for example AT_FDCWD is -100 | ||
| 905 | return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); | ||
| 906 | } | ||
| 907 | |||
| 908 | /// See also `clone` (from the arch-specific include) | ||
| 909 | pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { | ||
| 910 | return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); | ||
| 911 | } | ||
| 912 | |||
| 913 | /// See also `clone` (from the arch-specific include) | ||
| 914 | pub fn clone2(flags: u32, child_stack_ptr: usize) usize { | ||
| 915 | return syscall2(SYS_clone, flags, child_stack_ptr); | ||
| 916 | } | ||
| 917 | |||
| 918 | pub fn close(fd: i32) usize { | ||
| 919 | return syscall1(SYS_close, @bitCast(usize, isize(fd))); | ||
| 920 | } | ||
| 921 | |||
| 922 | pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { | ||
| 923 | return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); | ||
| 924 | } | ||
| 925 | |||
| 926 | pub fn exit(status: i32) noreturn { | ||
| 927 | _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); | ||
| 928 | unreachable; | ||
| 929 | } | ||
| 930 | |||
| 931 | pub fn exit_group(status: i32) noreturn { | ||
| 932 | _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); | ||
| 933 | unreachable; | ||
| 934 | } | ||
| 935 | |||
| 936 | pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { | ||
| 937 | return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); | ||
| 938 | } | ||
| 939 | |||
| 940 | pub fn kill(pid: i32, sig: i32) usize { | ||
| 941 | return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); | ||
| 942 | } | ||
| 943 | |||
| 944 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 945 | pub fn unlink(path: [*]const u8) usize { | ||
| 946 | return unlinkat(AT_FDCWD, path, 0); | ||
| 947 | } | ||
| 948 | |||
| 949 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 950 | pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { | ||
| 951 | return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); | ||
| 952 | } | ||
| 953 | |||
| 954 | pub fn waitpid(pid: i32, status: *i32, options: i32) usize { | ||
| 955 | return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); | ||
| 956 | } | ||
| 957 | |||
| 958 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | ||
| 959 | if (VDSO_CGT_SYM.len != 0) { | ||
| 960 | const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); | ||
| 961 | if (@ptrToInt(f) != 0) { | ||
| 962 | const rc = f(clk_id, tp); | ||
| 963 | switch (rc) { | ||
| 964 | 0, @bitCast(usize, isize(-EINVAL)) => return rc, | ||
| 965 | else => {}, | ||
| 966 | } | ||
| 967 | } | ||
| 968 | } | ||
| 969 | return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 970 | } | ||
| 971 | var vdso_clock_gettime = init_vdso_clock_gettime; | ||
| 972 | extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { | ||
| 973 | const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); | ||
| 974 | var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); | ||
| 975 | _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); | ||
| 976 | if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); | ||
| 977 | return f(clk, ts); | ||
| 978 | } | ||
| 979 | |||
| 980 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | ||
| 981 | return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 982 | } | ||
| 983 | |||
| 984 | pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { | ||
| 985 | return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 986 | } | ||
| 987 | |||
| 988 | pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { | ||
| 989 | return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); | ||
| 990 | } | ||
| 991 | |||
| 992 | pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { | ||
| 993 | return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); | ||
| 994 | } | ||
| 995 | |||
| 996 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | ||
| 997 | return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); | ||
| 998 | } | ||
| 999 | |||
| 1000 | pub fn setuid(uid: u32) usize { | ||
| 1001 | return syscall1(SYS_setuid, uid); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | pub fn setgid(gid: u32) usize { | ||
| 1005 | return syscall1(SYS_setgid, gid); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | pub fn setreuid(ruid: u32, euid: u32) usize { | ||
| 1009 | return syscall2(SYS_setreuid, ruid, euid); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | pub fn setregid(rgid: u32, egid: u32) usize { | ||
| 1013 | return syscall2(SYS_setregid, rgid, egid); | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | pub fn getuid() u32 { | ||
| 1017 | return u32(syscall0(SYS_getuid)); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | pub fn getgid() u32 { | ||
| 1021 | return u32(syscall0(SYS_getgid)); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | pub fn geteuid() u32 { | ||
| 1025 | return u32(syscall0(SYS_geteuid)); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | pub fn getegid() u32 { | ||
| 1029 | return u32(syscall0(SYS_getegid)); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | pub fn seteuid(euid: u32) usize { | ||
| 1033 | return syscall1(SYS_seteuid, euid); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | pub fn setegid(egid: u32) usize { | ||
| 1037 | return syscall1(SYS_setegid, egid); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { | ||
| 1041 | return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { | ||
| 1045 | return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { | ||
| 1049 | return syscall3(SYS_setresuid, ruid, euid, suid); | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { | ||
| 1053 | return syscall3(SYS_setresgid, rgid, egid, sgid); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | pub fn getgroups(size: usize, list: *u32) usize { | ||
| 1057 | return syscall2(SYS_getgroups, size, @ptrToInt(list)); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | pub fn setgroups(size: usize, list: *const u32) usize { | ||
| 1061 | return syscall2(SYS_setgroups, size, @ptrToInt(list)); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | pub fn getpid() i32 { | ||
| 1065 | return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | pub fn gettid() i32 { | ||
| 1069 | return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { | ||
| 1073 | return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { | ||
| 1077 | assert(sig >= 1); | ||
| 1078 | assert(sig != SIGKILL); | ||
| 1079 | assert(sig != SIGSTOP); | ||
| 1080 | var ksa = k_sigaction{ | ||
| 1081 | .handler = act.handler, | ||
| 1082 | .flags = act.flags | SA_RESTORER, | ||
| 1083 | .mask = undefined, | ||
| 1084 | .restorer = @ptrCast(extern fn () void, restore_rt), | ||
| 1085 | }; | ||
| 1086 | var ksa_old: k_sigaction = undefined; | ||
| 1087 | @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); | ||
| 1088 | const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); | ||
| 1089 | const err = getErrno(result); | ||
| 1090 | if (err != 0) { | ||
| 1091 | return result; | ||
| 1092 | } | ||
| 1093 | if (oact) |old| { | ||
| 1094 | old.handler = ksa_old.handler; | ||
| 1095 | old.flags = @truncate(u32, ksa_old.flags); | ||
| 1096 | @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); | ||
| 1097 | } | ||
| 1098 | return 0; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | const NSIG = 65; | ||
| 1102 | const sigset_t = [128 / @sizeOf(usize)]usize; | ||
| 1103 | const all_mask = []usize{maxInt(usize)}; | ||
| 1104 | const app_mask = []usize{0xfffffffc7fffffff}; | ||
| 1105 | |||
| 1106 | const k_sigaction = extern struct { | ||
| 1107 | handler: extern fn (i32) void, | ||
| 1108 | flags: usize, | ||
| 1109 | restorer: extern fn () void, | ||
| 1110 | mask: [2]u32, | ||
| 1111 | }; | ||
| 1112 | |||
| 1113 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | ||
| 1114 | pub const Sigaction = struct { | ||
| 1115 | handler: extern fn (i32) void, | ||
| 1116 | mask: sigset_t, | ||
| 1117 | flags: u32, | ||
| 1118 | }; | ||
| 1119 | |||
| 1120 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | ||
| 1121 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ||
| 1122 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | ||
| 1123 | pub const empty_sigset = []usize{0} ** sigset_t.len; | ||
| 1124 | |||
| 1125 | pub fn raise(sig: i32) usize { | ||
| 1126 | var set: sigset_t = undefined; | ||
| 1127 | blockAppSignals(&set); | ||
| 1128 | const tid = syscall0(SYS_gettid); | ||
| 1129 | const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); | ||
| 1130 | restoreSignals(&set); | ||
| 1131 | return ret; | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | fn blockAllSignals(set: *sigset_t) void { | ||
| 1135 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | fn blockAppSignals(set: *sigset_t) void { | ||
| 1139 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | fn restoreSignals(set: *sigset_t) void { | ||
| 1143 | _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | pub fn sigaddset(set: *sigset_t, sig: u6) void { | ||
| 1147 | const s = sig - 1; | ||
| 1148 | (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | pub fn sigismember(set: *const sigset_t, sig: u6) bool { | ||
| 1152 | const s = sig - 1; | ||
| 1153 | return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | pub const in_port_t = u16; | ||
| 1157 | pub const sa_family_t = u16; | ||
| 1158 | pub const socklen_t = u32; | ||
| 1159 | |||
| 1160 | /// This intentionally only has ip4 and ip6 | ||
| 1161 | pub const sockaddr = extern union { | ||
| 1162 | in: sockaddr_in, | ||
| 1163 | in6: sockaddr_in6, | ||
| 1164 | }; | ||
| 1165 | |||
| 1166 | pub const sockaddr_in = extern struct { | ||
| 1167 | family: sa_family_t, | ||
| 1168 | port: in_port_t, | ||
| 1169 | addr: u32, | ||
| 1170 | zero: [8]u8, | ||
| 1171 | }; | ||
| 1172 | |||
| 1173 | pub const sockaddr_in6 = extern struct { | ||
| 1174 | family: sa_family_t, | ||
| 1175 | port: in_port_t, | ||
| 1176 | flowinfo: u32, | ||
| 1177 | addr: [16]u8, | ||
| 1178 | scope_id: u32, | ||
| 1179 | }; | ||
| 1180 | |||
| 1181 | pub const sockaddr_un = extern struct { | ||
| 1182 | family: sa_family_t, | ||
| 1183 | path: [108]u8, | ||
| 1184 | }; | ||
| 1185 | |||
| 1186 | pub const iovec = extern struct { | ||
| 1187 | iov_base: [*]u8, | ||
| 1188 | iov_len: usize, | ||
| 1189 | }; | ||
| 1190 | |||
| 1191 | pub const iovec_const = extern struct { | ||
| 1192 | iov_base: [*]const u8, | ||
| 1193 | iov_len: usize, | ||
| 1194 | }; | ||
| 1195 | |||
| 1196 | pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | ||
| 1197 | return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | ||
| 1201 | return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { | ||
| 1205 | return syscall3(SYS_socket, domain, socket_type, protocol); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { | ||
| 1209 | return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { | ||
| 1213 | return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { | ||
| 1217 | return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { | ||
| 1221 | return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { | ||
| 1225 | return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { | ||
| 1229 | return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | pub fn shutdown(fd: i32, how: i32) usize { | ||
| 1233 | return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { | ||
| 1237 | return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | pub fn listen(fd: i32, backlog: u32) usize { | ||
| 1241 | return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { | ||
| 1245 | return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { | ||
| 1249 | return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | ||
| 1253 | return accept4(fd, addr, len, 0); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { | ||
| 1257 | return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | pub fn fstat(fd: i32, stat_buf: *Stat) usize { | ||
| 1261 | return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1265 | pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { | ||
| 1266 | return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1270 | pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { | ||
| 1271 | return fstatat(AF_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1275 | pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { | ||
| 1276 | return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1280 | pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { | ||
| 1281 | return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1285 | pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { | ||
| 1286 | return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { | ||
| 1290 | return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1294 | pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { | ||
| 1295 | return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1299 | pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { | ||
| 1300 | return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1304 | pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { | ||
| 1305 | return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1309 | pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | ||
| 1310 | return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1314 | pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | ||
| 1315 | return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1319 | pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | ||
| 1320 | return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1324 | pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { | ||
| 1325 | return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1329 | pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { | ||
| 1330 | return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1334 | pub fn fremovexattr(fd: usize, name: [*]const u8) usize { | ||
| 1335 | return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | pub fn sched_getaffinity(pid: i32, set: []usize) usize { | ||
| 1339 | return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | pub const epoll_data = packed union { | ||
| 1343 | ptr: usize, | ||
| 1344 | fd: i32, | ||
| 1345 | @"u32": u32, | ||
| 1346 | @"u64": u64, | ||
| 1347 | }; | ||
| 1348 | |||
| 1349 | pub const epoll_event = packed struct { | ||
| 1350 | events: u32, | ||
| 1351 | data: epoll_data, | ||
| 1352 | }; | ||
| 1353 | |||
| 1354 | pub fn epoll_create() usize { | ||
| 1355 | return epoll_create1(0); | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | pub fn epoll_create1(flags: usize) usize { | ||
| 1359 | return syscall1(SYS_epoll_create1, flags); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { | ||
| 1363 | return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { | ||
| 1367 | return epoll_pwait(epoll_fd, events, maxevents, timeout, null); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { | ||
| 1371 | return syscall6( | ||
| 1372 | SYS_epoll_pwait, | ||
| 1373 | @bitCast(usize, isize(epoll_fd)), | ||
| 1374 | @ptrToInt(events), | ||
| 1375 | @intCast(usize, maxevents), | ||
| 1376 | @bitCast(usize, isize(timeout)), | ||
| 1377 | @ptrToInt(sigmask), | ||
| 1378 | @sizeOf(sigset_t), | ||
| 1379 | ); | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | pub fn eventfd(count: u32, flags: u32) usize { | ||
| 1383 | return syscall2(SYS_eventfd2, count, flags); | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | pub fn timerfd_create(clockid: i32, flags: u32) usize { | ||
| 1387 | return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | pub const itimerspec = extern struct { | ||
| 1391 | it_interval: timespec, | ||
| 1392 | it_value: timespec, | ||
| 1393 | }; | ||
| 1394 | |||
| 1395 | pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { | ||
| 1396 | return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { | ||
| 1400 | return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; | ||
| 1404 | pub const _LINUX_CAPABILITY_U32S_1 = 1; | ||
| 1405 | |||
| 1406 | pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; | ||
| 1407 | pub const _LINUX_CAPABILITY_U32S_2 = 2; | ||
| 1408 | |||
| 1409 | pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; | ||
| 1410 | pub const _LINUX_CAPABILITY_U32S_3 = 2; | ||
| 1411 | |||
| 1412 | pub const VFS_CAP_REVISION_MASK = 0xFF000000; | ||
| 1413 | pub const VFS_CAP_REVISION_SHIFT = 24; | ||
| 1414 | pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; | ||
| 1415 | pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; | ||
| 1416 | |||
| 1417 | pub const VFS_CAP_REVISION_1 = 0x01000000; | ||
| 1418 | pub const VFS_CAP_U32_1 = 1; | ||
| 1419 | pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); | ||
| 1420 | |||
| 1421 | pub const VFS_CAP_REVISION_2 = 0x02000000; | ||
| 1422 | pub const VFS_CAP_U32_2 = 2; | ||
| 1423 | pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); | ||
| 1424 | |||
| 1425 | pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; | ||
| 1426 | pub const VFS_CAP_U32 = VFS_CAP_U32_2; | ||
| 1427 | pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; | ||
| 1428 | |||
| 1429 | pub const vfs_cap_data = extern struct { | ||
| 1430 | //all of these are mandated as little endian | ||
| 1431 | //when on disk. | ||
| 1432 | const Data = struct { | ||
| 1433 | permitted: u32, | ||
| 1434 | inheritable: u32, | ||
| 1435 | }; | ||
| 1436 | |||
| 1437 | magic_etc: u32, | ||
| 1438 | data: [VFS_CAP_U32]Data, | ||
| 1439 | }; | ||
| 1440 | |||
| 1441 | pub const CAP_CHOWN = 0; | ||
| 1442 | pub const CAP_DAC_OVERRIDE = 1; | ||
| 1443 | pub const CAP_DAC_READ_SEARCH = 2; | ||
| 1444 | pub const CAP_FOWNER = 3; | ||
| 1445 | pub const CAP_FSETID = 4; | ||
| 1446 | pub const CAP_KILL = 5; | ||
| 1447 | pub const CAP_SETGID = 6; | ||
| 1448 | pub const CAP_SETUID = 7; | ||
| 1449 | pub const CAP_SETPCAP = 8; | ||
| 1450 | pub const CAP_LINUX_IMMUTABLE = 9; | ||
| 1451 | pub const CAP_NET_BIND_SERVICE = 10; | ||
| 1452 | pub const CAP_NET_BROADCAST = 11; | ||
| 1453 | pub const CAP_NET_ADMIN = 12; | ||
| 1454 | pub const CAP_NET_RAW = 13; | ||
| 1455 | pub const CAP_IPC_LOCK = 14; | ||
| 1456 | pub const CAP_IPC_OWNER = 15; | ||
| 1457 | pub const CAP_SYS_MODULE = 16; | ||
| 1458 | pub const CAP_SYS_RAWIO = 17; | ||
| 1459 | pub const CAP_SYS_CHROOT = 18; | ||
| 1460 | pub const CAP_SYS_PTRACE = 19; | ||
| 1461 | pub const CAP_SYS_PACCT = 20; | ||
| 1462 | pub const CAP_SYS_ADMIN = 21; | ||
| 1463 | pub const CAP_SYS_BOOT = 22; | ||
| 1464 | pub const CAP_SYS_NICE = 23; | ||
| 1465 | pub const CAP_SYS_RESOURCE = 24; | ||
| 1466 | pub const CAP_SYS_TIME = 25; | ||
| 1467 | pub const CAP_SYS_TTY_CONFIG = 26; | ||
| 1468 | pub const CAP_MKNOD = 27; | ||
| 1469 | pub const CAP_LEASE = 28; | ||
| 1470 | pub const CAP_AUDIT_WRITE = 29; | ||
| 1471 | pub const CAP_AUDIT_CONTROL = 30; | ||
| 1472 | pub const CAP_SETFCAP = 31; | ||
| 1473 | pub const CAP_MAC_OVERRIDE = 32; | ||
| 1474 | pub const CAP_MAC_ADMIN = 33; | ||
| 1475 | pub const CAP_SYSLOG = 34; | ||
| 1476 | pub const CAP_WAKE_ALARM = 35; | ||
| 1477 | pub const CAP_BLOCK_SUSPEND = 36; | ||
| 1478 | pub const CAP_AUDIT_READ = 37; | ||
| 1479 | pub const CAP_LAST_CAP = CAP_AUDIT_READ; | ||
| 1480 | |||
| 1481 | pub fn cap_valid(u8: x) bool { | ||
| 1482 | return x >= 0 and x <= CAP_LAST_CAP; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | pub fn CAP_TO_MASK(cap: u8) u32 { | ||
| 1486 | return u32(1) << u5(cap & 31); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | pub fn CAP_TO_INDEX(cap: u8) u8 { | ||
| 1490 | return cap >> 5; | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | pub const cap_t = extern struct { | ||
| 1494 | hdrp: *cap_user_header_t, | ||
| 1495 | datap: *cap_user_data_t, | ||
| 1496 | }; | ||
| 1497 | |||
| 1498 | pub const cap_user_header_t = extern struct { | ||
| 1499 | version: u32, | ||
| 1500 | pid: usize, | ||
| 1501 | }; | ||
| 1502 | |||
| 1503 | pub const cap_user_data_t = extern struct { | ||
| 1504 | effective: u32, | ||
| 1505 | permitted: u32, | ||
| 1506 | inheritable: u32, | ||
| 1507 | }; | ||
| 1508 | |||
| 1509 | pub fn unshare(flags: usize) usize { | ||
| 1510 | return syscall1(SYS_unshare, flags); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { | ||
| 1514 | return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { | ||
| 1518 | return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | pub const inotify_event = extern struct { | ||
| 1522 | wd: i32, | ||
| 1523 | mask: u32, | ||
| 1524 | cookie: u32, | ||
| 1525 | len: u32, | ||
| 1526 | //name: [?]u8, | ||
| 1527 | }; | ||
| 1528 | |||
| 1529 | pub const dirent64 = extern struct { | ||
| 1530 | d_ino: u64, | ||
| 1531 | d_off: u64, | ||
| 1532 | d_reclen: u16, | ||
| 1533 | d_type: u8, | ||
| 1534 | d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 | ||
| 1535 | }; | ||
| 1536 | |||
| 1537 | test "import" { | ||
| 1538 | if (builtin.os == builtin.Os.linux) { | ||
| 1539 | _ = @import("linux/test.zig"); | ||
| 1540 | } | ||
| 1541 | } | ||
std/os/linux/arm64.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const linux = std.os.linux; | 2 | const linux = std.os.linux; |
| 3 | const socklen_t = linux.socklen_t; | 3 | const socklen_t = linux.socklen_t; |
| 4 | const iovec = linux.iovec; | 4 | const iovec = linux.iovec; |
std/os/linux/index.zig deleted-1541| ... | @@ -1,1541 +0,0 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const builtin = @import("builtin"); | ||
| 4 | const maxInt = std.math.maxInt; | ||
| 5 | const vdso = @import("vdso.zig"); | ||
| 6 | pub use switch (builtin.arch) { | ||
| 7 | builtin.Arch.x86_64 => @import("x86_64.zig"), | ||
| 8 | builtin.Arch.i386 => @import("i386.zig"), | ||
| 9 | builtin.Arch.aarch64 => @import("arm64.zig"), | ||
| 10 | else => @compileError("unsupported arch"), | ||
| 11 | }; | ||
| 12 | pub use @import("errno.zig"); | ||
| 13 | |||
| 14 | pub const PATH_MAX = 4096; | ||
| 15 | |||
| 16 | pub const STDIN_FILENO = 0; | ||
| 17 | pub const STDOUT_FILENO = 1; | ||
| 18 | pub const STDERR_FILENO = 2; | ||
| 19 | |||
| 20 | pub const FUTEX_WAIT = 0; | ||
| 21 | pub const FUTEX_WAKE = 1; | ||
| 22 | pub const FUTEX_FD = 2; | ||
| 23 | pub const FUTEX_REQUEUE = 3; | ||
| 24 | pub const FUTEX_CMP_REQUEUE = 4; | ||
| 25 | pub const FUTEX_WAKE_OP = 5; | ||
| 26 | pub const FUTEX_LOCK_PI = 6; | ||
| 27 | pub const FUTEX_UNLOCK_PI = 7; | ||
| 28 | pub const FUTEX_TRYLOCK_PI = 8; | ||
| 29 | pub const FUTEX_WAIT_BITSET = 9; | ||
| 30 | |||
| 31 | pub const FUTEX_PRIVATE_FLAG = 128; | ||
| 32 | |||
| 33 | pub const FUTEX_CLOCK_REALTIME = 256; | ||
| 34 | |||
| 35 | pub const PROT_NONE = 0; | ||
| 36 | pub const PROT_READ = 1; | ||
| 37 | pub const PROT_WRITE = 2; | ||
| 38 | pub const PROT_EXEC = 4; | ||
| 39 | pub const PROT_GROWSDOWN = 0x01000000; | ||
| 40 | pub const PROT_GROWSUP = 0x02000000; | ||
| 41 | |||
| 42 | pub const MAP_FAILED = maxInt(usize); | ||
| 43 | pub const MAP_SHARED = 0x01; | ||
| 44 | pub const MAP_PRIVATE = 0x02; | ||
| 45 | pub const MAP_TYPE = 0x0f; | ||
| 46 | pub const MAP_FIXED = 0x10; | ||
| 47 | pub const MAP_ANONYMOUS = 0x20; | ||
| 48 | pub const MAP_NORESERVE = 0x4000; | ||
| 49 | pub const MAP_GROWSDOWN = 0x0100; | ||
| 50 | pub const MAP_DENYWRITE = 0x0800; | ||
| 51 | pub const MAP_EXECUTABLE = 0x1000; | ||
| 52 | pub const MAP_LOCKED = 0x2000; | ||
| 53 | pub const MAP_POPULATE = 0x8000; | ||
| 54 | pub const MAP_NONBLOCK = 0x10000; | ||
| 55 | pub const MAP_STACK = 0x20000; | ||
| 56 | pub const MAP_HUGETLB = 0x40000; | ||
| 57 | pub const MAP_FILE = 0; | ||
| 58 | |||
| 59 | pub const F_OK = 0; | ||
| 60 | pub const X_OK = 1; | ||
| 61 | pub const W_OK = 2; | ||
| 62 | pub const R_OK = 4; | ||
| 63 | |||
| 64 | pub const WNOHANG = 1; | ||
| 65 | pub const WUNTRACED = 2; | ||
| 66 | pub const WSTOPPED = 2; | ||
| 67 | pub const WEXITED = 4; | ||
| 68 | pub const WCONTINUED = 8; | ||
| 69 | pub const WNOWAIT = 0x1000000; | ||
| 70 | |||
| 71 | pub const SA_NOCLDSTOP = 1; | ||
| 72 | pub const SA_NOCLDWAIT = 2; | ||
| 73 | pub const SA_SIGINFO = 4; | ||
| 74 | pub const SA_ONSTACK = 0x08000000; | ||
| 75 | pub const SA_RESTART = 0x10000000; | ||
| 76 | pub const SA_NODEFER = 0x40000000; | ||
| 77 | pub const SA_RESETHAND = 0x80000000; | ||
| 78 | pub const SA_RESTORER = 0x04000000; | ||
| 79 | |||
| 80 | pub const SIGHUP = 1; | ||
| 81 | pub const SIGINT = 2; | ||
| 82 | pub const SIGQUIT = 3; | ||
| 83 | pub const SIGILL = 4; | ||
| 84 | pub const SIGTRAP = 5; | ||
| 85 | pub const SIGABRT = 6; | ||
| 86 | pub const SIGIOT = SIGABRT; | ||
| 87 | pub const SIGBUS = 7; | ||
| 88 | pub const SIGFPE = 8; | ||
| 89 | pub const SIGKILL = 9; | ||
| 90 | pub const SIGUSR1 = 10; | ||
| 91 | pub const SIGSEGV = 11; | ||
| 92 | pub const SIGUSR2 = 12; | ||
| 93 | pub const SIGPIPE = 13; | ||
| 94 | pub const SIGALRM = 14; | ||
| 95 | pub const SIGTERM = 15; | ||
| 96 | pub const SIGSTKFLT = 16; | ||
| 97 | pub const SIGCHLD = 17; | ||
| 98 | pub const SIGCONT = 18; | ||
| 99 | pub const SIGSTOP = 19; | ||
| 100 | pub const SIGTSTP = 20; | ||
| 101 | pub const SIGTTIN = 21; | ||
| 102 | pub const SIGTTOU = 22; | ||
| 103 | pub const SIGURG = 23; | ||
| 104 | pub const SIGXCPU = 24; | ||
| 105 | pub const SIGXFSZ = 25; | ||
| 106 | pub const SIGVTALRM = 26; | ||
| 107 | pub const SIGPROF = 27; | ||
| 108 | pub const SIGWINCH = 28; | ||
| 109 | pub const SIGIO = 29; | ||
| 110 | pub const SIGPOLL = 29; | ||
| 111 | pub const SIGPWR = 30; | ||
| 112 | pub const SIGSYS = 31; | ||
| 113 | pub const SIGUNUSED = SIGSYS; | ||
| 114 | |||
| 115 | pub const O_RDONLY = 0o0; | ||
| 116 | pub const O_WRONLY = 0o1; | ||
| 117 | pub const O_RDWR = 0o2; | ||
| 118 | |||
| 119 | pub const SEEK_SET = 0; | ||
| 120 | pub const SEEK_CUR = 1; | ||
| 121 | pub const SEEK_END = 2; | ||
| 122 | |||
| 123 | pub const SIG_BLOCK = 0; | ||
| 124 | pub const SIG_UNBLOCK = 1; | ||
| 125 | pub const SIG_SETMASK = 2; | ||
| 126 | |||
| 127 | pub const PROTO_ip = 0o000; | ||
| 128 | pub const PROTO_icmp = 0o001; | ||
| 129 | pub const PROTO_igmp = 0o002; | ||
| 130 | pub const PROTO_ggp = 0o003; | ||
| 131 | pub const PROTO_ipencap = 0o004; | ||
| 132 | pub const PROTO_st = 0o005; | ||
| 133 | pub const PROTO_tcp = 0o006; | ||
| 134 | pub const PROTO_egp = 0o010; | ||
| 135 | pub const PROTO_pup = 0o014; | ||
| 136 | pub const PROTO_udp = 0o021; | ||
| 137 | pub const PROTO_hmp = 0o024; | ||
| 138 | pub const PROTO_xns_idp = 0o026; | ||
| 139 | pub const PROTO_rdp = 0o033; | ||
| 140 | pub const PROTO_iso_tp4 = 0o035; | ||
| 141 | pub const PROTO_xtp = 0o044; | ||
| 142 | pub const PROTO_ddp = 0o045; | ||
| 143 | pub const PROTO_idpr_cmtp = 0o046; | ||
| 144 | pub const PROTO_ipv6 = 0o051; | ||
| 145 | pub const PROTO_ipv6_route = 0o053; | ||
| 146 | pub const PROTO_ipv6_frag = 0o054; | ||
| 147 | pub const PROTO_idrp = 0o055; | ||
| 148 | pub const PROTO_rsvp = 0o056; | ||
| 149 | pub const PROTO_gre = 0o057; | ||
| 150 | pub const PROTO_esp = 0o062; | ||
| 151 | pub const PROTO_ah = 0o063; | ||
| 152 | pub const PROTO_skip = 0o071; | ||
| 153 | pub const PROTO_ipv6_icmp = 0o072; | ||
| 154 | pub const PROTO_ipv6_nonxt = 0o073; | ||
| 155 | pub const PROTO_ipv6_opts = 0o074; | ||
| 156 | pub const PROTO_rspf = 0o111; | ||
| 157 | pub const PROTO_vmtp = 0o121; | ||
| 158 | pub const PROTO_ospf = 0o131; | ||
| 159 | pub const PROTO_ipip = 0o136; | ||
| 160 | pub const PROTO_encap = 0o142; | ||
| 161 | pub const PROTO_pim = 0o147; | ||
| 162 | pub const PROTO_raw = 0o377; | ||
| 163 | |||
| 164 | pub const SHUT_RD = 0; | ||
| 165 | pub const SHUT_WR = 1; | ||
| 166 | pub const SHUT_RDWR = 2; | ||
| 167 | |||
| 168 | pub const SOCK_STREAM = 1; | ||
| 169 | pub const SOCK_DGRAM = 2; | ||
| 170 | pub const SOCK_RAW = 3; | ||
| 171 | pub const SOCK_RDM = 4; | ||
| 172 | pub const SOCK_SEQPACKET = 5; | ||
| 173 | pub const SOCK_DCCP = 6; | ||
| 174 | pub const SOCK_PACKET = 10; | ||
| 175 | pub const SOCK_CLOEXEC = 0o2000000; | ||
| 176 | pub const SOCK_NONBLOCK = 0o4000; | ||
| 177 | |||
| 178 | pub const PF_UNSPEC = 0; | ||
| 179 | pub const PF_LOCAL = 1; | ||
| 180 | pub const PF_UNIX = PF_LOCAL; | ||
| 181 | pub const PF_FILE = PF_LOCAL; | ||
| 182 | pub const PF_INET = 2; | ||
| 183 | pub const PF_AX25 = 3; | ||
| 184 | pub const PF_IPX = 4; | ||
| 185 | pub const PF_APPLETALK = 5; | ||
| 186 | pub const PF_NETROM = 6; | ||
| 187 | pub const PF_BRIDGE = 7; | ||
| 188 | pub const PF_ATMPVC = 8; | ||
| 189 | pub const PF_X25 = 9; | ||
| 190 | pub const PF_INET6 = 10; | ||
| 191 | pub const PF_ROSE = 11; | ||
| 192 | pub const PF_DECnet = 12; | ||
| 193 | pub const PF_NETBEUI = 13; | ||
| 194 | pub const PF_SECURITY = 14; | ||
| 195 | pub const PF_KEY = 15; | ||
| 196 | pub const PF_NETLINK = 16; | ||
| 197 | pub const PF_ROUTE = PF_NETLINK; | ||
| 198 | pub const PF_PACKET = 17; | ||
| 199 | pub const PF_ASH = 18; | ||
| 200 | pub const PF_ECONET = 19; | ||
| 201 | pub const PF_ATMSVC = 20; | ||
| 202 | pub const PF_RDS = 21; | ||
| 203 | pub const PF_SNA = 22; | ||
| 204 | pub const PF_IRDA = 23; | ||
| 205 | pub const PF_PPPOX = 24; | ||
| 206 | pub const PF_WANPIPE = 25; | ||
| 207 | pub const PF_LLC = 26; | ||
| 208 | pub const PF_IB = 27; | ||
| 209 | pub const PF_MPLS = 28; | ||
| 210 | pub const PF_CAN = 29; | ||
| 211 | pub const PF_TIPC = 30; | ||
| 212 | pub const PF_BLUETOOTH = 31; | ||
| 213 | pub const PF_IUCV = 32; | ||
| 214 | pub const PF_RXRPC = 33; | ||
| 215 | pub const PF_ISDN = 34; | ||
| 216 | pub const PF_PHONET = 35; | ||
| 217 | pub const PF_IEEE802154 = 36; | ||
| 218 | pub const PF_CAIF = 37; | ||
| 219 | pub const PF_ALG = 38; | ||
| 220 | pub const PF_NFC = 39; | ||
| 221 | pub const PF_VSOCK = 40; | ||
| 222 | pub const PF_KCM = 41; | ||
| 223 | pub const PF_QIPCRTR = 42; | ||
| 224 | pub const PF_SMC = 43; | ||
| 225 | pub const PF_MAX = 44; | ||
| 226 | |||
| 227 | pub const AF_UNSPEC = PF_UNSPEC; | ||
| 228 | pub const AF_LOCAL = PF_LOCAL; | ||
| 229 | pub const AF_UNIX = AF_LOCAL; | ||
| 230 | pub const AF_FILE = AF_LOCAL; | ||
| 231 | pub const AF_INET = PF_INET; | ||
| 232 | pub const AF_AX25 = PF_AX25; | ||
| 233 | pub const AF_IPX = PF_IPX; | ||
| 234 | pub const AF_APPLETALK = PF_APPLETALK; | ||
| 235 | pub const AF_NETROM = PF_NETROM; | ||
| 236 | pub const AF_BRIDGE = PF_BRIDGE; | ||
| 237 | pub const AF_ATMPVC = PF_ATMPVC; | ||
| 238 | pub const AF_X25 = PF_X25; | ||
| 239 | pub const AF_INET6 = PF_INET6; | ||
| 240 | pub const AF_ROSE = PF_ROSE; | ||
| 241 | pub const AF_DECnet = PF_DECnet; | ||
| 242 | pub const AF_NETBEUI = PF_NETBEUI; | ||
| 243 | pub const AF_SECURITY = PF_SECURITY; | ||
| 244 | pub const AF_KEY = PF_KEY; | ||
| 245 | pub const AF_NETLINK = PF_NETLINK; | ||
| 246 | pub const AF_ROUTE = PF_ROUTE; | ||
| 247 | pub const AF_PACKET = PF_PACKET; | ||
| 248 | pub const AF_ASH = PF_ASH; | ||
| 249 | pub const AF_ECONET = PF_ECONET; | ||
| 250 | pub const AF_ATMSVC = PF_ATMSVC; | ||
| 251 | pub const AF_RDS = PF_RDS; | ||
| 252 | pub const AF_SNA = PF_SNA; | ||
| 253 | pub const AF_IRDA = PF_IRDA; | ||
| 254 | pub const AF_PPPOX = PF_PPPOX; | ||
| 255 | pub const AF_WANPIPE = PF_WANPIPE; | ||
| 256 | pub const AF_LLC = PF_LLC; | ||
| 257 | pub const AF_IB = PF_IB; | ||
| 258 | pub const AF_MPLS = PF_MPLS; | ||
| 259 | pub const AF_CAN = PF_CAN; | ||
| 260 | pub const AF_TIPC = PF_TIPC; | ||
| 261 | pub const AF_BLUETOOTH = PF_BLUETOOTH; | ||
| 262 | pub const AF_IUCV = PF_IUCV; | ||
| 263 | pub const AF_RXRPC = PF_RXRPC; | ||
| 264 | pub const AF_ISDN = PF_ISDN; | ||
| 265 | pub const AF_PHONET = PF_PHONET; | ||
| 266 | pub const AF_IEEE802154 = PF_IEEE802154; | ||
| 267 | pub const AF_CAIF = PF_CAIF; | ||
| 268 | pub const AF_ALG = PF_ALG; | ||
| 269 | pub const AF_NFC = PF_NFC; | ||
| 270 | pub const AF_VSOCK = PF_VSOCK; | ||
| 271 | pub const AF_KCM = PF_KCM; | ||
| 272 | pub const AF_QIPCRTR = PF_QIPCRTR; | ||
| 273 | pub const AF_SMC = PF_SMC; | ||
| 274 | pub const AF_MAX = PF_MAX; | ||
| 275 | |||
| 276 | pub const SO_DEBUG = 1; | ||
| 277 | pub const SO_REUSEADDR = 2; | ||
| 278 | pub const SO_TYPE = 3; | ||
| 279 | pub const SO_ERROR = 4; | ||
| 280 | pub const SO_DONTROUTE = 5; | ||
| 281 | pub const SO_BROADCAST = 6; | ||
| 282 | pub const SO_SNDBUF = 7; | ||
| 283 | pub const SO_RCVBUF = 8; | ||
| 284 | pub const SO_KEEPALIVE = 9; | ||
| 285 | pub const SO_OOBINLINE = 10; | ||
| 286 | pub const SO_NO_CHECK = 11; | ||
| 287 | pub const SO_PRIORITY = 12; | ||
| 288 | pub const SO_LINGER = 13; | ||
| 289 | pub const SO_BSDCOMPAT = 14; | ||
| 290 | pub const SO_REUSEPORT = 15; | ||
| 291 | pub const SO_PASSCRED = 16; | ||
| 292 | pub const SO_PEERCRED = 17; | ||
| 293 | pub const SO_RCVLOWAT = 18; | ||
| 294 | pub const SO_SNDLOWAT = 19; | ||
| 295 | pub const SO_RCVTIMEO = 20; | ||
| 296 | pub const SO_SNDTIMEO = 21; | ||
| 297 | pub const SO_ACCEPTCONN = 30; | ||
| 298 | pub const SO_SNDBUFFORCE = 32; | ||
| 299 | pub const SO_RCVBUFFORCE = 33; | ||
| 300 | pub const SO_PROTOCOL = 38; | ||
| 301 | pub const SO_DOMAIN = 39; | ||
| 302 | |||
| 303 | pub const SO_SECURITY_AUTHENTICATION = 22; | ||
| 304 | pub const SO_SECURITY_ENCRYPTION_TRANSPORT = 23; | ||
| 305 | pub const SO_SECURITY_ENCRYPTION_NETWORK = 24; | ||
| 306 | |||
| 307 | pub const SO_BINDTODEVICE = 25; | ||
| 308 | |||
| 309 | pub const SO_ATTACH_FILTER = 26; | ||
| 310 | pub const SO_DETACH_FILTER = 27; | ||
| 311 | pub const SO_GET_FILTER = SO_ATTACH_FILTER; | ||
| 312 | |||
| 313 | pub const SO_PEERNAME = 28; | ||
| 314 | pub const SO_TIMESTAMP = 29; | ||
| 315 | pub const SCM_TIMESTAMP = SO_TIMESTAMP; | ||
| 316 | |||
| 317 | pub const SO_PEERSEC = 31; | ||
| 318 | pub const SO_PASSSEC = 34; | ||
| 319 | pub const SO_TIMESTAMPNS = 35; | ||
| 320 | pub const SCM_TIMESTAMPNS = SO_TIMESTAMPNS; | ||
| 321 | pub const SO_MARK = 36; | ||
| 322 | pub const SO_TIMESTAMPING = 37; | ||
| 323 | pub const SCM_TIMESTAMPING = SO_TIMESTAMPING; | ||
| 324 | pub const SO_RXQ_OVFL = 40; | ||
| 325 | pub const SO_WIFI_STATUS = 41; | ||
| 326 | pub const SCM_WIFI_STATUS = SO_WIFI_STATUS; | ||
| 327 | pub const SO_PEEK_OFF = 42; | ||
| 328 | pub const SO_NOFCS = 43; | ||
| 329 | pub const SO_LOCK_FILTER = 44; | ||
| 330 | pub const SO_SELECT_ERR_QUEUE = 45; | ||
| 331 | pub const SO_BUSY_POLL = 46; | ||
| 332 | pub const SO_MAX_PACING_RATE = 47; | ||
| 333 | pub const SO_BPF_EXTENSIONS = 48; | ||
| 334 | pub const SO_INCOMING_CPU = 49; | ||
| 335 | pub const SO_ATTACH_BPF = 50; | ||
| 336 | pub const SO_DETACH_BPF = SO_DETACH_FILTER; | ||
| 337 | pub const SO_ATTACH_REUSEPORT_CBPF = 51; | ||
| 338 | pub const SO_ATTACH_REUSEPORT_EBPF = 52; | ||
| 339 | pub const SO_CNX_ADVICE = 53; | ||
| 340 | pub const SCM_TIMESTAMPING_OPT_STATS = 54; | ||
| 341 | pub const SO_MEMINFO = 55; | ||
| 342 | pub const SO_INCOMING_NAPI_ID = 56; | ||
| 343 | pub const SO_COOKIE = 57; | ||
| 344 | pub const SCM_TIMESTAMPING_PKTINFO = 58; | ||
| 345 | pub const SO_PEERGROUPS = 59; | ||
| 346 | pub const SO_ZEROCOPY = 60; | ||
| 347 | |||
| 348 | pub const SOL_SOCKET = 1; | ||
| 349 | |||
| 350 | pub const SOL_IP = 0; | ||
| 351 | pub const SOL_IPV6 = 41; | ||
| 352 | pub const SOL_ICMPV6 = 58; | ||
| 353 | |||
| 354 | pub const SOL_RAW = 255; | ||
| 355 | pub const SOL_DECNET = 261; | ||
| 356 | pub const SOL_X25 = 262; | ||
| 357 | pub const SOL_PACKET = 263; | ||
| 358 | pub const SOL_ATM = 264; | ||
| 359 | pub const SOL_AAL = 265; | ||
| 360 | pub const SOL_IRDA = 266; | ||
| 361 | pub const SOL_NETBEUI = 267; | ||
| 362 | pub const SOL_LLC = 268; | ||
| 363 | pub const SOL_DCCP = 269; | ||
| 364 | pub const SOL_NETLINK = 270; | ||
| 365 | pub const SOL_TIPC = 271; | ||
| 366 | pub const SOL_RXRPC = 272; | ||
| 367 | pub const SOL_PPPOL2TP = 273; | ||
| 368 | pub const SOL_BLUETOOTH = 274; | ||
| 369 | pub const SOL_PNPIPE = 275; | ||
| 370 | pub const SOL_RDS = 276; | ||
| 371 | pub const SOL_IUCV = 277; | ||
| 372 | pub const SOL_CAIF = 278; | ||
| 373 | pub const SOL_ALG = 279; | ||
| 374 | pub const SOL_NFC = 280; | ||
| 375 | pub const SOL_KCM = 281; | ||
| 376 | pub const SOL_TLS = 282; | ||
| 377 | |||
| 378 | pub const SOMAXCONN = 128; | ||
| 379 | |||
| 380 | pub const MSG_OOB = 0x0001; | ||
| 381 | pub const MSG_PEEK = 0x0002; | ||
| 382 | pub const MSG_DONTROUTE = 0x0004; | ||
| 383 | pub const MSG_CTRUNC = 0x0008; | ||
| 384 | pub const MSG_PROXY = 0x0010; | ||
| 385 | pub const MSG_TRUNC = 0x0020; | ||
| 386 | pub const MSG_DONTWAIT = 0x0040; | ||
| 387 | pub const MSG_EOR = 0x0080; | ||
| 388 | pub const MSG_WAITALL = 0x0100; | ||
| 389 | pub const MSG_FIN = 0x0200; | ||
| 390 | pub const MSG_SYN = 0x0400; | ||
| 391 | pub const MSG_CONFIRM = 0x0800; | ||
| 392 | pub const MSG_RST = 0x1000; | ||
| 393 | pub const MSG_ERRQUEUE = 0x2000; | ||
| 394 | pub const MSG_NOSIGNAL = 0x4000; | ||
| 395 | pub const MSG_MORE = 0x8000; | ||
| 396 | pub const MSG_WAITFORONE = 0x10000; | ||
| 397 | pub const MSG_BATCH = 0x40000; | ||
| 398 | pub const MSG_ZEROCOPY = 0x4000000; | ||
| 399 | pub const MSG_FASTOPEN = 0x20000000; | ||
| 400 | pub const MSG_CMSG_CLOEXEC = 0x40000000; | ||
| 401 | |||
| 402 | pub const DT_UNKNOWN = 0; | ||
| 403 | pub const DT_FIFO = 1; | ||
| 404 | pub const DT_CHR = 2; | ||
| 405 | pub const DT_DIR = 4; | ||
| 406 | pub const DT_BLK = 6; | ||
| 407 | pub const DT_REG = 8; | ||
| 408 | pub const DT_LNK = 10; | ||
| 409 | pub const DT_SOCK = 12; | ||
| 410 | pub const DT_WHT = 14; | ||
| 411 | |||
| 412 | pub const TCGETS = 0x5401; | ||
| 413 | pub const TCSETS = 0x5402; | ||
| 414 | pub const TCSETSW = 0x5403; | ||
| 415 | pub const TCSETSF = 0x5404; | ||
| 416 | pub const TCGETA = 0x5405; | ||
| 417 | pub const TCSETA = 0x5406; | ||
| 418 | pub const TCSETAW = 0x5407; | ||
| 419 | pub const TCSETAF = 0x5408; | ||
| 420 | pub const TCSBRK = 0x5409; | ||
| 421 | pub const TCXONC = 0x540A; | ||
| 422 | pub const TCFLSH = 0x540B; | ||
| 423 | pub const TIOCEXCL = 0x540C; | ||
| 424 | pub const TIOCNXCL = 0x540D; | ||
| 425 | pub const TIOCSCTTY = 0x540E; | ||
| 426 | pub const TIOCGPGRP = 0x540F; | ||
| 427 | pub const TIOCSPGRP = 0x5410; | ||
| 428 | pub const TIOCOUTQ = 0x5411; | ||
| 429 | pub const TIOCSTI = 0x5412; | ||
| 430 | pub const TIOCGWINSZ = 0x5413; | ||
| 431 | pub const TIOCSWINSZ = 0x5414; | ||
| 432 | pub const TIOCMGET = 0x5415; | ||
| 433 | pub const TIOCMBIS = 0x5416; | ||
| 434 | pub const TIOCMBIC = 0x5417; | ||
| 435 | pub const TIOCMSET = 0x5418; | ||
| 436 | pub const TIOCGSOFTCAR = 0x5419; | ||
| 437 | pub const TIOCSSOFTCAR = 0x541A; | ||
| 438 | pub const FIONREAD = 0x541B; | ||
| 439 | pub const TIOCINQ = FIONREAD; | ||
| 440 | pub const TIOCLINUX = 0x541C; | ||
| 441 | pub const TIOCCONS = 0x541D; | ||
| 442 | pub const TIOCGSERIAL = 0x541E; | ||
| 443 | pub const TIOCSSERIAL = 0x541F; | ||
| 444 | pub const TIOCPKT = 0x5420; | ||
| 445 | pub const FIONBIO = 0x5421; | ||
| 446 | pub const TIOCNOTTY = 0x5422; | ||
| 447 | pub const TIOCSETD = 0x5423; | ||
| 448 | pub const TIOCGETD = 0x5424; | ||
| 449 | pub const TCSBRKP = 0x5425; | ||
| 450 | pub const TIOCSBRK = 0x5427; | ||
| 451 | pub const TIOCCBRK = 0x5428; | ||
| 452 | pub const TIOCGSID = 0x5429; | ||
| 453 | pub const TIOCGRS485 = 0x542E; | ||
| 454 | pub const TIOCSRS485 = 0x542F; | ||
| 455 | pub const TIOCGPTN = 0x80045430; | ||
| 456 | pub const TIOCSPTLCK = 0x40045431; | ||
| 457 | pub const TIOCGDEV = 0x80045432; | ||
| 458 | pub const TCGETX = 0x5432; | ||
| 459 | pub const TCSETX = 0x5433; | ||
| 460 | pub const TCSETXF = 0x5434; | ||
| 461 | pub const TCSETXW = 0x5435; | ||
| 462 | pub const TIOCSIG = 0x40045436; | ||
| 463 | pub const TIOCVHANGUP = 0x5437; | ||
| 464 | pub const TIOCGPKT = 0x80045438; | ||
| 465 | pub const TIOCGPTLCK = 0x80045439; | ||
| 466 | pub const TIOCGEXCL = 0x80045440; | ||
| 467 | |||
| 468 | pub const EPOLL_CLOEXEC = O_CLOEXEC; | ||
| 469 | |||
| 470 | pub const EPOLL_CTL_ADD = 1; | ||
| 471 | pub const EPOLL_CTL_DEL = 2; | ||
| 472 | pub const EPOLL_CTL_MOD = 3; | ||
| 473 | |||
| 474 | pub const EPOLLIN = 0x001; | ||
| 475 | pub const EPOLLPRI = 0x002; | ||
| 476 | pub const EPOLLOUT = 0x004; | ||
| 477 | pub const EPOLLRDNORM = 0x040; | ||
| 478 | pub const EPOLLRDBAND = 0x080; | ||
| 479 | pub const EPOLLWRNORM = 0x100; | ||
| 480 | pub const EPOLLWRBAND = 0x200; | ||
| 481 | pub const EPOLLMSG = 0x400; | ||
| 482 | pub const EPOLLERR = 0x008; | ||
| 483 | pub const EPOLLHUP = 0x010; | ||
| 484 | pub const EPOLLRDHUP = 0x2000; | ||
| 485 | pub const EPOLLEXCLUSIVE = (u32(1) << 28); | ||
| 486 | pub const EPOLLWAKEUP = (u32(1) << 29); | ||
| 487 | pub const EPOLLONESHOT = (u32(1) << 30); | ||
| 488 | pub const EPOLLET = (u32(1) << 31); | ||
| 489 | |||
| 490 | pub const CLOCK_REALTIME = 0; | ||
| 491 | pub const CLOCK_MONOTONIC = 1; | ||
| 492 | pub const CLOCK_PROCESS_CPUTIME_ID = 2; | ||
| 493 | pub const CLOCK_THREAD_CPUTIME_ID = 3; | ||
| 494 | pub const CLOCK_MONOTONIC_RAW = 4; | ||
| 495 | pub const CLOCK_REALTIME_COARSE = 5; | ||
| 496 | pub const CLOCK_MONOTONIC_COARSE = 6; | ||
| 497 | pub const CLOCK_BOOTTIME = 7; | ||
| 498 | pub const CLOCK_REALTIME_ALARM = 8; | ||
| 499 | pub const CLOCK_BOOTTIME_ALARM = 9; | ||
| 500 | pub const CLOCK_SGI_CYCLE = 10; | ||
| 501 | pub const CLOCK_TAI = 11; | ||
| 502 | |||
| 503 | pub const CSIGNAL = 0x000000ff; | ||
| 504 | pub const CLONE_VM = 0x00000100; | ||
| 505 | pub const CLONE_FS = 0x00000200; | ||
| 506 | pub const CLONE_FILES = 0x00000400; | ||
| 507 | pub const CLONE_SIGHAND = 0x00000800; | ||
| 508 | pub const CLONE_PTRACE = 0x00002000; | ||
| 509 | pub const CLONE_VFORK = 0x00004000; | ||
| 510 | pub const CLONE_PARENT = 0x00008000; | ||
| 511 | pub const CLONE_THREAD = 0x00010000; | ||
| 512 | pub const CLONE_NEWNS = 0x00020000; | ||
| 513 | pub const CLONE_SYSVSEM = 0x00040000; | ||
| 514 | pub const CLONE_SETTLS = 0x00080000; | ||
| 515 | pub const CLONE_PARENT_SETTID = 0x00100000; | ||
| 516 | pub const CLONE_CHILD_CLEARTID = 0x00200000; | ||
| 517 | pub const CLONE_DETACHED = 0x00400000; | ||
| 518 | pub const CLONE_UNTRACED = 0x00800000; | ||
| 519 | pub const CLONE_CHILD_SETTID = 0x01000000; | ||
| 520 | pub const CLONE_NEWCGROUP = 0x02000000; | ||
| 521 | pub const CLONE_NEWUTS = 0x04000000; | ||
| 522 | pub const CLONE_NEWIPC = 0x08000000; | ||
| 523 | pub const CLONE_NEWUSER = 0x10000000; | ||
| 524 | pub const CLONE_NEWPID = 0x20000000; | ||
| 525 | pub const CLONE_NEWNET = 0x40000000; | ||
| 526 | pub const CLONE_IO = 0x80000000; | ||
| 527 | |||
| 528 | pub const EFD_SEMAPHORE = 1; | ||
| 529 | pub const EFD_CLOEXEC = O_CLOEXEC; | ||
| 530 | pub const EFD_NONBLOCK = O_NONBLOCK; | ||
| 531 | |||
| 532 | pub const MS_RDONLY = 1; | ||
| 533 | pub const MS_NOSUID = 2; | ||
| 534 | pub const MS_NODEV = 4; | ||
| 535 | pub const MS_NOEXEC = 8; | ||
| 536 | pub const MS_SYNCHRONOUS = 16; | ||
| 537 | pub const MS_REMOUNT = 32; | ||
| 538 | pub const MS_MANDLOCK = 64; | ||
| 539 | pub const MS_DIRSYNC = 128; | ||
| 540 | pub const MS_NOATIME = 1024; | ||
| 541 | pub const MS_NODIRATIME = 2048; | ||
| 542 | pub const MS_BIND = 4096; | ||
| 543 | pub const MS_MOVE = 8192; | ||
| 544 | pub const MS_REC = 16384; | ||
| 545 | pub const MS_SILENT = 32768; | ||
| 546 | pub const MS_POSIXACL = (1 << 16); | ||
| 547 | pub const MS_UNBINDABLE = (1 << 17); | ||
| 548 | pub const MS_PRIVATE = (1 << 18); | ||
| 549 | pub const MS_SLAVE = (1 << 19); | ||
| 550 | pub const MS_SHARED = (1 << 20); | ||
| 551 | pub const MS_RELATIME = (1 << 21); | ||
| 552 | pub const MS_KERNMOUNT = (1 << 22); | ||
| 553 | pub const MS_I_VERSION = (1 << 23); | ||
| 554 | pub const MS_STRICTATIME = (1 << 24); | ||
| 555 | pub const MS_LAZYTIME = (1 << 25); | ||
| 556 | pub const MS_NOREMOTELOCK = (1 << 27); | ||
| 557 | pub const MS_NOSEC = (1 << 28); | ||
| 558 | pub const MS_BORN = (1 << 29); | ||
| 559 | pub const MS_ACTIVE = (1 << 30); | ||
| 560 | pub const MS_NOUSER = (1 << 31); | ||
| 561 | |||
| 562 | pub const MS_RMT_MASK = (MS_RDONLY | MS_SYNCHRONOUS | MS_MANDLOCK | MS_I_VERSION | MS_LAZYTIME); | ||
| 563 | |||
| 564 | pub const MS_MGC_VAL = 0xc0ed0000; | ||
| 565 | pub const MS_MGC_MSK = 0xffff0000; | ||
| 566 | |||
| 567 | pub const MNT_FORCE = 1; | ||
| 568 | pub const MNT_DETACH = 2; | ||
| 569 | pub const MNT_EXPIRE = 4; | ||
| 570 | pub const UMOUNT_NOFOLLOW = 8; | ||
| 571 | |||
| 572 | pub const IN_CLOEXEC = O_CLOEXEC; | ||
| 573 | pub const IN_NONBLOCK = O_NONBLOCK; | ||
| 574 | |||
| 575 | pub const IN_ACCESS = 0x00000001; | ||
| 576 | pub const IN_MODIFY = 0x00000002; | ||
| 577 | pub const IN_ATTRIB = 0x00000004; | ||
| 578 | pub const IN_CLOSE_WRITE = 0x00000008; | ||
| 579 | pub const IN_CLOSE_NOWRITE = 0x00000010; | ||
| 580 | pub const IN_CLOSE = IN_CLOSE_WRITE | IN_CLOSE_NOWRITE; | ||
| 581 | pub const IN_OPEN = 0x00000020; | ||
| 582 | pub const IN_MOVED_FROM = 0x00000040; | ||
| 583 | pub const IN_MOVED_TO = 0x00000080; | ||
| 584 | pub const IN_MOVE = IN_MOVED_FROM | IN_MOVED_TO; | ||
| 585 | pub const IN_CREATE = 0x00000100; | ||
| 586 | pub const IN_DELETE = 0x00000200; | ||
| 587 | pub const IN_DELETE_SELF = 0x00000400; | ||
| 588 | pub const IN_MOVE_SELF = 0x00000800; | ||
| 589 | pub const IN_ALL_EVENTS = 0x00000fff; | ||
| 590 | |||
| 591 | pub const IN_UNMOUNT = 0x00002000; | ||
| 592 | pub const IN_Q_OVERFLOW = 0x00004000; | ||
| 593 | pub const IN_IGNORED = 0x00008000; | ||
| 594 | |||
| 595 | pub const IN_ONLYDIR = 0x01000000; | ||
| 596 | pub const IN_DONT_FOLLOW = 0x02000000; | ||
| 597 | pub const IN_EXCL_UNLINK = 0x04000000; | ||
| 598 | pub const IN_MASK_ADD = 0x20000000; | ||
| 599 | |||
| 600 | pub const IN_ISDIR = 0x40000000; | ||
| 601 | pub const IN_ONESHOT = 0x80000000; | ||
| 602 | |||
| 603 | pub const S_IFMT = 0o170000; | ||
| 604 | |||
| 605 | pub const S_IFDIR = 0o040000; | ||
| 606 | pub const S_IFCHR = 0o020000; | ||
| 607 | pub const S_IFBLK = 0o060000; | ||
| 608 | pub const S_IFREG = 0o100000; | ||
| 609 | pub const S_IFIFO = 0o010000; | ||
| 610 | pub const S_IFLNK = 0o120000; | ||
| 611 | pub const S_IFSOCK = 0o140000; | ||
| 612 | |||
| 613 | pub const S_ISUID = 0o4000; | ||
| 614 | pub const S_ISGID = 0o2000; | ||
| 615 | pub const S_ISVTX = 0o1000; | ||
| 616 | pub const S_IRUSR = 0o400; | ||
| 617 | pub const S_IWUSR = 0o200; | ||
| 618 | pub const S_IXUSR = 0o100; | ||
| 619 | pub const S_IRWXU = 0o700; | ||
| 620 | pub const S_IRGRP = 0o040; | ||
| 621 | pub const S_IWGRP = 0o020; | ||
| 622 | pub const S_IXGRP = 0o010; | ||
| 623 | pub const S_IRWXG = 0o070; | ||
| 624 | pub const S_IROTH = 0o004; | ||
| 625 | pub const S_IWOTH = 0o002; | ||
| 626 | pub const S_IXOTH = 0o001; | ||
| 627 | pub const S_IRWXO = 0o007; | ||
| 628 | |||
| 629 | pub fn S_ISREG(m: u32) bool { | ||
| 630 | return m & S_IFMT == S_IFREG; | ||
| 631 | } | ||
| 632 | |||
| 633 | pub fn S_ISDIR(m: u32) bool { | ||
| 634 | return m & S_IFMT == S_IFDIR; | ||
| 635 | } | ||
| 636 | |||
| 637 | pub fn S_ISCHR(m: u32) bool { | ||
| 638 | return m & S_IFMT == S_IFCHR; | ||
| 639 | } | ||
| 640 | |||
| 641 | pub fn S_ISBLK(m: u32) bool { | ||
| 642 | return m & S_IFMT == S_IFBLK; | ||
| 643 | } | ||
| 644 | |||
| 645 | pub fn S_ISFIFO(m: u32) bool { | ||
| 646 | return m & S_IFMT == S_IFIFO; | ||
| 647 | } | ||
| 648 | |||
| 649 | pub fn S_ISLNK(m: u32) bool { | ||
| 650 | return m & S_IFMT == S_IFLNK; | ||
| 651 | } | ||
| 652 | |||
| 653 | pub fn S_ISSOCK(m: u32) bool { | ||
| 654 | return m & S_IFMT == S_IFSOCK; | ||
| 655 | } | ||
| 656 | |||
| 657 | pub const TFD_NONBLOCK = O_NONBLOCK; | ||
| 658 | pub const TFD_CLOEXEC = O_CLOEXEC; | ||
| 659 | |||
| 660 | pub const TFD_TIMER_ABSTIME = 1; | ||
| 661 | pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1); | ||
| 662 | |||
| 663 | fn unsigned(s: i32) u32 { | ||
| 664 | return @bitCast(u32, s); | ||
| 665 | } | ||
| 666 | fn signed(s: u32) i32 { | ||
| 667 | return @bitCast(i32, s); | ||
| 668 | } | ||
| 669 | pub fn WEXITSTATUS(s: i32) i32 { | ||
| 670 | return signed((unsigned(s) & 0xff00) >> 8); | ||
| 671 | } | ||
| 672 | pub fn WTERMSIG(s: i32) i32 { | ||
| 673 | return signed(unsigned(s) & 0x7f); | ||
| 674 | } | ||
| 675 | pub fn WSTOPSIG(s: i32) i32 { | ||
| 676 | return WEXITSTATUS(s); | ||
| 677 | } | ||
| 678 | pub fn WIFEXITED(s: i32) bool { | ||
| 679 | return WTERMSIG(s) == 0; | ||
| 680 | } | ||
| 681 | pub fn WIFSTOPPED(s: i32) bool { | ||
| 682 | return @intCast(u16, ((unsigned(s) & 0xffff) *% 0x10001) >> 8) > 0x7f00; | ||
| 683 | } | ||
| 684 | pub fn WIFSIGNALED(s: i32) bool { | ||
| 685 | return (unsigned(s) & 0xffff) -% 1 < 0xff; | ||
| 686 | } | ||
| 687 | |||
| 688 | pub const winsize = extern struct { | ||
| 689 | ws_row: u16, | ||
| 690 | ws_col: u16, | ||
| 691 | ws_xpixel: u16, | ||
| 692 | ws_ypixel: u16, | ||
| 693 | }; | ||
| 694 | |||
| 695 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 696 | pub fn getErrno(r: usize) usize { | ||
| 697 | const signed_r = @bitCast(isize, r); | ||
| 698 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 699 | } | ||
| 700 | |||
| 701 | pub fn dup2(old: i32, new: i32) usize { | ||
| 702 | return dup3(old, new, 0); | ||
| 703 | } | ||
| 704 | |||
| 705 | pub fn dup3(old: i32, new: i32, flags: u32) usize { | ||
| 706 | return syscall3(SYS_dup3, @bitCast(usize, isize(old)), @bitCast(usize, isize(new)), flags); | ||
| 707 | } | ||
| 708 | |||
| 709 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 710 | pub fn chdir(path: [*]const u8) usize { | ||
| 711 | return syscall1(SYS_chdir, @ptrToInt(path)); | ||
| 712 | } | ||
| 713 | |||
| 714 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 715 | pub fn chroot(path: [*]const u8) usize { | ||
| 716 | return syscall1(SYS_chroot, @ptrToInt(path)); | ||
| 717 | } | ||
| 718 | |||
| 719 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 720 | pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { | ||
| 721 | return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp)); | ||
| 722 | } | ||
| 723 | |||
| 724 | pub fn fork() usize { | ||
| 725 | return clone2(SIGCHLD, 0); | ||
| 726 | } | ||
| 727 | |||
| 728 | /// This must be inline, and inline call the syscall function, because if the | ||
| 729 | /// child does a return it will clobber the parent's stack. | ||
| 730 | /// It is advised to avoid this function and use clone instead, because | ||
| 731 | /// the compiler is not aware of how vfork affects control flow and you may | ||
| 732 | /// see different results in optimized builds. | ||
| 733 | pub inline fn vfork() usize { | ||
| 734 | return @inlineCall(syscall0, SYS_vfork); | ||
| 735 | } | ||
| 736 | |||
| 737 | pub fn futex_wait(uaddr: *const i32, futex_op: u32, val: i32, timeout: ?*timespec) usize { | ||
| 738 | return syscall4(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val), @ptrToInt(timeout)); | ||
| 739 | } | ||
| 740 | |||
| 741 | pub fn futex_wake(uaddr: *const i32, futex_op: u32, val: i32) usize { | ||
| 742 | return syscall3(SYS_futex, @ptrToInt(uaddr), futex_op, @bitCast(u32, val)); | ||
| 743 | } | ||
| 744 | |||
| 745 | pub fn getcwd(buf: [*]u8, size: usize) usize { | ||
| 746 | return syscall2(SYS_getcwd, @ptrToInt(buf), size); | ||
| 747 | } | ||
| 748 | |||
| 749 | pub fn getdents64(fd: i32, dirp: [*]u8, count: usize) usize { | ||
| 750 | return syscall3(SYS_getdents64, @bitCast(usize, isize(fd)), @ptrToInt(dirp), count); | ||
| 751 | } | ||
| 752 | |||
| 753 | pub fn inotify_init1(flags: u32) usize { | ||
| 754 | return syscall1(SYS_inotify_init1, flags); | ||
| 755 | } | ||
| 756 | |||
| 757 | pub fn inotify_add_watch(fd: i32, pathname: [*]const u8, mask: u32) usize { | ||
| 758 | return syscall3(SYS_inotify_add_watch, @bitCast(usize, isize(fd)), @ptrToInt(pathname), mask); | ||
| 759 | } | ||
| 760 | |||
| 761 | pub fn inotify_rm_watch(fd: i32, wd: i32) usize { | ||
| 762 | return syscall2(SYS_inotify_rm_watch, @bitCast(usize, isize(fd)), @bitCast(usize, isize(wd))); | ||
| 763 | } | ||
| 764 | |||
| 765 | pub fn isatty(fd: i32) bool { | ||
| 766 | var wsz: winsize = undefined; | ||
| 767 | return syscall3(SYS_ioctl, @bitCast(usize, isize(fd)), TIOCGWINSZ, @ptrToInt(&wsz)) == 0; | ||
| 768 | } | ||
| 769 | |||
| 770 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 771 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 772 | return readlinkat(AT_FDCWD, path, buf_ptr, buf_len); | ||
| 773 | } | ||
| 774 | |||
| 775 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 776 | pub fn readlinkat(dirfd: i32, noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 777 | return syscall4(SYS_readlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(buf_ptr), buf_len); | ||
| 778 | } | ||
| 779 | |||
| 780 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 781 | pub fn mkdir(path: [*]const u8, mode: u32) usize { | ||
| 782 | return mkdirat(AT_FDCWD, path, mode); | ||
| 783 | } | ||
| 784 | |||
| 785 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 786 | pub fn mkdirat(dirfd: i32, path: [*]const u8, mode: u32) usize { | ||
| 787 | return syscall3(SYS_mkdirat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); | ||
| 788 | } | ||
| 789 | |||
| 790 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 791 | pub fn mount(special: [*]const u8, dir: [*]const u8, fstype: [*]const u8, flags: u32, data: usize) usize { | ||
| 792 | return syscall5(SYS_mount, @ptrToInt(special), @ptrToInt(dir), @ptrToInt(fstype), flags, data); | ||
| 793 | } | ||
| 794 | |||
| 795 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 796 | pub fn umount(special: [*]const u8) usize { | ||
| 797 | return syscall2(SYS_umount2, @ptrToInt(special), 0); | ||
| 798 | } | ||
| 799 | |||
| 800 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 801 | pub fn umount2(special: [*]const u8, flags: u32) usize { | ||
| 802 | return syscall2(SYS_umount2, @ptrToInt(special), flags); | ||
| 803 | } | ||
| 804 | |||
| 805 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | ||
| 806 | return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, @bitCast(usize, isize(fd)), @bitCast(usize, offset)); | ||
| 807 | } | ||
| 808 | |||
| 809 | pub fn mprotect(address: usize, length: usize, protection: usize) usize { | ||
| 810 | return syscall3(SYS_mprotect, address, length, protection); | ||
| 811 | } | ||
| 812 | |||
| 813 | pub fn munmap(address: usize, length: usize) usize { | ||
| 814 | return syscall2(SYS_munmap, address, length); | ||
| 815 | } | ||
| 816 | |||
| 817 | pub fn read(fd: i32, buf: [*]u8, count: usize) usize { | ||
| 818 | return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | ||
| 819 | } | ||
| 820 | |||
| 821 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { | ||
| 822 | return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); | ||
| 823 | } | ||
| 824 | |||
| 825 | pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { | ||
| 826 | return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); | ||
| 827 | } | ||
| 828 | |||
| 829 | pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { | ||
| 830 | return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); | ||
| 831 | } | ||
| 832 | |||
| 833 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { | ||
| 834 | return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); | ||
| 835 | } | ||
| 836 | |||
| 837 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 838 | pub fn rmdir(path: [*]const u8) usize { | ||
| 839 | return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); | ||
| 840 | } | ||
| 841 | |||
| 842 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 843 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | ||
| 844 | return symlinkat(existing, AT_FDCWD, new); | ||
| 845 | } | ||
| 846 | |||
| 847 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 848 | pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { | ||
| 849 | return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); | ||
| 850 | } | ||
| 851 | |||
| 852 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 853 | pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { | ||
| 854 | return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | ||
| 855 | } | ||
| 856 | |||
| 857 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 858 | pub fn access(path: [*]const u8, mode: u32) usize { | ||
| 859 | return faccessat(AT_FDCWD, path, mode); | ||
| 860 | } | ||
| 861 | |||
| 862 | pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { | ||
| 863 | return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); | ||
| 864 | } | ||
| 865 | |||
| 866 | pub fn pipe(fd: *[2]i32) usize { | ||
| 867 | return pipe2(fd, 0); | ||
| 868 | } | ||
| 869 | |||
| 870 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | ||
| 871 | return syscall2(SYS_pipe2, @ptrToInt(fd), flags); | ||
| 872 | } | ||
| 873 | |||
| 874 | pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { | ||
| 875 | return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | ||
| 876 | } | ||
| 877 | |||
| 878 | pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { | ||
| 879 | return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | ||
| 880 | } | ||
| 881 | |||
| 882 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 883 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | ||
| 884 | return renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); | ||
| 885 | } | ||
| 886 | |||
| 887 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 888 | pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { | ||
| 889 | return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); | ||
| 890 | } | ||
| 891 | |||
| 892 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 893 | pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { | ||
| 894 | return openat(AT_FDCWD, path, flags, perm); | ||
| 895 | } | ||
| 896 | |||
| 897 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 898 | pub fn create(path: [*]const u8, perm: usize) usize { | ||
| 899 | return syscall2(SYS_creat, @ptrToInt(path), perm); | ||
| 900 | } | ||
| 901 | |||
| 902 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 903 | pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { | ||
| 904 | // dirfd could be negative, for example AT_FDCWD is -100 | ||
| 905 | return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); | ||
| 906 | } | ||
| 907 | |||
| 908 | /// See also `clone` (from the arch-specific include) | ||
| 909 | pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { | ||
| 910 | return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); | ||
| 911 | } | ||
| 912 | |||
| 913 | /// See also `clone` (from the arch-specific include) | ||
| 914 | pub fn clone2(flags: u32, child_stack_ptr: usize) usize { | ||
| 915 | return syscall2(SYS_clone, flags, child_stack_ptr); | ||
| 916 | } | ||
| 917 | |||
| 918 | pub fn close(fd: i32) usize { | ||
| 919 | return syscall1(SYS_close, @bitCast(usize, isize(fd))); | ||
| 920 | } | ||
| 921 | |||
| 922 | pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { | ||
| 923 | return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); | ||
| 924 | } | ||
| 925 | |||
| 926 | pub fn exit(status: i32) noreturn { | ||
| 927 | _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); | ||
| 928 | unreachable; | ||
| 929 | } | ||
| 930 | |||
| 931 | pub fn exit_group(status: i32) noreturn { | ||
| 932 | _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); | ||
| 933 | unreachable; | ||
| 934 | } | ||
| 935 | |||
| 936 | pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { | ||
| 937 | return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); | ||
| 938 | } | ||
| 939 | |||
| 940 | pub fn kill(pid: i32, sig: i32) usize { | ||
| 941 | return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); | ||
| 942 | } | ||
| 943 | |||
| 944 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 945 | pub fn unlink(path: [*]const u8) usize { | ||
| 946 | return unlinkat(AT_FDCWD, path, 0); | ||
| 947 | } | ||
| 948 | |||
| 949 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 950 | pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { | ||
| 951 | return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); | ||
| 952 | } | ||
| 953 | |||
| 954 | pub fn waitpid(pid: i32, status: *i32, options: i32) usize { | ||
| 955 | return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); | ||
| 956 | } | ||
| 957 | |||
| 958 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | ||
| 959 | if (VDSO_CGT_SYM.len != 0) { | ||
| 960 | const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); | ||
| 961 | if (@ptrToInt(f) != 0) { | ||
| 962 | const rc = f(clk_id, tp); | ||
| 963 | switch (rc) { | ||
| 964 | 0, @bitCast(usize, isize(-EINVAL)) => return rc, | ||
| 965 | else => {}, | ||
| 966 | } | ||
| 967 | } | ||
| 968 | } | ||
| 969 | return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 970 | } | ||
| 971 | var vdso_clock_gettime = init_vdso_clock_gettime; | ||
| 972 | extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { | ||
| 973 | const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); | ||
| 974 | var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); | ||
| 975 | _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); | ||
| 976 | if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); | ||
| 977 | return f(clk, ts); | ||
| 978 | } | ||
| 979 | |||
| 980 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | ||
| 981 | return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 982 | } | ||
| 983 | |||
| 984 | pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { | ||
| 985 | return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | ||
| 986 | } | ||
| 987 | |||
| 988 | pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { | ||
| 989 | return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); | ||
| 990 | } | ||
| 991 | |||
| 992 | pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { | ||
| 993 | return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); | ||
| 994 | } | ||
| 995 | |||
| 996 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | ||
| 997 | return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); | ||
| 998 | } | ||
| 999 | |||
| 1000 | pub fn setuid(uid: u32) usize { | ||
| 1001 | return syscall1(SYS_setuid, uid); | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | pub fn setgid(gid: u32) usize { | ||
| 1005 | return syscall1(SYS_setgid, gid); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | pub fn setreuid(ruid: u32, euid: u32) usize { | ||
| 1009 | return syscall2(SYS_setreuid, ruid, euid); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | pub fn setregid(rgid: u32, egid: u32) usize { | ||
| 1013 | return syscall2(SYS_setregid, rgid, egid); | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | pub fn getuid() u32 { | ||
| 1017 | return u32(syscall0(SYS_getuid)); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | pub fn getgid() u32 { | ||
| 1021 | return u32(syscall0(SYS_getgid)); | ||
| 1022 | } | ||
| 1023 | |||
| 1024 | pub fn geteuid() u32 { | ||
| 1025 | return u32(syscall0(SYS_geteuid)); | ||
| 1026 | } | ||
| 1027 | |||
| 1028 | pub fn getegid() u32 { | ||
| 1029 | return u32(syscall0(SYS_getegid)); | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | pub fn seteuid(euid: u32) usize { | ||
| 1033 | return syscall1(SYS_seteuid, euid); | ||
| 1034 | } | ||
| 1035 | |||
| 1036 | pub fn setegid(egid: u32) usize { | ||
| 1037 | return syscall1(SYS_setegid, egid); | ||
| 1038 | } | ||
| 1039 | |||
| 1040 | pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { | ||
| 1041 | return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); | ||
| 1042 | } | ||
| 1043 | |||
| 1044 | pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { | ||
| 1045 | return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); | ||
| 1046 | } | ||
| 1047 | |||
| 1048 | pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { | ||
| 1049 | return syscall3(SYS_setresuid, ruid, euid, suid); | ||
| 1050 | } | ||
| 1051 | |||
| 1052 | pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { | ||
| 1053 | return syscall3(SYS_setresgid, rgid, egid, sgid); | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | pub fn getgroups(size: usize, list: *u32) usize { | ||
| 1057 | return syscall2(SYS_getgroups, size, @ptrToInt(list)); | ||
| 1058 | } | ||
| 1059 | |||
| 1060 | pub fn setgroups(size: usize, list: *const u32) usize { | ||
| 1061 | return syscall2(SYS_setgroups, size, @ptrToInt(list)); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | pub fn getpid() i32 { | ||
| 1065 | return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); | ||
| 1066 | } | ||
| 1067 | |||
| 1068 | pub fn gettid() i32 { | ||
| 1069 | return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); | ||
| 1070 | } | ||
| 1071 | |||
| 1072 | pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { | ||
| 1073 | return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); | ||
| 1074 | } | ||
| 1075 | |||
| 1076 | pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { | ||
| 1077 | assert(sig >= 1); | ||
| 1078 | assert(sig != SIGKILL); | ||
| 1079 | assert(sig != SIGSTOP); | ||
| 1080 | var ksa = k_sigaction{ | ||
| 1081 | .handler = act.handler, | ||
| 1082 | .flags = act.flags | SA_RESTORER, | ||
| 1083 | .mask = undefined, | ||
| 1084 | .restorer = @ptrCast(extern fn () void, restore_rt), | ||
| 1085 | }; | ||
| 1086 | var ksa_old: k_sigaction = undefined; | ||
| 1087 | @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); | ||
| 1088 | const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); | ||
| 1089 | const err = getErrno(result); | ||
| 1090 | if (err != 0) { | ||
| 1091 | return result; | ||
| 1092 | } | ||
| 1093 | if (oact) |old| { | ||
| 1094 | old.handler = ksa_old.handler; | ||
| 1095 | old.flags = @truncate(u32, ksa_old.flags); | ||
| 1096 | @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); | ||
| 1097 | } | ||
| 1098 | return 0; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | const NSIG = 65; | ||
| 1102 | const sigset_t = [128 / @sizeOf(usize)]usize; | ||
| 1103 | const all_mask = []usize{maxInt(usize)}; | ||
| 1104 | const app_mask = []usize{0xfffffffc7fffffff}; | ||
| 1105 | |||
| 1106 | const k_sigaction = extern struct { | ||
| 1107 | handler: extern fn (i32) void, | ||
| 1108 | flags: usize, | ||
| 1109 | restorer: extern fn () void, | ||
| 1110 | mask: [2]u32, | ||
| 1111 | }; | ||
| 1112 | |||
| 1113 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | ||
| 1114 | pub const Sigaction = struct { | ||
| 1115 | handler: extern fn (i32) void, | ||
| 1116 | mask: sigset_t, | ||
| 1117 | flags: u32, | ||
| 1118 | }; | ||
| 1119 | |||
| 1120 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | ||
| 1121 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ||
| 1122 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | ||
| 1123 | pub const empty_sigset = []usize{0} ** sigset_t.len; | ||
| 1124 | |||
| 1125 | pub fn raise(sig: i32) usize { | ||
| 1126 | var set: sigset_t = undefined; | ||
| 1127 | blockAppSignals(&set); | ||
| 1128 | const tid = syscall0(SYS_gettid); | ||
| 1129 | const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); | ||
| 1130 | restoreSignals(&set); | ||
| 1131 | return ret; | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | fn blockAllSignals(set: *sigset_t) void { | ||
| 1135 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | fn blockAppSignals(set: *sigset_t) void { | ||
| 1139 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); | ||
| 1140 | } | ||
| 1141 | |||
| 1142 | fn restoreSignals(set: *sigset_t) void { | ||
| 1143 | _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); | ||
| 1144 | } | ||
| 1145 | |||
| 1146 | pub fn sigaddset(set: *sigset_t, sig: u6) void { | ||
| 1147 | const s = sig - 1; | ||
| 1148 | (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | pub fn sigismember(set: *const sigset_t, sig: u6) bool { | ||
| 1152 | const s = sig - 1; | ||
| 1153 | return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; | ||
| 1154 | } | ||
| 1155 | |||
| 1156 | pub const in_port_t = u16; | ||
| 1157 | pub const sa_family_t = u16; | ||
| 1158 | pub const socklen_t = u32; | ||
| 1159 | |||
| 1160 | /// This intentionally only has ip4 and ip6 | ||
| 1161 | pub const sockaddr = extern union { | ||
| 1162 | in: sockaddr_in, | ||
| 1163 | in6: sockaddr_in6, | ||
| 1164 | }; | ||
| 1165 | |||
| 1166 | pub const sockaddr_in = extern struct { | ||
| 1167 | family: sa_family_t, | ||
| 1168 | port: in_port_t, | ||
| 1169 | addr: u32, | ||
| 1170 | zero: [8]u8, | ||
| 1171 | }; | ||
| 1172 | |||
| 1173 | pub const sockaddr_in6 = extern struct { | ||
| 1174 | family: sa_family_t, | ||
| 1175 | port: in_port_t, | ||
| 1176 | flowinfo: u32, | ||
| 1177 | addr: [16]u8, | ||
| 1178 | scope_id: u32, | ||
| 1179 | }; | ||
| 1180 | |||
| 1181 | pub const sockaddr_un = extern struct { | ||
| 1182 | family: sa_family_t, | ||
| 1183 | path: [108]u8, | ||
| 1184 | }; | ||
| 1185 | |||
| 1186 | pub const iovec = extern struct { | ||
| 1187 | iov_base: [*]u8, | ||
| 1188 | iov_len: usize, | ||
| 1189 | }; | ||
| 1190 | |||
| 1191 | pub const iovec_const = extern struct { | ||
| 1192 | iov_base: [*]const u8, | ||
| 1193 | iov_len: usize, | ||
| 1194 | }; | ||
| 1195 | |||
| 1196 | pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | ||
| 1197 | return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); | ||
| 1198 | } | ||
| 1199 | |||
| 1200 | pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | ||
| 1201 | return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); | ||
| 1202 | } | ||
| 1203 | |||
| 1204 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { | ||
| 1205 | return syscall3(SYS_socket, domain, socket_type, protocol); | ||
| 1206 | } | ||
| 1207 | |||
| 1208 | pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { | ||
| 1209 | return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); | ||
| 1210 | } | ||
| 1211 | |||
| 1212 | pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { | ||
| 1213 | return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); | ||
| 1214 | } | ||
| 1215 | |||
| 1216 | pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { | ||
| 1217 | return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); | ||
| 1218 | } | ||
| 1219 | |||
| 1220 | pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { | ||
| 1221 | return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); | ||
| 1222 | } | ||
| 1223 | |||
| 1224 | pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { | ||
| 1225 | return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); | ||
| 1226 | } | ||
| 1227 | |||
| 1228 | pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { | ||
| 1229 | return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | pub fn shutdown(fd: i32, how: i32) usize { | ||
| 1233 | return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { | ||
| 1237 | return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | pub fn listen(fd: i32, backlog: u32) usize { | ||
| 1241 | return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { | ||
| 1245 | return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { | ||
| 1249 | return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); | ||
| 1250 | } | ||
| 1251 | |||
| 1252 | pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | ||
| 1253 | return accept4(fd, addr, len, 0); | ||
| 1254 | } | ||
| 1255 | |||
| 1256 | pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { | ||
| 1257 | return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | pub fn fstat(fd: i32, stat_buf: *Stat) usize { | ||
| 1261 | return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); | ||
| 1262 | } | ||
| 1263 | |||
| 1264 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1265 | pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { | ||
| 1266 | return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT); | ||
| 1267 | } | ||
| 1268 | |||
| 1269 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1270 | pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { | ||
| 1271 | return fstatat(AF_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1275 | pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { | ||
| 1276 | return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); | ||
| 1277 | } | ||
| 1278 | |||
| 1279 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1280 | pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { | ||
| 1281 | return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); | ||
| 1282 | } | ||
| 1283 | |||
| 1284 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1285 | pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { | ||
| 1286 | return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); | ||
| 1287 | } | ||
| 1288 | |||
| 1289 | pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { | ||
| 1290 | return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1294 | pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { | ||
| 1295 | return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); | ||
| 1296 | } | ||
| 1297 | |||
| 1298 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1299 | pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { | ||
| 1300 | return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); | ||
| 1301 | } | ||
| 1302 | |||
| 1303 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1304 | pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { | ||
| 1305 | return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1309 | pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | ||
| 1310 | return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1314 | pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | ||
| 1315 | return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1319 | pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | ||
| 1320 | return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); | ||
| 1321 | } | ||
| 1322 | |||
| 1323 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1324 | pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { | ||
| 1325 | return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); | ||
| 1326 | } | ||
| 1327 | |||
| 1328 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1329 | pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { | ||
| 1330 | return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); | ||
| 1331 | } | ||
| 1332 | |||
| 1333 | // TODO https://github.com/ziglang/zig/issues/265 | ||
| 1334 | pub fn fremovexattr(fd: usize, name: [*]const u8) usize { | ||
| 1335 | return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); | ||
| 1336 | } | ||
| 1337 | |||
| 1338 | pub fn sched_getaffinity(pid: i32, set: []usize) usize { | ||
| 1339 | return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | pub const epoll_data = packed union { | ||
| 1343 | ptr: usize, | ||
| 1344 | fd: i32, | ||
| 1345 | @"u32": u32, | ||
| 1346 | @"u64": u64, | ||
| 1347 | }; | ||
| 1348 | |||
| 1349 | pub const epoll_event = packed struct { | ||
| 1350 | events: u32, | ||
| 1351 | data: epoll_data, | ||
| 1352 | }; | ||
| 1353 | |||
| 1354 | pub fn epoll_create() usize { | ||
| 1355 | return epoll_create1(0); | ||
| 1356 | } | ||
| 1357 | |||
| 1358 | pub fn epoll_create1(flags: usize) usize { | ||
| 1359 | return syscall1(SYS_epoll_create1, flags); | ||
| 1360 | } | ||
| 1361 | |||
| 1362 | pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { | ||
| 1363 | return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); | ||
| 1364 | } | ||
| 1365 | |||
| 1366 | pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { | ||
| 1367 | return epoll_pwait(epoll_fd, events, maxevents, timeout, null); | ||
| 1368 | } | ||
| 1369 | |||
| 1370 | pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { | ||
| 1371 | return syscall6( | ||
| 1372 | SYS_epoll_pwait, | ||
| 1373 | @bitCast(usize, isize(epoll_fd)), | ||
| 1374 | @ptrToInt(events), | ||
| 1375 | @intCast(usize, maxevents), | ||
| 1376 | @bitCast(usize, isize(timeout)), | ||
| 1377 | @ptrToInt(sigmask), | ||
| 1378 | @sizeOf(sigset_t), | ||
| 1379 | ); | ||
| 1380 | } | ||
| 1381 | |||
| 1382 | pub fn eventfd(count: u32, flags: u32) usize { | ||
| 1383 | return syscall2(SYS_eventfd2, count, flags); | ||
| 1384 | } | ||
| 1385 | |||
| 1386 | pub fn timerfd_create(clockid: i32, flags: u32) usize { | ||
| 1387 | return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); | ||
| 1388 | } | ||
| 1389 | |||
| 1390 | pub const itimerspec = extern struct { | ||
| 1391 | it_interval: timespec, | ||
| 1392 | it_value: timespec, | ||
| 1393 | }; | ||
| 1394 | |||
| 1395 | pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { | ||
| 1396 | return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); | ||
| 1397 | } | ||
| 1398 | |||
| 1399 | pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { | ||
| 1400 | return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; | ||
| 1404 | pub const _LINUX_CAPABILITY_U32S_1 = 1; | ||
| 1405 | |||
| 1406 | pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; | ||
| 1407 | pub const _LINUX_CAPABILITY_U32S_2 = 2; | ||
| 1408 | |||
| 1409 | pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; | ||
| 1410 | pub const _LINUX_CAPABILITY_U32S_3 = 2; | ||
| 1411 | |||
| 1412 | pub const VFS_CAP_REVISION_MASK = 0xFF000000; | ||
| 1413 | pub const VFS_CAP_REVISION_SHIFT = 24; | ||
| 1414 | pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; | ||
| 1415 | pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; | ||
| 1416 | |||
| 1417 | pub const VFS_CAP_REVISION_1 = 0x01000000; | ||
| 1418 | pub const VFS_CAP_U32_1 = 1; | ||
| 1419 | pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); | ||
| 1420 | |||
| 1421 | pub const VFS_CAP_REVISION_2 = 0x02000000; | ||
| 1422 | pub const VFS_CAP_U32_2 = 2; | ||
| 1423 | pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); | ||
| 1424 | |||
| 1425 | pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; | ||
| 1426 | pub const VFS_CAP_U32 = VFS_CAP_U32_2; | ||
| 1427 | pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; | ||
| 1428 | |||
| 1429 | pub const vfs_cap_data = extern struct { | ||
| 1430 | //all of these are mandated as little endian | ||
| 1431 | //when on disk. | ||
| 1432 | const Data = struct { | ||
| 1433 | permitted: u32, | ||
| 1434 | inheritable: u32, | ||
| 1435 | }; | ||
| 1436 | |||
| 1437 | magic_etc: u32, | ||
| 1438 | data: [VFS_CAP_U32]Data, | ||
| 1439 | }; | ||
| 1440 | |||
| 1441 | pub const CAP_CHOWN = 0; | ||
| 1442 | pub const CAP_DAC_OVERRIDE = 1; | ||
| 1443 | pub const CAP_DAC_READ_SEARCH = 2; | ||
| 1444 | pub const CAP_FOWNER = 3; | ||
| 1445 | pub const CAP_FSETID = 4; | ||
| 1446 | pub const CAP_KILL = 5; | ||
| 1447 | pub const CAP_SETGID = 6; | ||
| 1448 | pub const CAP_SETUID = 7; | ||
| 1449 | pub const CAP_SETPCAP = 8; | ||
| 1450 | pub const CAP_LINUX_IMMUTABLE = 9; | ||
| 1451 | pub const CAP_NET_BIND_SERVICE = 10; | ||
| 1452 | pub const CAP_NET_BROADCAST = 11; | ||
| 1453 | pub const CAP_NET_ADMIN = 12; | ||
| 1454 | pub const CAP_NET_RAW = 13; | ||
| 1455 | pub const CAP_IPC_LOCK = 14; | ||
| 1456 | pub const CAP_IPC_OWNER = 15; | ||
| 1457 | pub const CAP_SYS_MODULE = 16; | ||
| 1458 | pub const CAP_SYS_RAWIO = 17; | ||
| 1459 | pub const CAP_SYS_CHROOT = 18; | ||
| 1460 | pub const CAP_SYS_PTRACE = 19; | ||
| 1461 | pub const CAP_SYS_PACCT = 20; | ||
| 1462 | pub const CAP_SYS_ADMIN = 21; | ||
| 1463 | pub const CAP_SYS_BOOT = 22; | ||
| 1464 | pub const CAP_SYS_NICE = 23; | ||
| 1465 | pub const CAP_SYS_RESOURCE = 24; | ||
| 1466 | pub const CAP_SYS_TIME = 25; | ||
| 1467 | pub const CAP_SYS_TTY_CONFIG = 26; | ||
| 1468 | pub const CAP_MKNOD = 27; | ||
| 1469 | pub const CAP_LEASE = 28; | ||
| 1470 | pub const CAP_AUDIT_WRITE = 29; | ||
| 1471 | pub const CAP_AUDIT_CONTROL = 30; | ||
| 1472 | pub const CAP_SETFCAP = 31; | ||
| 1473 | pub const CAP_MAC_OVERRIDE = 32; | ||
| 1474 | pub const CAP_MAC_ADMIN = 33; | ||
| 1475 | pub const CAP_SYSLOG = 34; | ||
| 1476 | pub const CAP_WAKE_ALARM = 35; | ||
| 1477 | pub const CAP_BLOCK_SUSPEND = 36; | ||
| 1478 | pub const CAP_AUDIT_READ = 37; | ||
| 1479 | pub const CAP_LAST_CAP = CAP_AUDIT_READ; | ||
| 1480 | |||
| 1481 | pub fn cap_valid(u8: x) bool { | ||
| 1482 | return x >= 0 and x <= CAP_LAST_CAP; | ||
| 1483 | } | ||
| 1484 | |||
| 1485 | pub fn CAP_TO_MASK(cap: u8) u32 { | ||
| 1486 | return u32(1) << u5(cap & 31); | ||
| 1487 | } | ||
| 1488 | |||
| 1489 | pub fn CAP_TO_INDEX(cap: u8) u8 { | ||
| 1490 | return cap >> 5; | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | pub const cap_t = extern struct { | ||
| 1494 | hdrp: *cap_user_header_t, | ||
| 1495 | datap: *cap_user_data_t, | ||
| 1496 | }; | ||
| 1497 | |||
| 1498 | pub const cap_user_header_t = extern struct { | ||
| 1499 | version: u32, | ||
| 1500 | pid: usize, | ||
| 1501 | }; | ||
| 1502 | |||
| 1503 | pub const cap_user_data_t = extern struct { | ||
| 1504 | effective: u32, | ||
| 1505 | permitted: u32, | ||
| 1506 | inheritable: u32, | ||
| 1507 | }; | ||
| 1508 | |||
| 1509 | pub fn unshare(flags: usize) usize { | ||
| 1510 | return syscall1(SYS_unshare, flags); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { | ||
| 1514 | return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); | ||
| 1515 | } | ||
| 1516 | |||
| 1517 | pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { | ||
| 1518 | return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); | ||
| 1519 | } | ||
| 1520 | |||
| 1521 | pub const inotify_event = extern struct { | ||
| 1522 | wd: i32, | ||
| 1523 | mask: u32, | ||
| 1524 | cookie: u32, | ||
| 1525 | len: u32, | ||
| 1526 | //name: [?]u8, | ||
| 1527 | }; | ||
| 1528 | |||
| 1529 | pub const dirent64 = extern struct { | ||
| 1530 | d_ino: u64, | ||
| 1531 | d_off: u64, | ||
| 1532 | d_reclen: u16, | ||
| 1533 | d_type: u8, | ||
| 1534 | d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 | ||
| 1535 | }; | ||
| 1536 | |||
| 1537 | test "import" { | ||
| 1538 | if (builtin.os == builtin.Os.linux) { | ||
| 1539 | _ = @import("test.zig"); | ||
| 1540 | } | ||
| 1541 | } | ||
std/os/linux/test.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const linux = std.os.linux; | 3 | const linux = std.os.linux; |
| 4 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
std/os/linux/vdso.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const elf = std.elf; | 2 | const elf = std.elf; |
| 3 | const linux = std.os.linux; | 3 | const linux = std.os.linux; |
| 4 | const cstr = std.cstr; | 4 | const cstr = std.cstr; |
std/os/linux/x86_64.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const linux = std.os.linux; | 2 | const linux = std.os.linux; |
| 3 | const socklen_t = linux.socklen_t; | 3 | const socklen_t = linux.socklen_t; |
| 4 | const iovec = linux.iovec; | 4 | const iovec = linux.iovec; |
std/os/netbsd.zig created+724| ... | @@ -0,0 +1,724 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | pub use @import("netbsd/errno.zig"); | ||
| 4 | |||
| 5 | const std = @import("../std.zig"); | ||
| 6 | const c = std.c; | ||
| 7 | |||
| 8 | const assert = std.debug.assert; | ||
| 9 | const maxInt = std.math.maxInt; | ||
| 10 | pub const Kevent = c.Kevent; | ||
| 11 | |||
| 12 | pub const CTL_KERN = 1; | ||
| 13 | pub const CTL_DEBUG = 5; | ||
| 14 | |||
| 15 | pub const KERN_PROC_ARGS = 48; // struct: process argv/env | ||
| 16 | pub const KERN_PROC_PATHNAME = 5; // path to executable | ||
| 17 | |||
| 18 | pub const PATH_MAX = 1024; | ||
| 19 | |||
| 20 | pub const STDIN_FILENO = 0; | ||
| 21 | pub const STDOUT_FILENO = 1; | ||
| 22 | pub const STDERR_FILENO = 2; | ||
| 23 | |||
| 24 | pub const PROT_NONE = 0; | ||
| 25 | pub const PROT_READ = 1; | ||
| 26 | pub const PROT_WRITE = 2; | ||
| 27 | pub const PROT_EXEC = 4; | ||
| 28 | |||
| 29 | pub const CLOCK_REALTIME = 0; | ||
| 30 | pub const CLOCK_VIRTUAL = 1; | ||
| 31 | pub const CLOCK_PROF = 2; | ||
| 32 | pub const CLOCK_MONOTONIC = 3; | ||
| 33 | pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; | ||
| 34 | pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; | ||
| 35 | |||
| 36 | pub const MAP_FAILED = maxInt(usize); | ||
| 37 | pub const MAP_SHARED = 0x0001; | ||
| 38 | pub const MAP_PRIVATE = 0x0002; | ||
| 39 | pub const MAP_REMAPDUP = 0x0004; | ||
| 40 | pub const MAP_FIXED = 0x0010; | ||
| 41 | pub const MAP_RENAME = 0x0020; | ||
| 42 | pub const MAP_NORESERVE = 0x0040; | ||
| 43 | pub const MAP_INHERIT = 0x0080; | ||
| 44 | pub const MAP_HASSEMAPHORE = 0x0200; | ||
| 45 | pub const MAP_TRYFIXED = 0x0400; | ||
| 46 | pub const MAP_WIRED = 0x0800; | ||
| 47 | |||
| 48 | pub const MAP_FILE = 0x0000; | ||
| 49 | pub const MAP_NOSYNC = 0x0800; | ||
| 50 | pub const MAP_ANON = 0x1000; | ||
| 51 | pub const MAP_ANONYMOUS = MAP_ANON; | ||
| 52 | pub const MAP_STACK = 0x2000; | ||
| 53 | |||
| 54 | pub const WNOHANG = 0x00000001; | ||
| 55 | pub const WUNTRACED = 0x00000002; | ||
| 56 | pub const WSTOPPED = WUNTRACED; | ||
| 57 | pub const WCONTINUED = 0x00000010; | ||
| 58 | pub const WNOWAIT = 0x00010000; | ||
| 59 | pub const WEXITED = 0x00000020; | ||
| 60 | pub const WTRAPPED = 0x00000040; | ||
| 61 | |||
| 62 | pub const SA_ONSTACK = 0x0001; | ||
| 63 | pub const SA_RESTART = 0x0002; | ||
| 64 | pub const SA_RESETHAND = 0x0004; | ||
| 65 | pub const SA_NOCLDSTOP = 0x0008; | ||
| 66 | pub const SA_NODEFER = 0x0010; | ||
| 67 | pub const SA_NOCLDWAIT = 0x0020; | ||
| 68 | pub const SA_SIGINFO = 0x0040; | ||
| 69 | |||
| 70 | pub const SIGHUP = 1; | ||
| 71 | pub const SIGINT = 2; | ||
| 72 | pub const SIGQUIT = 3; | ||
| 73 | pub const SIGILL = 4; | ||
| 74 | pub const SIGTRAP = 5; | ||
| 75 | pub const SIGABRT = 6; | ||
| 76 | pub const SIGIOT = SIGABRT; | ||
| 77 | pub const SIGEMT = 7; | ||
| 78 | pub const SIGFPE = 8; | ||
| 79 | pub const SIGKILL = 9; | ||
| 80 | pub const SIGBUS = 10; | ||
| 81 | pub const SIGSEGV = 11; | ||
| 82 | pub const SIGSYS = 12; | ||
| 83 | pub const SIGPIPE = 13; | ||
| 84 | pub const SIGALRM = 14; | ||
| 85 | pub const SIGTERM = 15; | ||
| 86 | pub const SIGURG = 16; | ||
| 87 | pub const SIGSTOP = 17; | ||
| 88 | pub const SIGTSTP = 18; | ||
| 89 | pub const SIGCONT = 19; | ||
| 90 | pub const SIGCHLD = 20; | ||
| 91 | pub const SIGTTIN = 21; | ||
| 92 | pub const SIGTTOU = 22; | ||
| 93 | pub const SIGIO = 23; | ||
| 94 | pub const SIGXCPU = 24; | ||
| 95 | pub const SIGXFSZ = 25; | ||
| 96 | pub const SIGVTALRM = 26; | ||
| 97 | pub const SIGPROF = 27; | ||
| 98 | pub const SIGWINCH = 28; | ||
| 99 | pub const SIGINFO = 29; | ||
| 100 | pub const SIGUSR1 = 30; | ||
| 101 | pub const SIGUSR2 = 31; | ||
| 102 | pub const SIGPWR = 32; | ||
| 103 | |||
| 104 | pub const SIGRTMIN = 33; | ||
| 105 | pub const SIGRTMAX = 63; | ||
| 106 | |||
| 107 | // access function | ||
| 108 | pub const F_OK = 0; // test for existence of file | ||
| 109 | pub const X_OK = 1; // test for execute or search permission | ||
| 110 | pub const W_OK = 2; // test for write permission | ||
| 111 | pub const R_OK = 4; // test for read permission | ||
| 112 | |||
| 113 | pub const O_RDONLY = 0x0000; | ||
| 114 | pub const O_WRONLY = 0x0001; | ||
| 115 | pub const O_RDWR = 0x0002; | ||
| 116 | pub const O_ACCMODE = 0x0003; | ||
| 117 | |||
| 118 | pub const O_CREAT = 0x0200; | ||
| 119 | pub const O_EXCL = 0x0800; | ||
| 120 | pub const O_NOCTTY = 0x8000; | ||
| 121 | pub const O_TRUNC = 0x0400; | ||
| 122 | pub const O_APPEND = 0x0008; | ||
| 123 | pub const O_NONBLOCK = 0x0004; | ||
| 124 | pub const O_DSYNC = 0x00010000; | ||
| 125 | pub const O_SYNC = 0x0080; | ||
| 126 | pub const O_RSYNC = 0x00020000; | ||
| 127 | pub const O_DIRECTORY = 0x00080000; | ||
| 128 | pub const O_NOFOLLOW = 0x00000100; | ||
| 129 | pub const O_CLOEXEC = 0x00400000; | ||
| 130 | |||
| 131 | pub const O_ASYNC = 0x0040; | ||
| 132 | pub const O_DIRECT = 0x00080000; | ||
| 133 | pub const O_LARGEFILE = 0; | ||
| 134 | pub const O_NOATIME = 0; | ||
| 135 | pub const O_PATH = 0; | ||
| 136 | pub const O_TMPFILE = 0; | ||
| 137 | pub const O_NDELAY = O_NONBLOCK; | ||
| 138 | |||
| 139 | pub const F_DUPFD = 0; | ||
| 140 | pub const F_GETFD = 1; | ||
| 141 | pub const F_SETFD = 2; | ||
| 142 | pub const F_GETFL = 3; | ||
| 143 | pub const F_SETFL = 4; | ||
| 144 | |||
| 145 | pub const F_GETOWN = 5; | ||
| 146 | pub const F_SETOWN = 6; | ||
| 147 | |||
| 148 | pub const F_GETLK = 7; | ||
| 149 | pub const F_SETLK = 8; | ||
| 150 | pub const F_SETLKW = 9; | ||
| 151 | |||
| 152 | pub const SEEK_SET = 0; | ||
| 153 | pub const SEEK_CUR = 1; | ||
| 154 | pub const SEEK_END = 2; | ||
| 155 | |||
| 156 | pub const SIG_BLOCK = 1; | ||
| 157 | pub const SIG_UNBLOCK = 2; | ||
| 158 | pub const SIG_SETMASK = 3; | ||
| 159 | |||
| 160 | pub const SOCK_STREAM = 1; | ||
| 161 | pub const SOCK_DGRAM = 2; | ||
| 162 | pub const SOCK_RAW = 3; | ||
| 163 | pub const SOCK_RDM = 4; | ||
| 164 | pub const SOCK_SEQPACKET = 5; | ||
| 165 | |||
| 166 | pub const SOCK_CLOEXEC = 0x10000000; | ||
| 167 | pub const SOCK_NONBLOCK = 0x20000000; | ||
| 168 | |||
| 169 | pub const PROTO_ip = 0; | ||
| 170 | pub const PROTO_icmp = 1; | ||
| 171 | pub const PROTO_igmp = 2; | ||
| 172 | pub const PROTO_ggp = 3; | ||
| 173 | pub const PROTO_ipencap = 4; | ||
| 174 | pub const PROTO_tcp = 6; | ||
| 175 | pub const PROTO_egp = 8; | ||
| 176 | pub const PROTO_pup = 12; | ||
| 177 | pub const PROTO_udp = 17; | ||
| 178 | pub const PROTO_xns_idp = 22; | ||
| 179 | pub const PROTO_iso_tp4 = 29; | ||
| 180 | pub const PROTO_ipv6 = 41; | ||
| 181 | pub const PROTO_ipv6_route = 43; | ||
| 182 | pub const PROTO_ipv6_frag = 44; | ||
| 183 | pub const PROTO_rsvp = 46; | ||
| 184 | pub const PROTO_gre = 47; | ||
| 185 | pub const PROTO_esp = 50; | ||
| 186 | pub const PROTO_ah = 51; | ||
| 187 | pub const PROTO_ipv6_icmp = 58; | ||
| 188 | pub const PROTO_ipv6_nonxt = 59; | ||
| 189 | pub const PROTO_ipv6_opts = 60; | ||
| 190 | pub const PROTO_encap = 98; | ||
| 191 | pub const PROTO_pim = 103; | ||
| 192 | pub const PROTO_raw = 255; | ||
| 193 | |||
| 194 | pub const PF_UNSPEC = 0; | ||
| 195 | pub const PF_LOCAL = 1; | ||
| 196 | pub const PF_UNIX = PF_LOCAL; | ||
| 197 | pub const PF_FILE = PF_LOCAL; | ||
| 198 | pub const PF_INET = 2; | ||
| 199 | pub const PF_APPLETALK = 16; | ||
| 200 | pub const PF_INET6 = 24; | ||
| 201 | pub const PF_DECnet = 12; | ||
| 202 | pub const PF_KEY = 29; | ||
| 203 | pub const PF_ROUTE = 34; | ||
| 204 | pub const PF_SNA = 11; | ||
| 205 | pub const PF_MPLS = 33; | ||
| 206 | pub const PF_CAN = 35; | ||
| 207 | pub const PF_BLUETOOTH = 31; | ||
| 208 | pub const PF_ISDN = 26; | ||
| 209 | pub const PF_MAX = 37; | ||
| 210 | |||
| 211 | pub const AF_UNSPEC = PF_UNSPEC; | ||
| 212 | pub const AF_LOCAL = PF_LOCAL; | ||
| 213 | pub const AF_UNIX = AF_LOCAL; | ||
| 214 | pub const AF_FILE = AF_LOCAL; | ||
| 215 | pub const AF_INET = PF_INET; | ||
| 216 | pub const AF_APPLETALK = PF_APPLETALK; | ||
| 217 | pub const AF_INET6 = PF_INET6; | ||
| 218 | pub const AF_KEY = PF_KEY; | ||
| 219 | pub const AF_ROUTE = PF_ROUTE; | ||
| 220 | pub const AF_SNA = PF_SNA; | ||
| 221 | pub const AF_MPLS = PF_MPLS; | ||
| 222 | pub const AF_CAN = PF_CAN; | ||
| 223 | pub const AF_BLUETOOTH = PF_BLUETOOTH; | ||
| 224 | pub const AF_ISDN = PF_ISDN; | ||
| 225 | pub const AF_MAX = PF_MAX; | ||
| 226 | |||
| 227 | pub const DT_UNKNOWN = 0; | ||
| 228 | pub const DT_FIFO = 1; | ||
| 229 | pub const DT_CHR = 2; | ||
| 230 | pub const DT_DIR = 4; | ||
| 231 | pub const DT_BLK = 6; | ||
| 232 | pub const DT_REG = 8; | ||
| 233 | pub const DT_LNK = 10; | ||
| 234 | pub const DT_SOCK = 12; | ||
| 235 | pub const DT_WHT = 14; | ||
| 236 | |||
| 237 | /// add event to kq (implies enable) | ||
| 238 | pub const EV_ADD = 0x0001; | ||
| 239 | |||
| 240 | /// delete event from kq | ||
| 241 | pub const EV_DELETE = 0x0002; | ||
| 242 | |||
| 243 | /// enable event | ||
| 244 | pub const EV_ENABLE = 0x0004; | ||
| 245 | |||
| 246 | /// disable event (not reported) | ||
| 247 | pub const EV_DISABLE = 0x0008; | ||
| 248 | |||
| 249 | /// only report one occurrence | ||
| 250 | pub const EV_ONESHOT = 0x0010; | ||
| 251 | |||
| 252 | /// clear event state after reporting | ||
| 253 | pub const EV_CLEAR = 0x0020; | ||
| 254 | |||
| 255 | /// force immediate event output | ||
| 256 | /// ... with or without EV_ERROR | ||
| 257 | /// ... use KEVENT_FLAG_ERROR_EVENTS | ||
| 258 | /// on syscalls supporting flags | ||
| 259 | pub const EV_RECEIPT = 0x0040; | ||
| 260 | |||
| 261 | /// disable event after reporting | ||
| 262 | pub const EV_DISPATCH = 0x0080; | ||
| 263 | |||
| 264 | pub const EVFILT_READ = 0; | ||
| 265 | pub const EVFILT_WRITE = 1; | ||
| 266 | |||
| 267 | /// attached to aio requests | ||
| 268 | pub const EVFILT_AIO = 2; | ||
| 269 | |||
| 270 | /// attached to vnodes | ||
| 271 | pub const EVFILT_VNODE = 3; | ||
| 272 | |||
| 273 | /// attached to struct proc | ||
| 274 | pub const EVFILT_PROC = 4; | ||
| 275 | |||
| 276 | /// attached to struct proc | ||
| 277 | pub const EVFILT_SIGNAL = 5; | ||
| 278 | |||
| 279 | /// timers | ||
| 280 | pub const EVFILT_TIMER = 6; | ||
| 281 | |||
| 282 | /// Filesystem events | ||
| 283 | pub const EVFILT_FS = 7; | ||
| 284 | |||
| 285 | /// On input, NOTE_TRIGGER causes the event to be triggered for output. | ||
| 286 | pub const NOTE_TRIGGER = 0x08000000; | ||
| 287 | |||
| 288 | /// low water mark | ||
| 289 | pub const NOTE_LOWAT = 0x00000001; | ||
| 290 | |||
| 291 | /// vnode was removed | ||
| 292 | pub const NOTE_DELETE = 0x00000001; | ||
| 293 | |||
| 294 | /// data contents changed | ||
| 295 | pub const NOTE_WRITE = 0x00000002; | ||
| 296 | |||
| 297 | /// size increased | ||
| 298 | pub const NOTE_EXTEND = 0x00000004; | ||
| 299 | |||
| 300 | /// attributes changed | ||
| 301 | pub const NOTE_ATTRIB = 0x00000008; | ||
| 302 | |||
| 303 | /// link count changed | ||
| 304 | pub const NOTE_LINK = 0x00000010; | ||
| 305 | |||
| 306 | /// vnode was renamed | ||
| 307 | pub const NOTE_RENAME = 0x00000020; | ||
| 308 | |||
| 309 | /// vnode access was revoked | ||
| 310 | pub const NOTE_REVOKE = 0x00000040; | ||
| 311 | |||
| 312 | /// process exited | ||
| 313 | pub const NOTE_EXIT = 0x80000000; | ||
| 314 | |||
| 315 | /// process forked | ||
| 316 | pub const NOTE_FORK = 0x40000000; | ||
| 317 | |||
| 318 | /// process exec'd | ||
| 319 | pub const NOTE_EXEC = 0x20000000; | ||
| 320 | |||
| 321 | /// mask for signal & exit status | ||
| 322 | pub const NOTE_PDATAMASK = 0x000fffff; | ||
| 323 | pub const NOTE_PCTRLMASK = 0xf0000000; | ||
| 324 | |||
| 325 | pub const TIOCCBRK = 0x2000747a; | ||
| 326 | pub const TIOCCDTR = 0x20007478; | ||
| 327 | pub const TIOCCONS = 0x80047462; | ||
| 328 | pub const TIOCDCDTIMESTAMP = 0x40107458; | ||
| 329 | pub const TIOCDRAIN = 0x2000745e; | ||
| 330 | pub const TIOCEXCL = 0x2000740d; | ||
| 331 | pub const TIOCEXT = 0x80047460; | ||
| 332 | pub const TIOCFLAG_CDTRCTS = 0x10; | ||
| 333 | pub const TIOCFLAG_CLOCAL = 0x2; | ||
| 334 | pub const TIOCFLAG_CRTSCTS = 0x4; | ||
| 335 | pub const TIOCFLAG_MDMBUF = 0x8; | ||
| 336 | pub const TIOCFLAG_SOFTCAR = 0x1; | ||
| 337 | pub const TIOCFLUSH = 0x80047410; | ||
| 338 | pub const TIOCGETA = 0x402c7413; | ||
| 339 | pub const TIOCGETD = 0x4004741a; | ||
| 340 | pub const TIOCGFLAGS = 0x4004745d; | ||
| 341 | pub const TIOCGLINED = 0x40207442; | ||
| 342 | pub const TIOCGPGRP = 0x40047477; | ||
| 343 | pub const TIOCGQSIZE = 0x40047481; | ||
| 344 | pub const TIOCGRANTPT = 0x20007447; | ||
| 345 | pub const TIOCGSID = 0x40047463; | ||
| 346 | pub const TIOCGSIZE = 0x40087468; | ||
| 347 | pub const TIOCGWINSZ = 0x40087468; | ||
| 348 | pub const TIOCMBIC = 0x8004746b; | ||
| 349 | pub const TIOCMBIS = 0x8004746c; | ||
| 350 | pub const TIOCMGET = 0x4004746a; | ||
| 351 | pub const TIOCMSET = 0x8004746d; | ||
| 352 | pub const TIOCM_CAR = 0x40; | ||
| 353 | pub const TIOCM_CD = 0x40; | ||
| 354 | pub const TIOCM_CTS = 0x20; | ||
| 355 | pub const TIOCM_DSR = 0x100; | ||
| 356 | pub const TIOCM_DTR = 0x2; | ||
| 357 | pub const TIOCM_LE = 0x1; | ||
| 358 | pub const TIOCM_RI = 0x80; | ||
| 359 | pub const TIOCM_RNG = 0x80; | ||
| 360 | pub const TIOCM_RTS = 0x4; | ||
| 361 | pub const TIOCM_SR = 0x10; | ||
| 362 | pub const TIOCM_ST = 0x8; | ||
| 363 | pub const TIOCNOTTY = 0x20007471; | ||
| 364 | pub const TIOCNXCL = 0x2000740e; | ||
| 365 | pub const TIOCOUTQ = 0x40047473; | ||
| 366 | pub const TIOCPKT = 0x80047470; | ||
| 367 | pub const TIOCPKT_DATA = 0x0; | ||
| 368 | pub const TIOCPKT_DOSTOP = 0x20; | ||
| 369 | pub const TIOCPKT_FLUSHREAD = 0x1; | ||
| 370 | pub const TIOCPKT_FLUSHWRITE = 0x2; | ||
| 371 | pub const TIOCPKT_IOCTL = 0x40; | ||
| 372 | pub const TIOCPKT_NOSTOP = 0x10; | ||
| 373 | pub const TIOCPKT_START = 0x8; | ||
| 374 | pub const TIOCPKT_STOP = 0x4; | ||
| 375 | pub const TIOCPTMGET = 0x40287446; | ||
| 376 | pub const TIOCPTSNAME = 0x40287448; | ||
| 377 | pub const TIOCRCVFRAME = 0x80087445; | ||
| 378 | pub const TIOCREMOTE = 0x80047469; | ||
| 379 | pub const TIOCSBRK = 0x2000747b; | ||
| 380 | pub const TIOCSCTTY = 0x20007461; | ||
| 381 | pub const TIOCSDTR = 0x20007479; | ||
| 382 | pub const TIOCSETA = 0x802c7414; | ||
| 383 | pub const TIOCSETAF = 0x802c7416; | ||
| 384 | pub const TIOCSETAW = 0x802c7415; | ||
| 385 | pub const TIOCSETD = 0x8004741b; | ||
| 386 | pub const TIOCSFLAGS = 0x8004745c; | ||
| 387 | pub const TIOCSIG = 0x2000745f; | ||
| 388 | pub const TIOCSLINED = 0x80207443; | ||
| 389 | pub const TIOCSPGRP = 0x80047476; | ||
| 390 | pub const TIOCSQSIZE = 0x80047480; | ||
| 391 | pub const TIOCSSIZE = 0x80087467; | ||
| 392 | pub const TIOCSTART = 0x2000746e; | ||
| 393 | pub const TIOCSTAT = 0x80047465; | ||
| 394 | pub const TIOCSTI = 0x80017472; | ||
| 395 | pub const TIOCSTOP = 0x2000746f; | ||
| 396 | pub const TIOCSWINSZ = 0x80087467; | ||
| 397 | pub const TIOCUCNTL = 0x80047466; | ||
| 398 | pub const TIOCXMTFRAME = 0x80087444; | ||
| 399 | |||
| 400 | pub const sockaddr = c.sockaddr; | ||
| 401 | pub const sockaddr_in = c.sockaddr_in; | ||
| 402 | pub const sockaddr_in6 = c.sockaddr_in6; | ||
| 403 | |||
| 404 | fn unsigned(s: i32) u32 { | ||
| 405 | return @bitCast(u32, s); | ||
| 406 | } | ||
| 407 | fn signed(s: u32) i32 { | ||
| 408 | return @bitCast(i32, s); | ||
| 409 | } | ||
| 410 | pub fn WEXITSTATUS(s: i32) i32 { | ||
| 411 | return signed((unsigned(s) >> 8) & 0xff); | ||
| 412 | } | ||
| 413 | pub fn WTERMSIG(s: i32) i32 { | ||
| 414 | return signed(unsigned(s) & 0x7f); | ||
| 415 | } | ||
| 416 | pub fn WSTOPSIG(s: i32) i32 { | ||
| 417 | return WEXITSTATUS(s); | ||
| 418 | } | ||
| 419 | pub fn WIFEXITED(s: i32) bool { | ||
| 420 | return WTERMSIG(s) == 0; | ||
| 421 | } | ||
| 422 | |||
| 423 | pub fn WIFCONTINUED(s: i32) bool { | ||
| 424 | return ((s & 0x7f) == 0xffff); | ||
| 425 | } | ||
| 426 | |||
| 427 | pub fn WIFSTOPPED(s: i32) bool { | ||
| 428 | return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); | ||
| 429 | } | ||
| 430 | |||
| 431 | pub fn WIFSIGNALED(s: i32) bool { | ||
| 432 | return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); | ||
| 433 | } | ||
| 434 | |||
| 435 | pub const winsize = extern struct { | ||
| 436 | ws_row: u16, | ||
| 437 | ws_col: u16, | ||
| 438 | ws_xpixel: u16, | ||
| 439 | ws_ypixel: u16, | ||
| 440 | }; | ||
| 441 | |||
| 442 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 443 | pub fn getErrno(r: usize) usize { | ||
| 444 | const signed_r = @bitCast(isize, r); | ||
| 445 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 446 | } | ||
| 447 | |||
| 448 | pub fn dup2(old: i32, new: i32) usize { | ||
| 449 | return errnoWrap(c.dup2(old, new)); | ||
| 450 | } | ||
| 451 | |||
| 452 | pub fn chdir(path: [*]const u8) usize { | ||
| 453 | return errnoWrap(c.chdir(path)); | ||
| 454 | } | ||
| 455 | |||
| 456 | pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { | ||
| 457 | return errnoWrap(c.execve(path, argv, envp)); | ||
| 458 | } | ||
| 459 | |||
| 460 | pub fn fork() usize { | ||
| 461 | return errnoWrap(c.fork()); | ||
| 462 | } | ||
| 463 | |||
| 464 | pub fn access(path: [*]const u8, mode: u32) usize { | ||
| 465 | return errnoWrap(c.access(path, mode)); | ||
| 466 | } | ||
| 467 | |||
| 468 | pub fn getcwd(buf: [*]u8, size: usize) usize { | ||
| 469 | return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 470 | } | ||
| 471 | |||
| 472 | pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { | ||
| 473 | return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); | ||
| 474 | } | ||
| 475 | |||
| 476 | pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { | ||
| 477 | return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); | ||
| 478 | } | ||
| 479 | |||
| 480 | pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { | ||
| 481 | return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 482 | } | ||
| 483 | |||
| 484 | pub fn isatty(fd: i32) bool { | ||
| 485 | return c.isatty(fd) != 0; | ||
| 486 | } | ||
| 487 | |||
| 488 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 489 | return errnoWrap(c.readlink(path, buf_ptr, buf_len)); | ||
| 490 | } | ||
| 491 | |||
| 492 | pub fn mkdir(path: [*]const u8, mode: u32) usize { | ||
| 493 | return errnoWrap(c.mkdir(path, mode)); | ||
| 494 | } | ||
| 495 | |||
| 496 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | ||
| 497 | const ptr_result = c.mmap( | ||
| 498 | @ptrCast(?*c_void, address), | ||
| 499 | length, | ||
| 500 | @bitCast(c_int, @intCast(c_uint, prot)), | ||
| 501 | @bitCast(c_int, c_uint(flags)), | ||
| 502 | fd, | ||
| 503 | offset, | ||
| 504 | ); | ||
| 505 | const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); | ||
| 506 | return errnoWrap(isize_result); | ||
| 507 | } | ||
| 508 | |||
| 509 | pub fn munmap(address: usize, length: usize) usize { | ||
| 510 | return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); | ||
| 511 | } | ||
| 512 | |||
| 513 | pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { | ||
| 514 | return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); | ||
| 515 | } | ||
| 516 | |||
| 517 | pub fn rmdir(path: [*]const u8) usize { | ||
| 518 | return errnoWrap(c.rmdir(path)); | ||
| 519 | } | ||
| 520 | |||
| 521 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | ||
| 522 | return errnoWrap(c.symlink(existing, new)); | ||
| 523 | } | ||
| 524 | |||
| 525 | pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { | ||
| 526 | return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); | ||
| 527 | } | ||
| 528 | |||
| 529 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { | ||
| 530 | return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 531 | } | ||
| 532 | |||
| 533 | pub fn pipe(fd: *[2]i32) usize { | ||
| 534 | return pipe2(fd, 0); | ||
| 535 | } | ||
| 536 | |||
| 537 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | ||
| 538 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 539 | return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); | ||
| 540 | } | ||
| 541 | |||
| 542 | pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { | ||
| 543 | return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); | ||
| 544 | } | ||
| 545 | |||
| 546 | pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { | ||
| 547 | return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); | ||
| 548 | } | ||
| 549 | |||
| 550 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { | ||
| 551 | return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 552 | } | ||
| 553 | |||
| 554 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | ||
| 555 | return errnoWrap(c.rename(old, new)); | ||
| 556 | } | ||
| 557 | |||
| 558 | pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { | ||
| 559 | return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); | ||
| 560 | } | ||
| 561 | |||
| 562 | pub fn create(path: [*]const u8, perm: usize) usize { | ||
| 563 | return arch.syscall2(SYS_creat, @ptrToInt(path), perm); | ||
| 564 | } | ||
| 565 | |||
| 566 | pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { | ||
| 567 | return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); | ||
| 568 | } | ||
| 569 | |||
| 570 | pub fn close(fd: i32) usize { | ||
| 571 | return errnoWrap(c.close(fd)); | ||
| 572 | } | ||
| 573 | |||
| 574 | pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { | ||
| 575 | return errnoWrap(c.lseek(fd, offset, whence)); | ||
| 576 | } | ||
| 577 | |||
| 578 | pub fn exit(code: i32) noreturn { | ||
| 579 | c.exit(code); | ||
| 580 | } | ||
| 581 | |||
| 582 | pub fn kill(pid: i32, sig: i32) usize { | ||
| 583 | return errnoWrap(c.kill(pid, sig)); | ||
| 584 | } | ||
| 585 | |||
| 586 | pub fn unlink(path: [*]const u8) usize { | ||
| 587 | return errnoWrap(c.unlink(path)); | ||
| 588 | } | ||
| 589 | |||
| 590 | pub fn waitpid(pid: i32, status: *i32, options: u32) usize { | ||
| 591 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 592 | return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); | ||
| 593 | } | ||
| 594 | |||
| 595 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | ||
| 596 | return errnoWrap(c.nanosleep(req, rem)); | ||
| 597 | } | ||
| 598 | |||
| 599 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | ||
| 600 | return errnoWrap(c.clock_gettime(clk_id, tp)); | ||
| 601 | } | ||
| 602 | |||
| 603 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | ||
| 604 | return errnoWrap(c.clock_getres(clk_id, tp)); | ||
| 605 | } | ||
| 606 | |||
| 607 | pub fn setuid(uid: u32) usize { | ||
| 608 | return errnoWrap(c.setuid(uid)); | ||
| 609 | } | ||
| 610 | |||
| 611 | pub fn setgid(gid: u32) usize { | ||
| 612 | return errnoWrap(c.setgid(gid)); | ||
| 613 | } | ||
| 614 | |||
| 615 | pub fn setreuid(ruid: u32, euid: u32) usize { | ||
| 616 | return errnoWrap(c.setreuid(ruid, euid)); | ||
| 617 | } | ||
| 618 | |||
| 619 | pub fn setregid(rgid: u32, egid: u32) usize { | ||
| 620 | return errnoWrap(c.setregid(rgid, egid)); | ||
| 621 | } | ||
| 622 | |||
| 623 | const NSIG = 32; | ||
| 624 | |||
| 625 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | ||
| 626 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ||
| 627 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | ||
| 628 | |||
| 629 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | ||
| 630 | pub const Sigaction = extern struct { | ||
| 631 | /// signal handler | ||
| 632 | __sigaction_u: extern union { | ||
| 633 | __sa_handler: extern fn (i32) void, | ||
| 634 | __sa_sigaction: extern fn (i32, *__siginfo, usize) void, | ||
| 635 | }, | ||
| 636 | |||
| 637 | /// see signal options | ||
| 638 | sa_flags: u32, | ||
| 639 | |||
| 640 | /// signal mask to apply | ||
| 641 | sa_mask: sigset_t, | ||
| 642 | }; | ||
| 643 | |||
| 644 | pub const _SIG_WORDS = 4; | ||
| 645 | pub const _SIG_MAXSIG = 128; | ||
| 646 | |||
| 647 | pub inline fn _SIG_IDX(sig: usize) usize { | ||
| 648 | return sig - 1; | ||
| 649 | } | ||
| 650 | pub inline fn _SIG_WORD(sig: usize) usize { | ||
| 651 | return_SIG_IDX(sig) >> 5; | ||
| 652 | } | ||
| 653 | pub inline fn _SIG_BIT(sig: usize) usize { | ||
| 654 | return 1 << (_SIG_IDX(sig) & 31); | ||
| 655 | } | ||
| 656 | pub inline fn _SIG_VALID(sig: usize) usize { | ||
| 657 | return sig <= _SIG_MAXSIG and sig > 0; | ||
| 658 | } | ||
| 659 | |||
| 660 | pub const sigset_t = extern struct { | ||
| 661 | __bits: [_SIG_WORDS]u32, | ||
| 662 | }; | ||
| 663 | |||
| 664 | pub fn raise(sig: i32) usize { | ||
| 665 | return errnoWrap(c.raise(sig)); | ||
| 666 | } | ||
| 667 | |||
| 668 | pub const Stat = c.Stat; | ||
| 669 | pub const dirent = c.dirent; | ||
| 670 | pub const timespec = c.timespec; | ||
| 671 | |||
| 672 | pub fn fstat(fd: i32, buf: *c.Stat) usize { | ||
| 673 | return errnoWrap(c.fstat(fd, buf)); | ||
| 674 | } | ||
| 675 | pub const iovec = extern struct { | ||
| 676 | iov_base: [*]u8, | ||
| 677 | iov_len: usize, | ||
| 678 | }; | ||
| 679 | |||
| 680 | pub const iovec_const = extern struct { | ||
| 681 | iov_base: [*]const u8, | ||
| 682 | iov_len: usize, | ||
| 683 | }; | ||
| 684 | |||
| 685 | // TODO avoid libc dependency | ||
| 686 | pub fn kqueue() usize { | ||
| 687 | return errnoWrap(c.kqueue()); | ||
| 688 | } | ||
| 689 | |||
| 690 | // TODO avoid libc dependency | ||
| 691 | pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { | ||
| 692 | return errnoWrap(c.kevent( | ||
| 693 | kq, | ||
| 694 | changelist.ptr, | ||
| 695 | @intCast(c_int, changelist.len), | ||
| 696 | eventlist.ptr, | ||
| 697 | @intCast(c_int, eventlist.len), | ||
| 698 | timeout, | ||
| 699 | )); | ||
| 700 | } | ||
| 701 | |||
| 702 | // TODO avoid libc dependency | ||
| 703 | pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 704 | return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); | ||
| 705 | } | ||
| 706 | |||
| 707 | // TODO avoid libc dependency | ||
| 708 | pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 709 | return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); | ||
| 710 | } | ||
| 711 | |||
| 712 | // TODO avoid libc dependency | ||
| 713 | pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { | ||
| 714 | return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); | ||
| 715 | } | ||
| 716 | |||
| 717 | // TODO avoid libc dependency | ||
| 718 | |||
| 719 | /// Takes the return value from a syscall and formats it back in the way | ||
| 720 | /// that the kernel represents it to libc. Errno was a mistake, let's make | ||
| 721 | /// it go away forever. | ||
| 722 | fn errnoWrap(value: isize) usize { | ||
| 723 | return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); | ||
| 724 | } | ||
std/os/netbsd/index.zig deleted-725| ... | @@ -1,725 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | |||
| 3 | pub use @import("errno.zig"); | ||
| 4 | |||
| 5 | const std = @import("../../index.zig"); | ||
| 6 | const c = std.c; | ||
| 7 | |||
| 8 | const assert = std.debug.assert; | ||
| 9 | const maxInt = std.math.maxInt; | ||
| 10 | pub const Kevent = c.Kevent; | ||
| 11 | |||
| 12 | pub const CTL_KERN = 1; | ||
| 13 | pub const CTL_DEBUG = 5; | ||
| 14 | |||
| 15 | pub const KERN_PROC_ARGS = 48; // struct: process argv/env | ||
| 16 | pub const KERN_PROC_PATHNAME = 5; // path to executable | ||
| 17 | |||
| 18 | pub const PATH_MAX = 1024; | ||
| 19 | |||
| 20 | pub const STDIN_FILENO = 0; | ||
| 21 | pub const STDOUT_FILENO = 1; | ||
| 22 | pub const STDERR_FILENO = 2; | ||
| 23 | |||
| 24 | pub const PROT_NONE = 0; | ||
| 25 | pub const PROT_READ = 1; | ||
| 26 | pub const PROT_WRITE = 2; | ||
| 27 | pub const PROT_EXEC = 4; | ||
| 28 | |||
| 29 | pub const CLOCK_REALTIME = 0; | ||
| 30 | pub const CLOCK_VIRTUAL = 1; | ||
| 31 | pub const CLOCK_PROF = 2; | ||
| 32 | pub const CLOCK_MONOTONIC = 3; | ||
| 33 | pub const CLOCK_THREAD_CPUTIME_ID = 0x20000000; | ||
| 34 | pub const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; | ||
| 35 | |||
| 36 | pub const MAP_FAILED = maxInt(usize); | ||
| 37 | pub const MAP_SHARED = 0x0001; | ||
| 38 | pub const MAP_PRIVATE = 0x0002; | ||
| 39 | pub const MAP_REMAPDUP = 0x0004; | ||
| 40 | pub const MAP_FIXED = 0x0010; | ||
| 41 | pub const MAP_RENAME = 0x0020; | ||
| 42 | pub const MAP_NORESERVE = 0x0040; | ||
| 43 | pub const MAP_INHERIT = 0x0080; | ||
| 44 | pub const MAP_HASSEMAPHORE = 0x0200; | ||
| 45 | pub const MAP_TRYFIXED = 0x0400; | ||
| 46 | pub const MAP_WIRED = 0x0800; | ||
| 47 | |||
| 48 | pub const MAP_FILE = 0x0000; | ||
| 49 | pub const MAP_NOSYNC = 0x0800; | ||
| 50 | pub const MAP_ANON = 0x1000; | ||
| 51 | pub const MAP_ANONYMOUS = MAP_ANON; | ||
| 52 | pub const MAP_STACK = 0x2000; | ||
| 53 | |||
| 54 | pub const WNOHANG = 0x00000001; | ||
| 55 | pub const WUNTRACED = 0x00000002; | ||
| 56 | pub const WSTOPPED = WUNTRACED; | ||
| 57 | pub const WCONTINUED = 0x00000010; | ||
| 58 | pub const WNOWAIT = 0x00010000; | ||
| 59 | pub const WEXITED = 0x00000020; | ||
| 60 | pub const WTRAPPED = 0x00000040; | ||
| 61 | |||
| 62 | pub const SA_ONSTACK = 0x0001; | ||
| 63 | pub const SA_RESTART = 0x0002; | ||
| 64 | pub const SA_RESETHAND = 0x0004; | ||
| 65 | pub const SA_NOCLDSTOP = 0x0008; | ||
| 66 | pub const SA_NODEFER = 0x0010; | ||
| 67 | pub const SA_NOCLDWAIT = 0x0020; | ||
| 68 | pub const SA_SIGINFO = 0x0040; | ||
| 69 | |||
| 70 | pub const SIGHUP = 1; | ||
| 71 | pub const SIGINT = 2; | ||
| 72 | pub const SIGQUIT = 3; | ||
| 73 | pub const SIGILL = 4; | ||
| 74 | pub const SIGTRAP = 5; | ||
| 75 | pub const SIGABRT = 6; | ||
| 76 | pub const SIGIOT = SIGABRT; | ||
| 77 | pub const SIGEMT = 7; | ||
| 78 | pub const SIGFPE = 8; | ||
| 79 | pub const SIGKILL = 9; | ||
| 80 | pub const SIGBUS = 10; | ||
| 81 | pub const SIGSEGV = 11; | ||
| 82 | pub const SIGSYS = 12; | ||
| 83 | pub const SIGPIPE = 13; | ||
| 84 | pub const SIGALRM = 14; | ||
| 85 | pub const SIGTERM = 15; | ||
| 86 | pub const SIGURG = 16; | ||
| 87 | pub const SIGSTOP = 17; | ||
| 88 | pub const SIGTSTP = 18; | ||
| 89 | pub const SIGCONT = 19; | ||
| 90 | pub const SIGCHLD = 20; | ||
| 91 | pub const SIGTTIN = 21; | ||
| 92 | pub const SIGTTOU = 22; | ||
| 93 | pub const SIGIO = 23; | ||
| 94 | pub const SIGXCPU = 24; | ||
| 95 | pub const SIGXFSZ = 25; | ||
| 96 | pub const SIGVTALRM = 26; | ||
| 97 | pub const SIGPROF = 27; | ||
| 98 | pub const SIGWINCH = 28; | ||
| 99 | pub const SIGINFO = 29; | ||
| 100 | pub const SIGUSR1 = 30; | ||
| 101 | pub const SIGUSR2 = 31; | ||
| 102 | pub const SIGPWR = 32; | ||
| 103 | |||
| 104 | pub const SIGRTMIN = 33; | ||
| 105 | pub const SIGRTMAX = 63; | ||
| 106 | |||
| 107 | // access function | ||
| 108 | pub const F_OK = 0; // test for existence of file | ||
| 109 | pub const X_OK = 1; // test for execute or search permission | ||
| 110 | pub const W_OK = 2; // test for write permission | ||
| 111 | pub const R_OK = 4; // test for read permission | ||
| 112 | |||
| 113 | |||
| 114 | pub const O_RDONLY = 0x0000; | ||
| 115 | pub const O_WRONLY = 0x0001; | ||
| 116 | pub const O_RDWR = 0x0002; | ||
| 117 | pub const O_ACCMODE = 0x0003; | ||
| 118 | |||
| 119 | pub const O_CREAT = 0x0200; | ||
| 120 | pub const O_EXCL = 0x0800; | ||
| 121 | pub const O_NOCTTY = 0x8000; | ||
| 122 | pub const O_TRUNC = 0x0400; | ||
| 123 | pub const O_APPEND = 0x0008; | ||
| 124 | pub const O_NONBLOCK = 0x0004; | ||
| 125 | pub const O_DSYNC = 0x00010000; | ||
| 126 | pub const O_SYNC = 0x0080; | ||
| 127 | pub const O_RSYNC = 0x00020000; | ||
| 128 | pub const O_DIRECTORY = 0x00080000; | ||
| 129 | pub const O_NOFOLLOW = 0x00000100; | ||
| 130 | pub const O_CLOEXEC = 0x00400000; | ||
| 131 | |||
| 132 | pub const O_ASYNC = 0x0040; | ||
| 133 | pub const O_DIRECT = 0x00080000; | ||
| 134 | pub const O_LARGEFILE = 0; | ||
| 135 | pub const O_NOATIME = 0; | ||
| 136 | pub const O_PATH = 0; | ||
| 137 | pub const O_TMPFILE = 0; | ||
| 138 | pub const O_NDELAY = O_NONBLOCK; | ||
| 139 | |||
| 140 | pub const F_DUPFD = 0; | ||
| 141 | pub const F_GETFD = 1; | ||
| 142 | pub const F_SETFD = 2; | ||
| 143 | pub const F_GETFL = 3; | ||
| 144 | pub const F_SETFL = 4; | ||
| 145 | |||
| 146 | pub const F_GETOWN = 5; | ||
| 147 | pub const F_SETOWN = 6; | ||
| 148 | |||
| 149 | pub const F_GETLK = 7; | ||
| 150 | pub const F_SETLK = 8; | ||
| 151 | pub const F_SETLKW = 9; | ||
| 152 | |||
| 153 | pub const SEEK_SET = 0; | ||
| 154 | pub const SEEK_CUR = 1; | ||
| 155 | pub const SEEK_END = 2; | ||
| 156 | |||
| 157 | pub const SIG_BLOCK = 1; | ||
| 158 | pub const SIG_UNBLOCK = 2; | ||
| 159 | pub const SIG_SETMASK = 3; | ||
| 160 | |||
| 161 | pub const SOCK_STREAM = 1; | ||
| 162 | pub const SOCK_DGRAM = 2; | ||
| 163 | pub const SOCK_RAW = 3; | ||
| 164 | pub const SOCK_RDM = 4; | ||
| 165 | pub const SOCK_SEQPACKET = 5; | ||
| 166 | |||
| 167 | pub const SOCK_CLOEXEC = 0x10000000; | ||
| 168 | pub const SOCK_NONBLOCK = 0x20000000; | ||
| 169 | |||
| 170 | pub const PROTO_ip = 0; | ||
| 171 | pub const PROTO_icmp = 1; | ||
| 172 | pub const PROTO_igmp = 2; | ||
| 173 | pub const PROTO_ggp = 3; | ||
| 174 | pub const PROTO_ipencap = 4; | ||
| 175 | pub const PROTO_tcp = 6; | ||
| 176 | pub const PROTO_egp = 8; | ||
| 177 | pub const PROTO_pup = 12; | ||
| 178 | pub const PROTO_udp = 17; | ||
| 179 | pub const PROTO_xns_idp = 22; | ||
| 180 | pub const PROTO_iso_tp4 = 29; | ||
| 181 | pub const PROTO_ipv6 = 41; | ||
| 182 | pub const PROTO_ipv6_route = 43; | ||
| 183 | pub const PROTO_ipv6_frag = 44; | ||
| 184 | pub const PROTO_rsvp = 46; | ||
| 185 | pub const PROTO_gre = 47; | ||
| 186 | pub const PROTO_esp = 50; | ||
| 187 | pub const PROTO_ah = 51; | ||
| 188 | pub const PROTO_ipv6_icmp = 58; | ||
| 189 | pub const PROTO_ipv6_nonxt = 59; | ||
| 190 | pub const PROTO_ipv6_opts = 60; | ||
| 191 | pub const PROTO_encap = 98; | ||
| 192 | pub const PROTO_pim = 103; | ||
| 193 | pub const PROTO_raw = 255; | ||
| 194 | |||
| 195 | pub const PF_UNSPEC = 0; | ||
| 196 | pub const PF_LOCAL = 1; | ||
| 197 | pub const PF_UNIX = PF_LOCAL; | ||
| 198 | pub const PF_FILE = PF_LOCAL; | ||
| 199 | pub const PF_INET = 2; | ||
| 200 | pub const PF_APPLETALK = 16; | ||
| 201 | pub const PF_INET6 = 24; | ||
| 202 | pub const PF_DECnet = 12; | ||
| 203 | pub const PF_KEY = 29; | ||
| 204 | pub const PF_ROUTE = 34; | ||
| 205 | pub const PF_SNA = 11; | ||
| 206 | pub const PF_MPLS = 33; | ||
| 207 | pub const PF_CAN = 35; | ||
| 208 | pub const PF_BLUETOOTH = 31; | ||
| 209 | pub const PF_ISDN = 26; | ||
| 210 | pub const PF_MAX = 37; | ||
| 211 | |||
| 212 | pub const AF_UNSPEC = PF_UNSPEC; | ||
| 213 | pub const AF_LOCAL = PF_LOCAL; | ||
| 214 | pub const AF_UNIX = AF_LOCAL; | ||
| 215 | pub const AF_FILE = AF_LOCAL; | ||
| 216 | pub const AF_INET = PF_INET; | ||
| 217 | pub const AF_APPLETALK = PF_APPLETALK; | ||
| 218 | pub const AF_INET6 = PF_INET6; | ||
| 219 | pub const AF_KEY = PF_KEY; | ||
| 220 | pub const AF_ROUTE = PF_ROUTE; | ||
| 221 | pub const AF_SNA = PF_SNA; | ||
| 222 | pub const AF_MPLS = PF_MPLS; | ||
| 223 | pub const AF_CAN = PF_CAN; | ||
| 224 | pub const AF_BLUETOOTH = PF_BLUETOOTH; | ||
| 225 | pub const AF_ISDN = PF_ISDN; | ||
| 226 | pub const AF_MAX = PF_MAX; | ||
| 227 | |||
| 228 | pub const DT_UNKNOWN = 0; | ||
| 229 | pub const DT_FIFO = 1; | ||
| 230 | pub const DT_CHR = 2; | ||
| 231 | pub const DT_DIR = 4; | ||
| 232 | pub const DT_BLK = 6; | ||
| 233 | pub const DT_REG = 8; | ||
| 234 | pub const DT_LNK = 10; | ||
| 235 | pub const DT_SOCK = 12; | ||
| 236 | pub const DT_WHT = 14; | ||
| 237 | |||
| 238 | /// add event to kq (implies enable) | ||
| 239 | pub const EV_ADD = 0x0001; | ||
| 240 | |||
| 241 | /// delete event from kq | ||
| 242 | pub const EV_DELETE = 0x0002; | ||
| 243 | |||
| 244 | /// enable event | ||
| 245 | pub const EV_ENABLE = 0x0004; | ||
| 246 | |||
| 247 | /// disable event (not reported) | ||
| 248 | pub const EV_DISABLE = 0x0008; | ||
| 249 | |||
| 250 | /// only report one occurrence | ||
| 251 | pub const EV_ONESHOT = 0x0010; | ||
| 252 | |||
| 253 | /// clear event state after reporting | ||
| 254 | pub const EV_CLEAR = 0x0020; | ||
| 255 | |||
| 256 | /// force immediate event output | ||
| 257 | /// ... with or without EV_ERROR | ||
| 258 | /// ... use KEVENT_FLAG_ERROR_EVENTS | ||
| 259 | /// on syscalls supporting flags | ||
| 260 | pub const EV_RECEIPT = 0x0040; | ||
| 261 | |||
| 262 | /// disable event after reporting | ||
| 263 | pub const EV_DISPATCH = 0x0080; | ||
| 264 | |||
| 265 | pub const EVFILT_READ = 0; | ||
| 266 | pub const EVFILT_WRITE = 1; | ||
| 267 | |||
| 268 | /// attached to aio requests | ||
| 269 | pub const EVFILT_AIO = 2; | ||
| 270 | |||
| 271 | /// attached to vnodes | ||
| 272 | pub const EVFILT_VNODE = 3; | ||
| 273 | |||
| 274 | /// attached to struct proc | ||
| 275 | pub const EVFILT_PROC = 4; | ||
| 276 | |||
| 277 | /// attached to struct proc | ||
| 278 | pub const EVFILT_SIGNAL = 5; | ||
| 279 | |||
| 280 | /// timers | ||
| 281 | pub const EVFILT_TIMER = 6; | ||
| 282 | |||
| 283 | /// Filesystem events | ||
| 284 | pub const EVFILT_FS = 7; | ||
| 285 | |||
| 286 | /// On input, NOTE_TRIGGER causes the event to be triggered for output. | ||
| 287 | pub const NOTE_TRIGGER = 0x08000000; | ||
| 288 | |||
| 289 | /// low water mark | ||
| 290 | pub const NOTE_LOWAT = 0x00000001; | ||
| 291 | |||
| 292 | /// vnode was removed | ||
| 293 | pub const NOTE_DELETE = 0x00000001; | ||
| 294 | |||
| 295 | /// data contents changed | ||
| 296 | pub const NOTE_WRITE = 0x00000002; | ||
| 297 | |||
| 298 | /// size increased | ||
| 299 | pub const NOTE_EXTEND = 0x00000004; | ||
| 300 | |||
| 301 | /// attributes changed | ||
| 302 | pub const NOTE_ATTRIB = 0x00000008; | ||
| 303 | |||
| 304 | /// link count changed | ||
| 305 | pub const NOTE_LINK = 0x00000010; | ||
| 306 | |||
| 307 | /// vnode was renamed | ||
| 308 | pub const NOTE_RENAME = 0x00000020; | ||
| 309 | |||
| 310 | /// vnode access was revoked | ||
| 311 | pub const NOTE_REVOKE = 0x00000040; | ||
| 312 | |||
| 313 | /// process exited | ||
| 314 | pub const NOTE_EXIT = 0x80000000; | ||
| 315 | |||
| 316 | /// process forked | ||
| 317 | pub const NOTE_FORK = 0x40000000; | ||
| 318 | |||
| 319 | /// process exec'd | ||
| 320 | pub const NOTE_EXEC = 0x20000000; | ||
| 321 | |||
| 322 | /// mask for signal & exit status | ||
| 323 | pub const NOTE_PDATAMASK = 0x000fffff; | ||
| 324 | pub const NOTE_PCTRLMASK = 0xf0000000; | ||
| 325 | |||
| 326 | pub const TIOCCBRK = 0x2000747a; | ||
| 327 | pub const TIOCCDTR = 0x20007478; | ||
| 328 | pub const TIOCCONS = 0x80047462; | ||
| 329 | pub const TIOCDCDTIMESTAMP = 0x40107458; | ||
| 330 | pub const TIOCDRAIN = 0x2000745e; | ||
| 331 | pub const TIOCEXCL = 0x2000740d; | ||
| 332 | pub const TIOCEXT = 0x80047460; | ||
| 333 | pub const TIOCFLAG_CDTRCTS = 0x10; | ||
| 334 | pub const TIOCFLAG_CLOCAL = 0x2; | ||
| 335 | pub const TIOCFLAG_CRTSCTS = 0x4; | ||
| 336 | pub const TIOCFLAG_MDMBUF = 0x8; | ||
| 337 | pub const TIOCFLAG_SOFTCAR = 0x1; | ||
| 338 | pub const TIOCFLUSH = 0x80047410; | ||
| 339 | pub const TIOCGETA = 0x402c7413; | ||
| 340 | pub const TIOCGETD = 0x4004741a; | ||
| 341 | pub const TIOCGFLAGS = 0x4004745d; | ||
| 342 | pub const TIOCGLINED = 0x40207442; | ||
| 343 | pub const TIOCGPGRP = 0x40047477; | ||
| 344 | pub const TIOCGQSIZE = 0x40047481; | ||
| 345 | pub const TIOCGRANTPT = 0x20007447; | ||
| 346 | pub const TIOCGSID = 0x40047463; | ||
| 347 | pub const TIOCGSIZE = 0x40087468; | ||
| 348 | pub const TIOCGWINSZ = 0x40087468; | ||
| 349 | pub const TIOCMBIC = 0x8004746b; | ||
| 350 | pub const TIOCMBIS = 0x8004746c; | ||
| 351 | pub const TIOCMGET = 0x4004746a; | ||
| 352 | pub const TIOCMSET = 0x8004746d; | ||
| 353 | pub const TIOCM_CAR = 0x40; | ||
| 354 | pub const TIOCM_CD = 0x40; | ||
| 355 | pub const TIOCM_CTS = 0x20; | ||
| 356 | pub const TIOCM_DSR = 0x100; | ||
| 357 | pub const TIOCM_DTR = 0x2; | ||
| 358 | pub const TIOCM_LE = 0x1; | ||
| 359 | pub const TIOCM_RI = 0x80; | ||
| 360 | pub const TIOCM_RNG = 0x80; | ||
| 361 | pub const TIOCM_RTS = 0x4; | ||
| 362 | pub const TIOCM_SR = 0x10; | ||
| 363 | pub const TIOCM_ST = 0x8; | ||
| 364 | pub const TIOCNOTTY = 0x20007471; | ||
| 365 | pub const TIOCNXCL = 0x2000740e; | ||
| 366 | pub const TIOCOUTQ = 0x40047473; | ||
| 367 | pub const TIOCPKT = 0x80047470; | ||
| 368 | pub const TIOCPKT_DATA = 0x0; | ||
| 369 | pub const TIOCPKT_DOSTOP = 0x20; | ||
| 370 | pub const TIOCPKT_FLUSHREAD = 0x1; | ||
| 371 | pub const TIOCPKT_FLUSHWRITE = 0x2; | ||
| 372 | pub const TIOCPKT_IOCTL = 0x40; | ||
| 373 | pub const TIOCPKT_NOSTOP = 0x10; | ||
| 374 | pub const TIOCPKT_START = 0x8; | ||
| 375 | pub const TIOCPKT_STOP = 0x4; | ||
| 376 | pub const TIOCPTMGET = 0x40287446; | ||
| 377 | pub const TIOCPTSNAME = 0x40287448; | ||
| 378 | pub const TIOCRCVFRAME = 0x80087445; | ||
| 379 | pub const TIOCREMOTE = 0x80047469; | ||
| 380 | pub const TIOCSBRK = 0x2000747b; | ||
| 381 | pub const TIOCSCTTY = 0x20007461; | ||
| 382 | pub const TIOCSDTR = 0x20007479; | ||
| 383 | pub const TIOCSETA = 0x802c7414; | ||
| 384 | pub const TIOCSETAF = 0x802c7416; | ||
| 385 | pub const TIOCSETAW = 0x802c7415; | ||
| 386 | pub const TIOCSETD = 0x8004741b; | ||
| 387 | pub const TIOCSFLAGS = 0x8004745c; | ||
| 388 | pub const TIOCSIG = 0x2000745f; | ||
| 389 | pub const TIOCSLINED = 0x80207443; | ||
| 390 | pub const TIOCSPGRP = 0x80047476; | ||
| 391 | pub const TIOCSQSIZE = 0x80047480; | ||
| 392 | pub const TIOCSSIZE = 0x80087467; | ||
| 393 | pub const TIOCSTART = 0x2000746e; | ||
| 394 | pub const TIOCSTAT = 0x80047465; | ||
| 395 | pub const TIOCSTI = 0x80017472; | ||
| 396 | pub const TIOCSTOP = 0x2000746f; | ||
| 397 | pub const TIOCSWINSZ = 0x80087467; | ||
| 398 | pub const TIOCUCNTL = 0x80047466; | ||
| 399 | pub const TIOCXMTFRAME = 0x80087444; | ||
| 400 | |||
| 401 | pub const sockaddr = c.sockaddr; | ||
| 402 | pub const sockaddr_in = c.sockaddr_in; | ||
| 403 | pub const sockaddr_in6 = c.sockaddr_in6; | ||
| 404 | |||
| 405 | fn unsigned(s: i32) u32 { | ||
| 406 | return @bitCast(u32, s); | ||
| 407 | } | ||
| 408 | fn signed(s: u32) i32 { | ||
| 409 | return @bitCast(i32, s); | ||
| 410 | } | ||
| 411 | pub fn WEXITSTATUS(s: i32) i32 { | ||
| 412 | return signed((unsigned(s) >> 8) & 0xff); | ||
| 413 | } | ||
| 414 | pub fn WTERMSIG(s: i32) i32 { | ||
| 415 | return signed(unsigned(s) & 0x7f); | ||
| 416 | } | ||
| 417 | pub fn WSTOPSIG(s: i32) i32 { | ||
| 418 | return WEXITSTATUS(s); | ||
| 419 | } | ||
| 420 | pub fn WIFEXITED(s: i32) bool { | ||
| 421 | return WTERMSIG(s) == 0; | ||
| 422 | } | ||
| 423 | |||
| 424 | pub fn WIFCONTINUED(s: i32) bool { | ||
| 425 | return ((s & 0x7f) == 0xffff); | ||
| 426 | } | ||
| 427 | |||
| 428 | pub fn WIFSTOPPED(s: i32) bool { | ||
| 429 | return ((s & 0x7f != 0x7f) and !WIFCONTINUED(s)); | ||
| 430 | } | ||
| 431 | |||
| 432 | pub fn WIFSIGNALED(s: i32) bool { | ||
| 433 | return !WIFSTOPPED(s) and !WIFCONTINUED(s) and !WIFEXITED(s); | ||
| 434 | } | ||
| 435 | |||
| 436 | pub const winsize = extern struct { | ||
| 437 | ws_row: u16, | ||
| 438 | ws_col: u16, | ||
| 439 | ws_xpixel: u16, | ||
| 440 | ws_ypixel: u16, | ||
| 441 | }; | ||
| 442 | |||
| 443 | /// Get the errno from a syscall return value, or 0 for no error. | ||
| 444 | pub fn getErrno(r: usize) usize { | ||
| 445 | const signed_r = @bitCast(isize, r); | ||
| 446 | return if (signed_r > -4096 and signed_r < 0) @intCast(usize, -signed_r) else 0; | ||
| 447 | } | ||
| 448 | |||
| 449 | pub fn dup2(old: i32, new: i32) usize { | ||
| 450 | return errnoWrap(c.dup2(old, new)); | ||
| 451 | } | ||
| 452 | |||
| 453 | pub fn chdir(path: [*]const u8) usize { | ||
| 454 | return errnoWrap(c.chdir(path)); | ||
| 455 | } | ||
| 456 | |||
| 457 | pub fn execve(path: [*]const u8, argv: [*]const ?[*]const u8, envp: [*]const ?[*]const u8) usize { | ||
| 458 | return errnoWrap(c.execve(path, argv, envp)); | ||
| 459 | } | ||
| 460 | |||
| 461 | pub fn fork() usize { | ||
| 462 | return errnoWrap(c.fork()); | ||
| 463 | } | ||
| 464 | |||
| 465 | pub fn access(path: [*]const u8, mode: u32) usize { | ||
| 466 | return errnoWrap(c.access(path, mode)); | ||
| 467 | } | ||
| 468 | |||
| 469 | pub fn getcwd(buf: [*]u8, size: usize) usize { | ||
| 470 | return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 471 | } | ||
| 472 | |||
| 473 | pub fn getdents(fd: i32, dirp: [*]u8, count: usize) usize { | ||
| 474 | return errnoWrap(@bitCast(isize, c.getdents(fd, drip, count))); | ||
| 475 | } | ||
| 476 | |||
| 477 | pub fn getdirentries(fd: i32, buf_ptr: [*]u8, buf_len: usize, basep: *i64) usize { | ||
| 478 | return errnoWrap(@bitCast(isize, c.getdirentries(fd, buf_ptr, buf_len, basep))); | ||
| 479 | } | ||
| 480 | |||
| 481 | pub fn realpath(noalias filename: [*]const u8, noalias resolved_name: [*]u8) usize { | ||
| 482 | return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(c._errno().*)) else 0; | ||
| 483 | } | ||
| 484 | |||
| 485 | pub fn isatty(fd: i32) bool { | ||
| 486 | return c.isatty(fd) != 0; | ||
| 487 | } | ||
| 488 | |||
| 489 | pub fn readlink(noalias path: [*]const u8, noalias buf_ptr: [*]u8, buf_len: usize) usize { | ||
| 490 | return errnoWrap(c.readlink(path, buf_ptr, buf_len)); | ||
| 491 | } | ||
| 492 | |||
| 493 | pub fn mkdir(path: [*]const u8, mode: u32) usize { | ||
| 494 | return errnoWrap(c.mkdir(path, mode)); | ||
| 495 | } | ||
| 496 | |||
| 497 | pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { | ||
| 498 | const ptr_result = c.mmap( | ||
| 499 | @ptrCast(?*c_void, address), | ||
| 500 | length, | ||
| 501 | @bitCast(c_int, @intCast(c_uint, prot)), | ||
| 502 | @bitCast(c_int, c_uint(flags)), | ||
| 503 | fd, | ||
| 504 | offset, | ||
| 505 | ); | ||
| 506 | const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); | ||
| 507 | return errnoWrap(isize_result); | ||
| 508 | } | ||
| 509 | |||
| 510 | pub fn munmap(address: usize, length: usize) usize { | ||
| 511 | return errnoWrap(c.munmap(@intToPtr(*c_void, address), length)); | ||
| 512 | } | ||
| 513 | |||
| 514 | pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { | ||
| 515 | return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte)); | ||
| 516 | } | ||
| 517 | |||
| 518 | pub fn rmdir(path: [*]const u8) usize { | ||
| 519 | return errnoWrap(c.rmdir(path)); | ||
| 520 | } | ||
| 521 | |||
| 522 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | ||
| 523 | return errnoWrap(c.symlink(existing, new)); | ||
| 524 | } | ||
| 525 | |||
| 526 | pub fn pread(fd: i32, buf: [*]u8, nbyte: usize, offset: u64) usize { | ||
| 527 | return errnoWrap(c.pread(fd, @ptrCast(*c_void, buf), nbyte, offset)); | ||
| 528 | } | ||
| 529 | |||
| 530 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: usize) usize { | ||
| 531 | return errnoWrap(c.preadv(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 532 | } | ||
| 533 | |||
| 534 | pub fn pipe(fd: *[2]i32) usize { | ||
| 535 | return pipe2(fd, 0); | ||
| 536 | } | ||
| 537 | |||
| 538 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | ||
| 539 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 540 | return errnoWrap(c.pipe2(@ptrCast(*[2]c_int, fd), flags)); | ||
| 541 | } | ||
| 542 | |||
| 543 | pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { | ||
| 544 | return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte)); | ||
| 545 | } | ||
| 546 | |||
| 547 | pub fn pwrite(fd: i32, buf: [*]const u8, nbyte: usize, offset: u64) usize { | ||
| 548 | return errnoWrap(c.pwrite(fd, @ptrCast(*const c_void, buf), nbyte, offset)); | ||
| 549 | } | ||
| 550 | |||
| 551 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: usize) usize { | ||
| 552 | return errnoWrap(c.pwritev(fd, @ptrCast(*const c_void, iov), @intCast(c_int, count), offset)); | ||
| 553 | } | ||
| 554 | |||
| 555 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | ||
| 556 | return errnoWrap(c.rename(old, new)); | ||
| 557 | } | ||
| 558 | |||
| 559 | pub fn open(path: [*]const u8, flags: u32, mode: usize) usize { | ||
| 560 | return errnoWrap(c.open(path, @bitCast(c_int, flags), mode)); | ||
| 561 | } | ||
| 562 | |||
| 563 | pub fn create(path: [*]const u8, perm: usize) usize { | ||
| 564 | return arch.syscall2(SYS_creat, @ptrToInt(path), perm); | ||
| 565 | } | ||
| 566 | |||
| 567 | pub fn openat(dirfd: i32, path: [*]const u8, flags: usize, mode: usize) usize { | ||
| 568 | return errnoWrap(c.openat(@bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode)); | ||
| 569 | } | ||
| 570 | |||
| 571 | pub fn close(fd: i32) usize { | ||
| 572 | return errnoWrap(c.close(fd)); | ||
| 573 | } | ||
| 574 | |||
| 575 | pub fn lseek(fd: i32, offset: isize, whence: c_int) usize { | ||
| 576 | return errnoWrap(c.lseek(fd, offset, whence)); | ||
| 577 | } | ||
| 578 | |||
| 579 | pub fn exit(code: i32) noreturn { | ||
| 580 | c.exit(code); | ||
| 581 | } | ||
| 582 | |||
| 583 | pub fn kill(pid: i32, sig: i32) usize { | ||
| 584 | return errnoWrap(c.kill(pid, sig)); | ||
| 585 | } | ||
| 586 | |||
| 587 | pub fn unlink(path: [*]const u8) usize { | ||
| 588 | return errnoWrap(c.unlink(path)); | ||
| 589 | } | ||
| 590 | |||
| 591 | pub fn waitpid(pid: i32, status: *i32, options: u32) usize { | ||
| 592 | comptime assert(i32.bit_count == c_int.bit_count); | ||
| 593 | return errnoWrap(c.waitpid(pid, @ptrCast(*c_int, status), @bitCast(c_int, options))); | ||
| 594 | } | ||
| 595 | |||
| 596 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | ||
| 597 | return errnoWrap(c.nanosleep(req, rem)); | ||
| 598 | } | ||
| 599 | |||
| 600 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | ||
| 601 | return errnoWrap(c.clock_gettime(clk_id, tp)); | ||
| 602 | } | ||
| 603 | |||
| 604 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | ||
| 605 | return errnoWrap(c.clock_getres(clk_id, tp)); | ||
| 606 | } | ||
| 607 | |||
| 608 | pub fn setuid(uid: u32) usize { | ||
| 609 | return errnoWrap(c.setuid(uid)); | ||
| 610 | } | ||
| 611 | |||
| 612 | pub fn setgid(gid: u32) usize { | ||
| 613 | return errnoWrap(c.setgid(gid)); | ||
| 614 | } | ||
| 615 | |||
| 616 | pub fn setreuid(ruid: u32, euid: u32) usize { | ||
| 617 | return errnoWrap(c.setreuid(ruid, euid)); | ||
| 618 | } | ||
| 619 | |||
| 620 | pub fn setregid(rgid: u32, egid: u32) usize { | ||
| 621 | return errnoWrap(c.setregid(rgid, egid)); | ||
| 622 | } | ||
| 623 | |||
| 624 | const NSIG = 32; | ||
| 625 | |||
| 626 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | ||
| 627 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | ||
| 628 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | ||
| 629 | |||
| 630 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | ||
| 631 | pub const Sigaction = extern struct { | ||
| 632 | /// signal handler | ||
| 633 | __sigaction_u: extern union { | ||
| 634 | __sa_handler: extern fn (i32) void, | ||
| 635 | __sa_sigaction: extern fn (i32, *__siginfo, usize) void, | ||
| 636 | }, | ||
| 637 | |||
| 638 | /// see signal options | ||
| 639 | sa_flags: u32, | ||
| 640 | |||
| 641 | /// signal mask to apply | ||
| 642 | sa_mask: sigset_t, | ||
| 643 | }; | ||
| 644 | |||
| 645 | pub const _SIG_WORDS = 4; | ||
| 646 | pub const _SIG_MAXSIG = 128; | ||
| 647 | |||
| 648 | pub inline fn _SIG_IDX(sig: usize) usize { | ||
| 649 | return sig - 1; | ||
| 650 | } | ||
| 651 | pub inline fn _SIG_WORD(sig: usize) usize { | ||
| 652 | return_SIG_IDX(sig) >> 5; | ||
| 653 | } | ||
| 654 | pub inline fn _SIG_BIT(sig: usize) usize { | ||
| 655 | return 1 << (_SIG_IDX(sig) & 31); | ||
| 656 | } | ||
| 657 | pub inline fn _SIG_VALID(sig: usize) usize { | ||
| 658 | return sig <= _SIG_MAXSIG and sig > 0; | ||
| 659 | } | ||
| 660 | |||
| 661 | pub const sigset_t = extern struct { | ||
| 662 | __bits: [_SIG_WORDS]u32, | ||
| 663 | }; | ||
| 664 | |||
| 665 | pub fn raise(sig: i32) usize { | ||
| 666 | return errnoWrap(c.raise(sig)); | ||
| 667 | } | ||
| 668 | |||
| 669 | pub const Stat = c.Stat; | ||
| 670 | pub const dirent = c.dirent; | ||
| 671 | pub const timespec = c.timespec; | ||
| 672 | |||
| 673 | pub fn fstat(fd: i32, buf: *c.Stat) usize { | ||
| 674 | return errnoWrap(c.fstat(fd, buf)); | ||
| 675 | } | ||
| 676 | pub const iovec = extern struct { | ||
| 677 | iov_base: [*]u8, | ||
| 678 | iov_len: usize, | ||
| 679 | }; | ||
| 680 | |||
| 681 | pub const iovec_const = extern struct { | ||
| 682 | iov_base: [*]const u8, | ||
| 683 | iov_len: usize, | ||
| 684 | }; | ||
| 685 | |||
| 686 | // TODO avoid libc dependency | ||
| 687 | pub fn kqueue() usize { | ||
| 688 | return errnoWrap(c.kqueue()); | ||
| 689 | } | ||
| 690 | |||
| 691 | // TODO avoid libc dependency | ||
| 692 | pub fn kevent(kq: i32, changelist: []const Kevent, eventlist: []Kevent, timeout: ?*const timespec) usize { | ||
| 693 | return errnoWrap(c.kevent( | ||
| 694 | kq, | ||
| 695 | changelist.ptr, | ||
| 696 | @intCast(c_int, changelist.len), | ||
| 697 | eventlist.ptr, | ||
| 698 | @intCast(c_int, eventlist.len), | ||
| 699 | timeout, | ||
| 700 | )); | ||
| 701 | } | ||
| 702 | |||
| 703 | // TODO avoid libc dependency | ||
| 704 | pub fn sysctl(name: [*]c_int, namelen: c_uint, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 705 | return errnoWrap(c.sysctl(name, namelen, oldp, oldlenp, newp, newlen)); | ||
| 706 | } | ||
| 707 | |||
| 708 | // TODO avoid libc dependency | ||
| 709 | pub fn sysctlbyname(name: [*]const u8, oldp: ?*c_void, oldlenp: ?*usize, newp: ?*c_void, newlen: usize) usize { | ||
| 710 | return errnoWrap(c.sysctlbyname(name, oldp, oldlenp, newp, newlen)); | ||
| 711 | } | ||
| 712 | |||
| 713 | // TODO avoid libc dependency | ||
| 714 | pub fn sysctlnametomib(name: [*]const u8, mibp: ?*c_int, sizep: ?*usize) usize { | ||
| 715 | return errnoWrap(c.sysctlnametomib(name, wibp, sizep)); | ||
| 716 | } | ||
| 717 | |||
| 718 | // TODO avoid libc dependency | ||
| 719 | |||
| 720 | /// Takes the return value from a syscall and formats it back in the way | ||
| 721 | /// that the kernel represents it to libc. Errno was a mistake, let's make | ||
| 722 | /// it go away forever. | ||
| 723 | fn errnoWrap(value: isize) usize { | ||
| 724 | return @bitCast(usize, if (value == -1) -isize(c._errno().*) else value); | ||
| 725 | } | ||
std/os/path.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Os = builtin.Os; | 3 | const Os = builtin.Os; |
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
std/os/test.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const os = std.os; | 2 | const os = std.os; |
| 3 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 4 | const io = std.io; | 4 | const io = std.io; |
std/os/time.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const Os = builtin.Os; | 3 | const Os = builtin.Os; |
| 4 | const debug = std.debug; | 4 | const debug = std.debug; |
std/os/windows.zig created+399| ... | @@ -0,0 +1,399 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const maxInt = std.math.maxInt; | ||
| 4 | |||
| 5 | pub use @import("windows/advapi32.zig"); | ||
| 6 | pub use @import("windows/kernel32.zig"); | ||
| 7 | pub use @import("windows/ntdll.zig"); | ||
| 8 | pub use @import("windows/ole32.zig"); | ||
| 9 | pub use @import("windows/shell32.zig"); | ||
| 10 | |||
| 11 | test "import" { | ||
| 12 | _ = @import("windows/util.zig"); | ||
| 13 | } | ||
| 14 | |||
| 15 | pub const ERROR = @import("windows/error.zig"); | ||
| 16 | |||
| 17 | pub const SHORT = c_short; | ||
| 18 | pub const BOOL = c_int; | ||
| 19 | pub const BOOLEAN = BYTE; | ||
| 20 | pub const BYTE = u8; | ||
| 21 | pub const CHAR = u8; | ||
| 22 | pub const DWORD = u32; | ||
| 23 | pub const FLOAT = f32; | ||
| 24 | pub const HANDLE = *c_void; | ||
| 25 | pub const HCRYPTPROV = ULONG_PTR; | ||
| 26 | pub const HINSTANCE = *@OpaqueType(); | ||
| 27 | pub const HMODULE = *@OpaqueType(); | ||
| 28 | pub const FARPROC = *@OpaqueType(); | ||
| 29 | pub const INT = c_int; | ||
| 30 | pub const LPBYTE = *BYTE; | ||
| 31 | pub const LPCH = *CHAR; | ||
| 32 | pub const LPCSTR = [*]const CHAR; | ||
| 33 | pub const LPCTSTR = [*]const TCHAR; | ||
| 34 | pub const LPCVOID = *const c_void; | ||
| 35 | pub const LPDWORD = *DWORD; | ||
| 36 | pub const LPSTR = [*]CHAR; | ||
| 37 | pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; | ||
| 38 | pub const LPVOID = *c_void; | ||
| 39 | pub const LPWSTR = [*]WCHAR; | ||
| 40 | pub const LPCWSTR = [*]const WCHAR; | ||
| 41 | pub const PVOID = *c_void; | ||
| 42 | pub const PWSTR = [*]WCHAR; | ||
| 43 | pub const SIZE_T = usize; | ||
| 44 | pub const TCHAR = if (UNICODE) WCHAR else u8; | ||
| 45 | pub const UINT = c_uint; | ||
| 46 | pub const ULONG_PTR = usize; | ||
| 47 | pub const DWORD_PTR = ULONG_PTR; | ||
| 48 | pub const UNICODE = false; | ||
| 49 | pub const WCHAR = u16; | ||
| 50 | pub const WORD = u16; | ||
| 51 | pub const LARGE_INTEGER = i64; | ||
| 52 | pub const ULONG = u32; | ||
| 53 | pub const LONG = i32; | ||
| 54 | pub const ULONGLONG = u64; | ||
| 55 | pub const LONGLONG = i64; | ||
| 56 | |||
| 57 | pub const TRUE = 1; | ||
| 58 | pub const FALSE = 0; | ||
| 59 | |||
| 60 | /// The standard input device. Initially, this is the console input buffer, CONIN$. | ||
| 61 | pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; | ||
| 62 | |||
| 63 | /// The standard output device. Initially, this is the active console screen buffer, CONOUT$. | ||
| 64 | pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; | ||
| 65 | |||
| 66 | /// The standard error device. Initially, this is the active console screen buffer, CONOUT$. | ||
| 67 | pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; | ||
| 68 | |||
| 69 | pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); | ||
| 70 | |||
| 71 | pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); | ||
| 72 | |||
| 73 | pub const OVERLAPPED = extern struct { | ||
| 74 | Internal: ULONG_PTR, | ||
| 75 | InternalHigh: ULONG_PTR, | ||
| 76 | Offset: DWORD, | ||
| 77 | OffsetHigh: DWORD, | ||
| 78 | hEvent: ?HANDLE, | ||
| 79 | }; | ||
| 80 | pub const LPOVERLAPPED = *OVERLAPPED; | ||
| 81 | |||
| 82 | pub const MAX_PATH = 260; | ||
| 83 | |||
| 84 | // TODO issue #305 | ||
| 85 | pub const FILE_INFO_BY_HANDLE_CLASS = u32; | ||
| 86 | pub const FileBasicInfo = 0; | ||
| 87 | pub const FileStandardInfo = 1; | ||
| 88 | pub const FileNameInfo = 2; | ||
| 89 | pub const FileRenameInfo = 3; | ||
| 90 | pub const FileDispositionInfo = 4; | ||
| 91 | pub const FileAllocationInfo = 5; | ||
| 92 | pub const FileEndOfFileInfo = 6; | ||
| 93 | pub const FileStreamInfo = 7; | ||
| 94 | pub const FileCompressionInfo = 8; | ||
| 95 | pub const FileAttributeTagInfo = 9; | ||
| 96 | pub const FileIdBothDirectoryInfo = 10; | ||
| 97 | pub const FileIdBothDirectoryRestartInfo = 11; | ||
| 98 | pub const FileIoPriorityHintInfo = 12; | ||
| 99 | pub const FileRemoteProtocolInfo = 13; | ||
| 100 | pub const FileFullDirectoryInfo = 14; | ||
| 101 | pub const FileFullDirectoryRestartInfo = 15; | ||
| 102 | pub const FileStorageInfo = 16; | ||
| 103 | pub const FileAlignmentInfo = 17; | ||
| 104 | pub const FileIdInfo = 18; | ||
| 105 | pub const FileIdExtdDirectoryInfo = 19; | ||
| 106 | pub const FileIdExtdDirectoryRestartInfo = 20; | ||
| 107 | |||
| 108 | pub const FILE_NAME_INFO = extern struct { | ||
| 109 | FileNameLength: DWORD, | ||
| 110 | FileName: [1]WCHAR, | ||
| 111 | }; | ||
| 112 | |||
| 113 | /// Return the normalized drive name. This is the default. | ||
| 114 | pub const FILE_NAME_NORMALIZED = 0x0; | ||
| 115 | |||
| 116 | /// Return the opened file name (not normalized). | ||
| 117 | pub const FILE_NAME_OPENED = 0x8; | ||
| 118 | |||
| 119 | /// Return the path with the drive letter. This is the default. | ||
| 120 | pub const VOLUME_NAME_DOS = 0x0; | ||
| 121 | |||
| 122 | /// Return the path with a volume GUID path instead of the drive name. | ||
| 123 | pub const VOLUME_NAME_GUID = 0x1; | ||
| 124 | |||
| 125 | /// Return the path with no drive information. | ||
| 126 | pub const VOLUME_NAME_NONE = 0x4; | ||
| 127 | |||
| 128 | /// Return the path with the volume device path. | ||
| 129 | pub const VOLUME_NAME_NT = 0x2; | ||
| 130 | |||
| 131 | pub const SECURITY_ATTRIBUTES = extern struct { | ||
| 132 | nLength: DWORD, | ||
| 133 | lpSecurityDescriptor: ?*c_void, | ||
| 134 | bInheritHandle: BOOL, | ||
| 135 | }; | ||
| 136 | pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; | ||
| 137 | pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; | ||
| 138 | |||
| 139 | pub const GENERIC_READ = 0x80000000; | ||
| 140 | pub const GENERIC_WRITE = 0x40000000; | ||
| 141 | pub const GENERIC_EXECUTE = 0x20000000; | ||
| 142 | pub const GENERIC_ALL = 0x10000000; | ||
| 143 | |||
| 144 | pub const FILE_SHARE_DELETE = 0x00000004; | ||
| 145 | pub const FILE_SHARE_READ = 0x00000001; | ||
| 146 | pub const FILE_SHARE_WRITE = 0x00000002; | ||
| 147 | |||
| 148 | pub const CREATE_ALWAYS = 2; | ||
| 149 | pub const CREATE_NEW = 1; | ||
| 150 | pub const OPEN_ALWAYS = 4; | ||
| 151 | pub const OPEN_EXISTING = 3; | ||
| 152 | pub const TRUNCATE_EXISTING = 5; | ||
| 153 | |||
| 154 | pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; | ||
| 155 | pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; | ||
| 156 | pub const FILE_ATTRIBUTE_DEVICE = 0x40; | ||
| 157 | pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; | ||
| 158 | pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; | ||
| 159 | pub const FILE_ATTRIBUTE_HIDDEN = 0x2; | ||
| 160 | pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; | ||
| 161 | pub const FILE_ATTRIBUTE_NORMAL = 0x80; | ||
| 162 | pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; | ||
| 163 | pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; | ||
| 164 | pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; | ||
| 165 | pub const FILE_ATTRIBUTE_READONLY = 0x1; | ||
| 166 | pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; | ||
| 167 | pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; | ||
| 168 | pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; | ||
| 169 | pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; | ||
| 170 | pub const FILE_ATTRIBUTE_SYSTEM = 0x4; | ||
| 171 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; | ||
| 172 | pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; | ||
| 173 | |||
| 174 | pub const PROCESS_INFORMATION = extern struct { | ||
| 175 | hProcess: HANDLE, | ||
| 176 | hThread: HANDLE, | ||
| 177 | dwProcessId: DWORD, | ||
| 178 | dwThreadId: DWORD, | ||
| 179 | }; | ||
| 180 | |||
| 181 | pub const STARTUPINFOW = extern struct { | ||
| 182 | cb: DWORD, | ||
| 183 | lpReserved: ?LPWSTR, | ||
| 184 | lpDesktop: ?LPWSTR, | ||
| 185 | lpTitle: ?LPWSTR, | ||
| 186 | dwX: DWORD, | ||
| 187 | dwY: DWORD, | ||
| 188 | dwXSize: DWORD, | ||
| 189 | dwYSize: DWORD, | ||
| 190 | dwXCountChars: DWORD, | ||
| 191 | dwYCountChars: DWORD, | ||
| 192 | dwFillAttribute: DWORD, | ||
| 193 | dwFlags: DWORD, | ||
| 194 | wShowWindow: WORD, | ||
| 195 | cbReserved2: WORD, | ||
| 196 | lpReserved2: ?LPBYTE, | ||
| 197 | hStdInput: ?HANDLE, | ||
| 198 | hStdOutput: ?HANDLE, | ||
| 199 | hStdError: ?HANDLE, | ||
| 200 | }; | ||
| 201 | |||
| 202 | pub const STARTF_FORCEONFEEDBACK = 0x00000040; | ||
| 203 | pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; | ||
| 204 | pub const STARTF_PREVENTPINNING = 0x00002000; | ||
| 205 | pub const STARTF_RUNFULLSCREEN = 0x00000020; | ||
| 206 | pub const STARTF_TITLEISAPPID = 0x00001000; | ||
| 207 | pub const STARTF_TITLEISLINKNAME = 0x00000800; | ||
| 208 | pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; | ||
| 209 | pub const STARTF_USECOUNTCHARS = 0x00000008; | ||
| 210 | pub const STARTF_USEFILLATTRIBUTE = 0x00000010; | ||
| 211 | pub const STARTF_USEHOTKEY = 0x00000200; | ||
| 212 | pub const STARTF_USEPOSITION = 0x00000004; | ||
| 213 | pub const STARTF_USESHOWWINDOW = 0x00000001; | ||
| 214 | pub const STARTF_USESIZE = 0x00000002; | ||
| 215 | pub const STARTF_USESTDHANDLES = 0x00000100; | ||
| 216 | |||
| 217 | pub const INFINITE = 4294967295; | ||
| 218 | |||
| 219 | pub const WAIT_ABANDONED = 0x00000080; | ||
| 220 | pub const WAIT_OBJECT_0 = 0x00000000; | ||
| 221 | pub const WAIT_TIMEOUT = 0x00000102; | ||
| 222 | pub const WAIT_FAILED = 0xFFFFFFFF; | ||
| 223 | |||
| 224 | pub const HANDLE_FLAG_INHERIT = 0x00000001; | ||
| 225 | pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; | ||
| 226 | |||
| 227 | pub const MOVEFILE_COPY_ALLOWED = 2; | ||
| 228 | pub const MOVEFILE_CREATE_HARDLINK = 16; | ||
| 229 | pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; | ||
| 230 | pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; | ||
| 231 | pub const MOVEFILE_REPLACE_EXISTING = 1; | ||
| 232 | pub const MOVEFILE_WRITE_THROUGH = 8; | ||
| 233 | |||
| 234 | pub const FILE_BEGIN = 0; | ||
| 235 | pub const FILE_CURRENT = 1; | ||
| 236 | pub const FILE_END = 2; | ||
| 237 | |||
| 238 | pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; | ||
| 239 | pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; | ||
| 240 | pub const HEAP_NO_SERIALIZE = 0x00000001; | ||
| 241 | |||
| 242 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; | ||
| 243 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; | ||
| 244 | |||
| 245 | pub const WIN32_FIND_DATAW = extern struct { | ||
| 246 | dwFileAttributes: DWORD, | ||
| 247 | ftCreationTime: FILETIME, | ||
| 248 | ftLastAccessTime: FILETIME, | ||
| 249 | ftLastWriteTime: FILETIME, | ||
| 250 | nFileSizeHigh: DWORD, | ||
| 251 | nFileSizeLow: DWORD, | ||
| 252 | dwReserved0: DWORD, | ||
| 253 | dwReserved1: DWORD, | ||
| 254 | cFileName: [260]u16, | ||
| 255 | cAlternateFileName: [14]u16, | ||
| 256 | }; | ||
| 257 | |||
| 258 | pub const FILETIME = extern struct { | ||
| 259 | dwLowDateTime: DWORD, | ||
| 260 | dwHighDateTime: DWORD, | ||
| 261 | }; | ||
| 262 | |||
| 263 | pub const SYSTEM_INFO = extern struct { | ||
| 264 | anon1: extern union { | ||
| 265 | dwOemId: DWORD, | ||
| 266 | anon2: extern struct { | ||
| 267 | wProcessorArchitecture: WORD, | ||
| 268 | wReserved: WORD, | ||
| 269 | }, | ||
| 270 | }, | ||
| 271 | dwPageSize: DWORD, | ||
| 272 | lpMinimumApplicationAddress: LPVOID, | ||
| 273 | lpMaximumApplicationAddress: LPVOID, | ||
| 274 | dwActiveProcessorMask: DWORD_PTR, | ||
| 275 | dwNumberOfProcessors: DWORD, | ||
| 276 | dwProcessorType: DWORD, | ||
| 277 | dwAllocationGranularity: DWORD, | ||
| 278 | wProcessorLevel: WORD, | ||
| 279 | wProcessorRevision: WORD, | ||
| 280 | }; | ||
| 281 | |||
| 282 | pub const HRESULT = c_long; | ||
| 283 | |||
| 284 | pub const KNOWNFOLDERID = GUID; | ||
| 285 | pub const GUID = extern struct { | ||
| 286 | Data1: c_ulong, | ||
| 287 | Data2: c_ushort, | ||
| 288 | Data3: c_ushort, | ||
| 289 | Data4: [8]u8, | ||
| 290 | |||
| 291 | pub fn parse(str: []const u8) GUID { | ||
| 292 | var guid: GUID = undefined; | ||
| 293 | var index: usize = 0; | ||
| 294 | assert(str[index] == '{'); | ||
| 295 | index += 1; | ||
| 296 | |||
| 297 | guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; | ||
| 298 | index += 8; | ||
| 299 | |||
| 300 | assert(str[index] == '-'); | ||
| 301 | index += 1; | ||
| 302 | |||
| 303 | guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; | ||
| 304 | index += 4; | ||
| 305 | |||
| 306 | assert(str[index] == '-'); | ||
| 307 | index += 1; | ||
| 308 | |||
| 309 | guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; | ||
| 310 | index += 4; | ||
| 311 | |||
| 312 | assert(str[index] == '-'); | ||
| 313 | index += 1; | ||
| 314 | |||
| 315 | guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; | ||
| 316 | index += 2; | ||
| 317 | guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; | ||
| 318 | index += 2; | ||
| 319 | |||
| 320 | assert(str[index] == '-'); | ||
| 321 | index += 1; | ||
| 322 | |||
| 323 | var i: usize = 2; | ||
| 324 | while (i < guid.Data4.len) : (i += 1) { | ||
| 325 | guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; | ||
| 326 | index += 2; | ||
| 327 | } | ||
| 328 | |||
| 329 | assert(str[index] == '}'); | ||
| 330 | index += 1; | ||
| 331 | return guid; | ||
| 332 | } | ||
| 333 | }; | ||
| 334 | |||
| 335 | pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); | ||
| 336 | |||
| 337 | pub const KF_FLAG_DEFAULT = 0; | ||
| 338 | pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; | ||
| 339 | pub const KF_FLAG_CREATE = 32768; | ||
| 340 | pub const KF_FLAG_DONT_VERIFY = 16384; | ||
| 341 | pub const KF_FLAG_DONT_UNEXPAND = 8192; | ||
| 342 | pub const KF_FLAG_NO_ALIAS = 4096; | ||
| 343 | pub const KF_FLAG_INIT = 2048; | ||
| 344 | pub const KF_FLAG_DEFAULT_PATH = 1024; | ||
| 345 | pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; | ||
| 346 | pub const KF_FLAG_SIMPLE_IDLIST = 256; | ||
| 347 | pub const KF_FLAG_ALIAS_ONLY = -2147483648; | ||
| 348 | |||
| 349 | pub const S_OK = 0; | ||
| 350 | pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); | ||
| 351 | pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); | ||
| 352 | pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); | ||
| 353 | pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); | ||
| 354 | pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); | ||
| 355 | pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); | ||
| 356 | pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); | ||
| 357 | pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); | ||
| 358 | pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); | ||
| 359 | pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); | ||
| 360 | |||
| 361 | pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; | ||
| 362 | pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; | ||
| 363 | pub const FILE_FLAG_NO_BUFFERING = 0x20000000; | ||
| 364 | pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; | ||
| 365 | pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; | ||
| 366 | pub const FILE_FLAG_OVERLAPPED = 0x40000000; | ||
| 367 | pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; | ||
| 368 | pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; | ||
| 369 | pub const FILE_FLAG_SESSION_AWARE = 0x00800000; | ||
| 370 | pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; | ||
| 371 | pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; | ||
| 372 | |||
| 373 | pub const SMALL_RECT = extern struct { | ||
| 374 | Left: SHORT, | ||
| 375 | Top: SHORT, | ||
| 376 | Right: SHORT, | ||
| 377 | Bottom: SHORT, | ||
| 378 | }; | ||
| 379 | |||
| 380 | pub const COORD = extern struct { | ||
| 381 | X: SHORT, | ||
| 382 | Y: SHORT, | ||
| 383 | }; | ||
| 384 | |||
| 385 | pub const CREATE_UNICODE_ENVIRONMENT = 1024; | ||
| 386 | |||
| 387 | pub const TLS_OUT_OF_INDEXES = 4294967295; | ||
| 388 | pub const IMAGE_TLS_DIRECTORY = extern struct { | ||
| 389 | StartAddressOfRawData: usize, | ||
| 390 | EndAddressOfRawData: usize, | ||
| 391 | AddressOfIndex: usize, | ||
| 392 | AddressOfCallBacks: usize, | ||
| 393 | SizeOfZeroFill: u32, | ||
| 394 | Characteristics: u32, | ||
| 395 | }; | ||
| 396 | pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; | ||
| 397 | pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; | ||
| 398 | |||
| 399 | pub const PIMAGE_TLS_CALLBACK = ?extern fn (PVOID, DWORD, PVOID) void; | ||
std/os/windows/advapi32.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | use @import("index.zig"); | 1 | use @import("../windows.zig"); |
| 2 | 2 | ||
| 3 | pub const PROV_RSA_FULL = 1; | 3 | pub const PROV_RSA_FULL = 1; |
| 4 | 4 |
std/os/windows/index.zig deleted-399| ... | @@ -1,399 +0,0 @@ | ||
| 1 | const std = @import("../../index.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const maxInt = std.math.maxInt; | ||
| 4 | |||
| 5 | pub use @import("advapi32.zig"); | ||
| 6 | pub use @import("kernel32.zig"); | ||
| 7 | pub use @import("ntdll.zig"); | ||
| 8 | pub use @import("ole32.zig"); | ||
| 9 | pub use @import("shell32.zig"); | ||
| 10 | |||
| 11 | test "import" { | ||
| 12 | _ = @import("util.zig"); | ||
| 13 | } | ||
| 14 | |||
| 15 | pub const ERROR = @import("error.zig"); | ||
| 16 | |||
| 17 | pub const SHORT = c_short; | ||
| 18 | pub const BOOL = c_int; | ||
| 19 | pub const BOOLEAN = BYTE; | ||
| 20 | pub const BYTE = u8; | ||
| 21 | pub const CHAR = u8; | ||
| 22 | pub const DWORD = u32; | ||
| 23 | pub const FLOAT = f32; | ||
| 24 | pub const HANDLE = *c_void; | ||
| 25 | pub const HCRYPTPROV = ULONG_PTR; | ||
| 26 | pub const HINSTANCE = *@OpaqueType(); | ||
| 27 | pub const HMODULE = *@OpaqueType(); | ||
| 28 | pub const FARPROC = *@OpaqueType(); | ||
| 29 | pub const INT = c_int; | ||
| 30 | pub const LPBYTE = *BYTE; | ||
| 31 | pub const LPCH = *CHAR; | ||
| 32 | pub const LPCSTR = [*]const CHAR; | ||
| 33 | pub const LPCTSTR = [*]const TCHAR; | ||
| 34 | pub const LPCVOID = *const c_void; | ||
| 35 | pub const LPDWORD = *DWORD; | ||
| 36 | pub const LPSTR = [*]CHAR; | ||
| 37 | pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR; | ||
| 38 | pub const LPVOID = *c_void; | ||
| 39 | pub const LPWSTR = [*]WCHAR; | ||
| 40 | pub const LPCWSTR = [*]const WCHAR; | ||
| 41 | pub const PVOID = *c_void; | ||
| 42 | pub const PWSTR = [*]WCHAR; | ||
| 43 | pub const SIZE_T = usize; | ||
| 44 | pub const TCHAR = if (UNICODE) WCHAR else u8; | ||
| 45 | pub const UINT = c_uint; | ||
| 46 | pub const ULONG_PTR = usize; | ||
| 47 | pub const DWORD_PTR = ULONG_PTR; | ||
| 48 | pub const UNICODE = false; | ||
| 49 | pub const WCHAR = u16; | ||
| 50 | pub const WORD = u16; | ||
| 51 | pub const LARGE_INTEGER = i64; | ||
| 52 | pub const ULONG = u32; | ||
| 53 | pub const LONG = i32; | ||
| 54 | pub const ULONGLONG = u64; | ||
| 55 | pub const LONGLONG = i64; | ||
| 56 | |||
| 57 | pub const TRUE = 1; | ||
| 58 | pub const FALSE = 0; | ||
| 59 | |||
| 60 | /// The standard input device. Initially, this is the console input buffer, CONIN$. | ||
| 61 | pub const STD_INPUT_HANDLE = maxInt(DWORD) - 10 + 1; | ||
| 62 | |||
| 63 | /// The standard output device. Initially, this is the active console screen buffer, CONOUT$. | ||
| 64 | pub const STD_OUTPUT_HANDLE = maxInt(DWORD) - 11 + 1; | ||
| 65 | |||
| 66 | /// The standard error device. Initially, this is the active console screen buffer, CONOUT$. | ||
| 67 | pub const STD_ERROR_HANDLE = maxInt(DWORD) - 12 + 1; | ||
| 68 | |||
| 69 | pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, maxInt(usize)); | ||
| 70 | |||
| 71 | pub const INVALID_FILE_ATTRIBUTES = DWORD(maxInt(DWORD)); | ||
| 72 | |||
| 73 | pub const OVERLAPPED = extern struct { | ||
| 74 | Internal: ULONG_PTR, | ||
| 75 | InternalHigh: ULONG_PTR, | ||
| 76 | Offset: DWORD, | ||
| 77 | OffsetHigh: DWORD, | ||
| 78 | hEvent: ?HANDLE, | ||
| 79 | }; | ||
| 80 | pub const LPOVERLAPPED = *OVERLAPPED; | ||
| 81 | |||
| 82 | pub const MAX_PATH = 260; | ||
| 83 | |||
| 84 | // TODO issue #305 | ||
| 85 | pub const FILE_INFO_BY_HANDLE_CLASS = u32; | ||
| 86 | pub const FileBasicInfo = 0; | ||
| 87 | pub const FileStandardInfo = 1; | ||
| 88 | pub const FileNameInfo = 2; | ||
| 89 | pub const FileRenameInfo = 3; | ||
| 90 | pub const FileDispositionInfo = 4; | ||
| 91 | pub const FileAllocationInfo = 5; | ||
| 92 | pub const FileEndOfFileInfo = 6; | ||
| 93 | pub const FileStreamInfo = 7; | ||
| 94 | pub const FileCompressionInfo = 8; | ||
| 95 | pub const FileAttributeTagInfo = 9; | ||
| 96 | pub const FileIdBothDirectoryInfo = 10; | ||
| 97 | pub const FileIdBothDirectoryRestartInfo = 11; | ||
| 98 | pub const FileIoPriorityHintInfo = 12; | ||
| 99 | pub const FileRemoteProtocolInfo = 13; | ||
| 100 | pub const FileFullDirectoryInfo = 14; | ||
| 101 | pub const FileFullDirectoryRestartInfo = 15; | ||
| 102 | pub const FileStorageInfo = 16; | ||
| 103 | pub const FileAlignmentInfo = 17; | ||
| 104 | pub const FileIdInfo = 18; | ||
| 105 | pub const FileIdExtdDirectoryInfo = 19; | ||
| 106 | pub const FileIdExtdDirectoryRestartInfo = 20; | ||
| 107 | |||
| 108 | pub const FILE_NAME_INFO = extern struct { | ||
| 109 | FileNameLength: DWORD, | ||
| 110 | FileName: [1]WCHAR, | ||
| 111 | }; | ||
| 112 | |||
| 113 | /// Return the normalized drive name. This is the default. | ||
| 114 | pub const FILE_NAME_NORMALIZED = 0x0; | ||
| 115 | |||
| 116 | /// Return the opened file name (not normalized). | ||
| 117 | pub const FILE_NAME_OPENED = 0x8; | ||
| 118 | |||
| 119 | /// Return the path with the drive letter. This is the default. | ||
| 120 | pub const VOLUME_NAME_DOS = 0x0; | ||
| 121 | |||
| 122 | /// Return the path with a volume GUID path instead of the drive name. | ||
| 123 | pub const VOLUME_NAME_GUID = 0x1; | ||
| 124 | |||
| 125 | /// Return the path with no drive information. | ||
| 126 | pub const VOLUME_NAME_NONE = 0x4; | ||
| 127 | |||
| 128 | /// Return the path with the volume device path. | ||
| 129 | pub const VOLUME_NAME_NT = 0x2; | ||
| 130 | |||
| 131 | pub const SECURITY_ATTRIBUTES = extern struct { | ||
| 132 | nLength: DWORD, | ||
| 133 | lpSecurityDescriptor: ?*c_void, | ||
| 134 | bInheritHandle: BOOL, | ||
| 135 | }; | ||
| 136 | pub const PSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; | ||
| 137 | pub const LPSECURITY_ATTRIBUTES = *SECURITY_ATTRIBUTES; | ||
| 138 | |||
| 139 | pub const GENERIC_READ = 0x80000000; | ||
| 140 | pub const GENERIC_WRITE = 0x40000000; | ||
| 141 | pub const GENERIC_EXECUTE = 0x20000000; | ||
| 142 | pub const GENERIC_ALL = 0x10000000; | ||
| 143 | |||
| 144 | pub const FILE_SHARE_DELETE = 0x00000004; | ||
| 145 | pub const FILE_SHARE_READ = 0x00000001; | ||
| 146 | pub const FILE_SHARE_WRITE = 0x00000002; | ||
| 147 | |||
| 148 | pub const CREATE_ALWAYS = 2; | ||
| 149 | pub const CREATE_NEW = 1; | ||
| 150 | pub const OPEN_ALWAYS = 4; | ||
| 151 | pub const OPEN_EXISTING = 3; | ||
| 152 | pub const TRUNCATE_EXISTING = 5; | ||
| 153 | |||
| 154 | pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; | ||
| 155 | pub const FILE_ATTRIBUTE_COMPRESSED = 0x800; | ||
| 156 | pub const FILE_ATTRIBUTE_DEVICE = 0x40; | ||
| 157 | pub const FILE_ATTRIBUTE_DIRECTORY = 0x10; | ||
| 158 | pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; | ||
| 159 | pub const FILE_ATTRIBUTE_HIDDEN = 0x2; | ||
| 160 | pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000; | ||
| 161 | pub const FILE_ATTRIBUTE_NORMAL = 0x80; | ||
| 162 | pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000; | ||
| 163 | pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000; | ||
| 164 | pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; | ||
| 165 | pub const FILE_ATTRIBUTE_READONLY = 0x1; | ||
| 166 | pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000; | ||
| 167 | pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000; | ||
| 168 | pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400; | ||
| 169 | pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200; | ||
| 170 | pub const FILE_ATTRIBUTE_SYSTEM = 0x4; | ||
| 171 | pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; | ||
| 172 | pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000; | ||
| 173 | |||
| 174 | pub const PROCESS_INFORMATION = extern struct { | ||
| 175 | hProcess: HANDLE, | ||
| 176 | hThread: HANDLE, | ||
| 177 | dwProcessId: DWORD, | ||
| 178 | dwThreadId: DWORD, | ||
| 179 | }; | ||
| 180 | |||
| 181 | pub const STARTUPINFOW = extern struct { | ||
| 182 | cb: DWORD, | ||
| 183 | lpReserved: ?LPWSTR, | ||
| 184 | lpDesktop: ?LPWSTR, | ||
| 185 | lpTitle: ?LPWSTR, | ||
| 186 | dwX: DWORD, | ||
| 187 | dwY: DWORD, | ||
| 188 | dwXSize: DWORD, | ||
| 189 | dwYSize: DWORD, | ||
| 190 | dwXCountChars: DWORD, | ||
| 191 | dwYCountChars: DWORD, | ||
| 192 | dwFillAttribute: DWORD, | ||
| 193 | dwFlags: DWORD, | ||
| 194 | wShowWindow: WORD, | ||
| 195 | cbReserved2: WORD, | ||
| 196 | lpReserved2: ?LPBYTE, | ||
| 197 | hStdInput: ?HANDLE, | ||
| 198 | hStdOutput: ?HANDLE, | ||
| 199 | hStdError: ?HANDLE, | ||
| 200 | }; | ||
| 201 | |||
| 202 | pub const STARTF_FORCEONFEEDBACK = 0x00000040; | ||
| 203 | pub const STARTF_FORCEOFFFEEDBACK = 0x00000080; | ||
| 204 | pub const STARTF_PREVENTPINNING = 0x00002000; | ||
| 205 | pub const STARTF_RUNFULLSCREEN = 0x00000020; | ||
| 206 | pub const STARTF_TITLEISAPPID = 0x00001000; | ||
| 207 | pub const STARTF_TITLEISLINKNAME = 0x00000800; | ||
| 208 | pub const STARTF_UNTRUSTEDSOURCE = 0x00008000; | ||
| 209 | pub const STARTF_USECOUNTCHARS = 0x00000008; | ||
| 210 | pub const STARTF_USEFILLATTRIBUTE = 0x00000010; | ||
| 211 | pub const STARTF_USEHOTKEY = 0x00000200; | ||
| 212 | pub const STARTF_USEPOSITION = 0x00000004; | ||
| 213 | pub const STARTF_USESHOWWINDOW = 0x00000001; | ||
| 214 | pub const STARTF_USESIZE = 0x00000002; | ||
| 215 | pub const STARTF_USESTDHANDLES = 0x00000100; | ||
| 216 | |||
| 217 | pub const INFINITE = 4294967295; | ||
| 218 | |||
| 219 | pub const WAIT_ABANDONED = 0x00000080; | ||
| 220 | pub const WAIT_OBJECT_0 = 0x00000000; | ||
| 221 | pub const WAIT_TIMEOUT = 0x00000102; | ||
| 222 | pub const WAIT_FAILED = 0xFFFFFFFF; | ||
| 223 | |||
| 224 | pub const HANDLE_FLAG_INHERIT = 0x00000001; | ||
| 225 | pub const HANDLE_FLAG_PROTECT_FROM_CLOSE = 0x00000002; | ||
| 226 | |||
| 227 | pub const MOVEFILE_COPY_ALLOWED = 2; | ||
| 228 | pub const MOVEFILE_CREATE_HARDLINK = 16; | ||
| 229 | pub const MOVEFILE_DELAY_UNTIL_REBOOT = 4; | ||
| 230 | pub const MOVEFILE_FAIL_IF_NOT_TRACKABLE = 32; | ||
| 231 | pub const MOVEFILE_REPLACE_EXISTING = 1; | ||
| 232 | pub const MOVEFILE_WRITE_THROUGH = 8; | ||
| 233 | |||
| 234 | pub const FILE_BEGIN = 0; | ||
| 235 | pub const FILE_CURRENT = 1; | ||
| 236 | pub const FILE_END = 2; | ||
| 237 | |||
| 238 | pub const HEAP_CREATE_ENABLE_EXECUTE = 0x00040000; | ||
| 239 | pub const HEAP_GENERATE_EXCEPTIONS = 0x00000004; | ||
| 240 | pub const HEAP_NO_SERIALIZE = 0x00000001; | ||
| 241 | |||
| 242 | pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; | ||
| 243 | pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; | ||
| 244 | |||
| 245 | pub const WIN32_FIND_DATAW = extern struct { | ||
| 246 | dwFileAttributes: DWORD, | ||
| 247 | ftCreationTime: FILETIME, | ||
| 248 | ftLastAccessTime: FILETIME, | ||
| 249 | ftLastWriteTime: FILETIME, | ||
| 250 | nFileSizeHigh: DWORD, | ||
| 251 | nFileSizeLow: DWORD, | ||
| 252 | dwReserved0: DWORD, | ||
| 253 | dwReserved1: DWORD, | ||
| 254 | cFileName: [260]u16, | ||
| 255 | cAlternateFileName: [14]u16, | ||
| 256 | }; | ||
| 257 | |||
| 258 | pub const FILETIME = extern struct { | ||
| 259 | dwLowDateTime: DWORD, | ||
| 260 | dwHighDateTime: DWORD, | ||
| 261 | }; | ||
| 262 | |||
| 263 | pub const SYSTEM_INFO = extern struct { | ||
| 264 | anon1: extern union { | ||
| 265 | dwOemId: DWORD, | ||
| 266 | anon2: extern struct { | ||
| 267 | wProcessorArchitecture: WORD, | ||
| 268 | wReserved: WORD, | ||
| 269 | }, | ||
| 270 | }, | ||
| 271 | dwPageSize: DWORD, | ||
| 272 | lpMinimumApplicationAddress: LPVOID, | ||
| 273 | lpMaximumApplicationAddress: LPVOID, | ||
| 274 | dwActiveProcessorMask: DWORD_PTR, | ||
| 275 | dwNumberOfProcessors: DWORD, | ||
| 276 | dwProcessorType: DWORD, | ||
| 277 | dwAllocationGranularity: DWORD, | ||
| 278 | wProcessorLevel: WORD, | ||
| 279 | wProcessorRevision: WORD, | ||
| 280 | }; | ||
| 281 | |||
| 282 | pub const HRESULT = c_long; | ||
| 283 | |||
| 284 | pub const KNOWNFOLDERID = GUID; | ||
| 285 | pub const GUID = extern struct { | ||
| 286 | Data1: c_ulong, | ||
| 287 | Data2: c_ushort, | ||
| 288 | Data3: c_ushort, | ||
| 289 | Data4: [8]u8, | ||
| 290 | |||
| 291 | pub fn parse(str: []const u8) GUID { | ||
| 292 | var guid: GUID = undefined; | ||
| 293 | var index: usize = 0; | ||
| 294 | assert(str[index] == '{'); | ||
| 295 | index += 1; | ||
| 296 | |||
| 297 | guid.Data1 = std.fmt.parseUnsigned(c_ulong, str[index .. index + 8], 16) catch unreachable; | ||
| 298 | index += 8; | ||
| 299 | |||
| 300 | assert(str[index] == '-'); | ||
| 301 | index += 1; | ||
| 302 | |||
| 303 | guid.Data2 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; | ||
| 304 | index += 4; | ||
| 305 | |||
| 306 | assert(str[index] == '-'); | ||
| 307 | index += 1; | ||
| 308 | |||
| 309 | guid.Data3 = std.fmt.parseUnsigned(c_ushort, str[index .. index + 4], 16) catch unreachable; | ||
| 310 | index += 4; | ||
| 311 | |||
| 312 | assert(str[index] == '-'); | ||
| 313 | index += 1; | ||
| 314 | |||
| 315 | guid.Data4[0] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; | ||
| 316 | index += 2; | ||
| 317 | guid.Data4[1] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; | ||
| 318 | index += 2; | ||
| 319 | |||
| 320 | assert(str[index] == '-'); | ||
| 321 | index += 1; | ||
| 322 | |||
| 323 | var i: usize = 2; | ||
| 324 | while (i < guid.Data4.len) : (i += 1) { | ||
| 325 | guid.Data4[i] = std.fmt.parseUnsigned(u8, str[index .. index + 2], 16) catch unreachable; | ||
| 326 | index += 2; | ||
| 327 | } | ||
| 328 | |||
| 329 | assert(str[index] == '}'); | ||
| 330 | index += 1; | ||
| 331 | return guid; | ||
| 332 | } | ||
| 333 | }; | ||
| 334 | |||
| 335 | pub const FOLDERID_LocalAppData = GUID.parse("{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}"); | ||
| 336 | |||
| 337 | pub const KF_FLAG_DEFAULT = 0; | ||
| 338 | pub const KF_FLAG_NO_APPCONTAINER_REDIRECTION = 65536; | ||
| 339 | pub const KF_FLAG_CREATE = 32768; | ||
| 340 | pub const KF_FLAG_DONT_VERIFY = 16384; | ||
| 341 | pub const KF_FLAG_DONT_UNEXPAND = 8192; | ||
| 342 | pub const KF_FLAG_NO_ALIAS = 4096; | ||
| 343 | pub const KF_FLAG_INIT = 2048; | ||
| 344 | pub const KF_FLAG_DEFAULT_PATH = 1024; | ||
| 345 | pub const KF_FLAG_NOT_PARENT_RELATIVE = 512; | ||
| 346 | pub const KF_FLAG_SIMPLE_IDLIST = 256; | ||
| 347 | pub const KF_FLAG_ALIAS_ONLY = -2147483648; | ||
| 348 | |||
| 349 | pub const S_OK = 0; | ||
| 350 | pub const E_NOTIMPL = @bitCast(c_long, c_ulong(0x80004001)); | ||
| 351 | pub const E_NOINTERFACE = @bitCast(c_long, c_ulong(0x80004002)); | ||
| 352 | pub const E_POINTER = @bitCast(c_long, c_ulong(0x80004003)); | ||
| 353 | pub const E_ABORT = @bitCast(c_long, c_ulong(0x80004004)); | ||
| 354 | pub const E_FAIL = @bitCast(c_long, c_ulong(0x80004005)); | ||
| 355 | pub const E_UNEXPECTED = @bitCast(c_long, c_ulong(0x8000FFFF)); | ||
| 356 | pub const E_ACCESSDENIED = @bitCast(c_long, c_ulong(0x80070005)); | ||
| 357 | pub const E_HANDLE = @bitCast(c_long, c_ulong(0x80070006)); | ||
| 358 | pub const E_OUTOFMEMORY = @bitCast(c_long, c_ulong(0x8007000E)); | ||
| 359 | pub const E_INVALIDARG = @bitCast(c_long, c_ulong(0x80070057)); | ||
| 360 | |||
| 361 | pub const FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; | ||
| 362 | pub const FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; | ||
| 363 | pub const FILE_FLAG_NO_BUFFERING = 0x20000000; | ||
| 364 | pub const FILE_FLAG_OPEN_NO_RECALL = 0x00100000; | ||
| 365 | pub const FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; | ||
| 366 | pub const FILE_FLAG_OVERLAPPED = 0x40000000; | ||
| 367 | pub const FILE_FLAG_POSIX_SEMANTICS = 0x0100000; | ||
| 368 | pub const FILE_FLAG_RANDOM_ACCESS = 0x10000000; | ||
| 369 | pub const FILE_FLAG_SESSION_AWARE = 0x00800000; | ||
| 370 | pub const FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000; | ||
| 371 | pub const FILE_FLAG_WRITE_THROUGH = 0x80000000; | ||
| 372 | |||
| 373 | pub const SMALL_RECT = extern struct { | ||
| 374 | Left: SHORT, | ||
| 375 | Top: SHORT, | ||
| 376 | Right: SHORT, | ||
| 377 | Bottom: SHORT, | ||
| 378 | }; | ||
| 379 | |||
| 380 | pub const COORD = extern struct { | ||
| 381 | X: SHORT, | ||
| 382 | Y: SHORT, | ||
| 383 | }; | ||
| 384 | |||
| 385 | pub const CREATE_UNICODE_ENVIRONMENT = 1024; | ||
| 386 | |||
| 387 | pub const TLS_OUT_OF_INDEXES = 4294967295; | ||
| 388 | pub const IMAGE_TLS_DIRECTORY = extern struct { | ||
| 389 | StartAddressOfRawData: usize, | ||
| 390 | EndAddressOfRawData: usize, | ||
| 391 | AddressOfIndex: usize, | ||
| 392 | AddressOfCallBacks: usize, | ||
| 393 | SizeOfZeroFill: u32, | ||
| 394 | Characteristics: u32, | ||
| 395 | }; | ||
| 396 | pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY; | ||
| 397 | pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY; | ||
| 398 | |||
| 399 | pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void; | ||
std/os/windows/kernel32.zig+3-5| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | use @import("index.zig"); | 1 | use @import("../windows.zig"); |
| 2 | 2 | ||
| 3 | pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; | 3 | pub extern "kernel32" stdcallcc fn CancelIoEx(hFile: HANDLE, lpOverlapped: LPOVERLAPPED) BOOL; |
| 4 | 4 | ||
| ... | @@ -262,12 +262,10 @@ pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; | ... | @@ -262,12 +262,10 @@ pub const INIT_ONCE_STATIC_INIT = RTL_RUN_ONCE_INIT; |
| 262 | 262 | ||
| 263 | pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; | 263 | pub extern "kernel32" stdcallcc fn InitOnceExecuteOnce(InitOnce: *INIT_ONCE, InitFn: INIT_ONCE_FN, Parameter: ?*c_void, Context: ?*c_void) BOOL; |
| 264 | 264 | ||
| 265 | pub const INIT_ONCE_FN = extern fn(InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; | 265 | pub const INIT_ONCE_FN = extern fn (InitOnce: *INIT_ONCE, Parameter: ?*c_void, Context: ?*c_void) BOOL; |
| 266 | 266 | ||
| 267 | pub const RTL_RUN_ONCE = extern struct { | 267 | pub const RTL_RUN_ONCE = extern struct { |
| 268 | Ptr: ?*c_void, | 268 | Ptr: ?*c_void, |
| 269 | }; | 269 | }; |
| 270 | 270 | ||
| 271 | pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { | 271 | pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; |
| 272 | .Ptr = null, | ||
| 273 | }; |
std/os/windows/ntdll.zig+1-1| ... | @@ -1,3 +1,3 @@ | ... | @@ -1,3 +1,3 @@ |
| 1 | use @import("index.zig"); | 1 | use @import("../windows.zig"); |
| 2 | 2 | ||
| 3 | pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD; | 3 | pub extern "NtDll" stdcallcc fn RtlCaptureStackBackTrace(FramesToSkip: DWORD, FramesToCapture: DWORD, BackTrace: **c_void, BackTraceHash: ?*DWORD) WORD; |
std/os/windows/ole32.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | use @import("index.zig"); | 1 | use @import("../windows.zig"); |
| 2 | 2 | ||
| 3 | pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; | 3 | pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; |
| 4 | pub extern "ole32" stdcallcc fn CoUninitialize() void; | 4 | pub extern "ole32" stdcallcc fn CoUninitialize() void; |
std/os/windows/shell32.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | use @import("index.zig"); | 1 | use @import("../windows.zig"); |
| 2 | 2 | ||
| 3 | pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; | 3 | pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; |
| 4 | 4 |
std/os/windows/util.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | 1 | const std = @import("../../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const os = std.os; | 3 | const os = std.os; |
| 4 | const unicode = std.unicode; | 4 | const unicode = std.unicode; |
std/os/zen.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | 3 | ||
| 4 | ////////////////////////// | 4 | ////////////////////////// |
std/pdb.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | 2 | const std = @import("std.zig"); |
| 3 | const io = std.io; | 3 | const io = std.io; |
| 4 | const math = std.math; | 4 | const math = std.math; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
std/priority_queue.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const Allocator = std.mem.Allocator; | 2 | const Allocator = std.mem.Allocator; |
| 3 | const debug = std.debug; | 3 | const debug = std.debug; |
| 4 | const expect = std.testing.expect; | 4 | const expect = std.testing.expect; |
std/rand.zig created+1005| ... | @@ -0,0 +1,1005 @@ | ||
| 1 | // The engines provided here should be initialized from an external source. For now, getRandomBytes | ||
| 2 | // from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using | ||
| 3 | // a normal PRNG will be faster and use substantially less stack space. | ||
| 4 | // | ||
| 5 | // ``` | ||
| 6 | // var buf: [8]u8 = undefined; | ||
| 7 | // try std.os.getRandomBytes(buf[0..]); | ||
| 8 | // const seed = mem.readIntSliceLittle(u64, buf[0..8]); | ||
| 9 | // | ||
| 10 | // var r = DefaultPrng.init(seed); | ||
| 11 | // | ||
| 12 | // const s = r.random.int(u64); | ||
| 13 | // ``` | ||
| 14 | // | ||
| 15 | // TODO(tiehuis): Benchmark these against other reference implementations. | ||
| 16 | |||
| 17 | const std = @import("std.zig"); | ||
| 18 | const builtin = @import("builtin"); | ||
| 19 | const assert = std.debug.assert; | ||
| 20 | const expect = std.testing.expect; | ||
| 21 | const mem = std.mem; | ||
| 22 | const math = std.math; | ||
| 23 | const ziggurat = @import("rand/ziggurat.zig"); | ||
| 24 | const maxInt = std.math.maxInt; | ||
| 25 | |||
| 26 | // When you need fast unbiased random numbers | ||
| 27 | pub const DefaultPrng = Xoroshiro128; | ||
| 28 | |||
| 29 | // When you need cryptographically secure random numbers | ||
| 30 | pub const DefaultCsprng = Isaac64; | ||
| 31 | |||
| 32 | pub const Random = struct { | ||
| 33 | fillFn: fn (r: *Random, buf: []u8) void, | ||
| 34 | |||
| 35 | /// Read random bytes into the specified buffer until full. | ||
| 36 | pub fn bytes(r: *Random, buf: []u8) void { | ||
| 37 | r.fillFn(r, buf); | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn boolean(r: *Random) bool { | ||
| 41 | return r.int(u1) != 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. | ||
| 45 | /// `i` is evenly distributed. | ||
| 46 | pub fn int(r: *Random, comptime T: type) T { | ||
| 47 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 48 | const ByteAlignedT = @IntType(false, @divTrunc(T.bit_count + 7, 8) * 8); | ||
| 49 | |||
| 50 | var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined; | ||
| 51 | r.bytes(rand_bytes[0..]); | ||
| 52 | |||
| 53 | // use LE instead of native endian for better portability maybe? | ||
| 54 | // TODO: endian portability is pointless if the underlying prng isn't endian portable. | ||
| 55 | // TODO: document the endian portability of this library. | ||
| 56 | const byte_aligned_result = mem.readIntSliceLittle(ByteAlignedT, rand_bytes); | ||
| 57 | const unsigned_result = @truncate(UnsignedT, byte_aligned_result); | ||
| 58 | return @bitCast(T, unsigned_result); | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Constant-time implementation off ::uintLessThan. | ||
| 62 | /// The results of this function may be biased. | ||
| 63 | pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T { | ||
| 64 | comptime assert(T.is_signed == false); | ||
| 65 | comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! | ||
| 66 | assert(0 < less_than); | ||
| 67 | if (T.bit_count <= 32) { | ||
| 68 | return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than)); | ||
| 69 | } else { | ||
| 70 | return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than)); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. | ||
| 75 | /// This function assumes that the underlying ::fillFn produces evenly distributed values. | ||
| 76 | /// Within this assumption, the runtime of this function is exponentially distributed. | ||
| 77 | /// If ::fillFn were backed by a true random generator, | ||
| 78 | /// the runtime of this function would technically be unbounded. | ||
| 79 | /// However, if ::fillFn is backed by any evenly distributed pseudo random number generator, | ||
| 80 | /// this function is guaranteed to return. | ||
| 81 | /// If you need deterministic runtime bounds, use `::uintLessThanBiased`. | ||
| 82 | pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { | ||
| 83 | comptime assert(T.is_signed == false); | ||
| 84 | comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! | ||
| 85 | assert(0 < less_than); | ||
| 86 | // Small is typically u32 | ||
| 87 | const Small = @IntType(false, @divTrunc(T.bit_count + 31, 32) * 32); | ||
| 88 | // Large is typically u64 | ||
| 89 | const Large = @IntType(false, Small.bit_count * 2); | ||
| 90 | |||
| 91 | // adapted from: | ||
| 92 | // http://www.pcg-random.org/posts/bounded-rands.html | ||
| 93 | // "Lemire's (with an extra tweak from me)" | ||
| 94 | var x: Small = r.int(Small); | ||
| 95 | var m: Large = Large(x) * Large(less_than); | ||
| 96 | var l: Small = @truncate(Small, m); | ||
| 97 | if (l < less_than) { | ||
| 98 | // TODO: workaround for https://github.com/ziglang/zig/issues/1770 | ||
| 99 | // should be: | ||
| 100 | // var t: Small = -%less_than; | ||
| 101 | var t: Small = @bitCast(Small, -%@bitCast(@IntType(true, Small.bit_count), Small(less_than))); | ||
| 102 | |||
| 103 | if (t >= less_than) { | ||
| 104 | t -= less_than; | ||
| 105 | if (t >= less_than) { | ||
| 106 | t %= less_than; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | while (l < t) { | ||
| 110 | x = r.int(Small); | ||
| 111 | m = Large(x) * Large(less_than); | ||
| 112 | l = @truncate(Small, m); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | return @intCast(T, m >> Small.bit_count); | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Constant-time implementation off ::uintAtMost. | ||
| 119 | /// The results of this function may be biased. | ||
| 120 | pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T { | ||
| 121 | assert(T.is_signed == false); | ||
| 122 | if (at_most == maxInt(T)) { | ||
| 123 | // have the full range | ||
| 124 | return r.int(T); | ||
| 125 | } | ||
| 126 | return r.uintLessThanBiased(T, at_most + 1); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. | ||
| 130 | /// See ::uintLessThan, which this function uses in most cases, | ||
| 131 | /// for commentary on the runtime of this function. | ||
| 132 | pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T { | ||
| 133 | assert(T.is_signed == false); | ||
| 134 | if (at_most == maxInt(T)) { | ||
| 135 | // have the full range | ||
| 136 | return r.int(T); | ||
| 137 | } | ||
| 138 | return r.uintLessThan(T, at_most + 1); | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Constant-time implementation off ::intRangeLessThan. | ||
| 142 | /// The results of this function may be biased. | ||
| 143 | pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T { | ||
| 144 | assert(at_least < less_than); | ||
| 145 | if (T.is_signed) { | ||
| 146 | // Two's complement makes this math pretty easy. | ||
| 147 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 148 | const lo = @bitCast(UnsignedT, at_least); | ||
| 149 | const hi = @bitCast(UnsignedT, less_than); | ||
| 150 | const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); | ||
| 151 | return @bitCast(T, result); | ||
| 152 | } else { | ||
| 153 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 154 | return at_least + r.uintLessThanBiased(T, less_than - at_least); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. | ||
| 159 | /// See ::uintLessThan, which this function uses in most cases, | ||
| 160 | /// for commentary on the runtime of this function. | ||
| 161 | pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T { | ||
| 162 | assert(at_least < less_than); | ||
| 163 | if (T.is_signed) { | ||
| 164 | // Two's complement makes this math pretty easy. | ||
| 165 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 166 | const lo = @bitCast(UnsignedT, at_least); | ||
| 167 | const hi = @bitCast(UnsignedT, less_than); | ||
| 168 | const result = lo +% r.uintLessThan(UnsignedT, hi -% lo); | ||
| 169 | return @bitCast(T, result); | ||
| 170 | } else { | ||
| 171 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 172 | return at_least + r.uintLessThan(T, less_than - at_least); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Constant-time implementation off ::intRangeAtMostBiased. | ||
| 177 | /// The results of this function may be biased. | ||
| 178 | pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T { | ||
| 179 | assert(at_least <= at_most); | ||
| 180 | if (T.is_signed) { | ||
| 181 | // Two's complement makes this math pretty easy. | ||
| 182 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 183 | const lo = @bitCast(UnsignedT, at_least); | ||
| 184 | const hi = @bitCast(UnsignedT, at_most); | ||
| 185 | const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); | ||
| 186 | return @bitCast(T, result); | ||
| 187 | } else { | ||
| 188 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 189 | return at_least + r.uintAtMostBiased(T, at_most - at_least); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. | ||
| 194 | /// See ::uintLessThan, which this function uses in most cases, | ||
| 195 | /// for commentary on the runtime of this function. | ||
| 196 | pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T { | ||
| 197 | assert(at_least <= at_most); | ||
| 198 | if (T.is_signed) { | ||
| 199 | // Two's complement makes this math pretty easy. | ||
| 200 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 201 | const lo = @bitCast(UnsignedT, at_least); | ||
| 202 | const hi = @bitCast(UnsignedT, at_most); | ||
| 203 | const result = lo +% r.uintAtMost(UnsignedT, hi -% lo); | ||
| 204 | return @bitCast(T, result); | ||
| 205 | } else { | ||
| 206 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 207 | return at_least + r.uintAtMost(T, at_most - at_least); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | /// TODO: deprecated. use ::boolean or ::int instead. | ||
| 212 | pub fn scalar(r: *Random, comptime T: type) T { | ||
| 213 | return if (T == bool) r.boolean() else r.int(T); | ||
| 214 | } | ||
| 215 | |||
| 216 | /// TODO: deprecated. renamed to ::intRangeLessThan | ||
| 217 | pub fn range(r: *Random, comptime T: type, start: T, end: T) T { | ||
| 218 | return r.intRangeLessThan(T, start, end); | ||
| 219 | } | ||
| 220 | |||
| 221 | /// Return a floating point value evenly distributed in the range [0, 1). | ||
| 222 | pub fn float(r: *Random, comptime T: type) T { | ||
| 223 | // Generate a uniform value between [1, 2) and scale down to [0, 1). | ||
| 224 | // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. | ||
| 225 | switch (T) { | ||
| 226 | f32 => { | ||
| 227 | const s = r.int(u32); | ||
| 228 | const repr = (0x7f << 23) | (s >> 9); | ||
| 229 | return @bitCast(f32, repr) - 1.0; | ||
| 230 | }, | ||
| 231 | f64 => { | ||
| 232 | const s = r.int(u64); | ||
| 233 | const repr = (0x3ff << 52) | (s >> 12); | ||
| 234 | return @bitCast(f64, repr) - 1.0; | ||
| 235 | }, | ||
| 236 | else => @compileError("unknown floating point type"), | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Return a floating point value normally distributed with mean = 0, stddev = 1. | ||
| 241 | /// | ||
| 242 | /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. | ||
| 243 | pub fn floatNorm(r: *Random, comptime T: type) T { | ||
| 244 | const value = ziggurat.next_f64(r, ziggurat.NormDist); | ||
| 245 | switch (T) { | ||
| 246 | f32 => return @floatCast(f32, value), | ||
| 247 | f64 => return value, | ||
| 248 | else => @compileError("unknown floating point type"), | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Return an exponentially distributed float with a rate parameter of 1. | ||
| 253 | /// | ||
| 254 | /// To use a different rate parameter, use: floatExp(...) / desiredRate. | ||
| 255 | pub fn floatExp(r: *Random, comptime T: type) T { | ||
| 256 | const value = ziggurat.next_f64(r, ziggurat.ExpDist); | ||
| 257 | switch (T) { | ||
| 258 | f32 => return @floatCast(f32, value), | ||
| 259 | f64 => return value, | ||
| 260 | else => @compileError("unknown floating point type"), | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Shuffle a slice into a random order. | ||
| 265 | pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { | ||
| 266 | if (buf.len < 2) { | ||
| 267 | return; | ||
| 268 | } | ||
| 269 | |||
| 270 | var i: usize = 0; | ||
| 271 | while (i < buf.len - 1) : (i += 1) { | ||
| 272 | const j = r.intRangeLessThan(usize, i, buf.len); | ||
| 273 | mem.swap(T, &buf[i], &buf[j]); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | }; | ||
| 277 | |||
| 278 | /// Convert a random integer 0 <= random_int <= maxValue(T), | ||
| 279 | /// into an integer 0 <= result < less_than. | ||
| 280 | /// This function introduces a minor bias. | ||
| 281 | pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { | ||
| 282 | comptime assert(T.is_signed == false); | ||
| 283 | const T2 = @IntType(false, T.bit_count * 2); | ||
| 284 | |||
| 285 | // adapted from: | ||
| 286 | // http://www.pcg-random.org/posts/bounded-rands.html | ||
| 287 | // "Integer Multiplication (Biased)" | ||
| 288 | var m: T2 = T2(random_int) * T2(less_than); | ||
| 289 | return @intCast(T, m >> T.bit_count); | ||
| 290 | } | ||
| 291 | |||
| 292 | const SequentialPrng = struct { | ||
| 293 | const Self = @This(); | ||
| 294 | random: Random, | ||
| 295 | next_value: u8, | ||
| 296 | |||
| 297 | pub fn init() Self { | ||
| 298 | return Self{ | ||
| 299 | .random = Random{ .fillFn = fill }, | ||
| 300 | .next_value = 0, | ||
| 301 | }; | ||
| 302 | } | ||
| 303 | |||
| 304 | fn fill(r: *Random, buf: []u8) void { | ||
| 305 | const self = @fieldParentPtr(Self, "random", r); | ||
| 306 | for (buf) |*b| { | ||
| 307 | b.* = self.next_value; | ||
| 308 | } | ||
| 309 | self.next_value +%= 1; | ||
| 310 | } | ||
| 311 | }; | ||
| 312 | |||
| 313 | test "Random int" { | ||
| 314 | testRandomInt(); | ||
| 315 | comptime testRandomInt(); | ||
| 316 | } | ||
| 317 | fn testRandomInt() void { | ||
| 318 | var r = SequentialPrng.init(); | ||
| 319 | |||
| 320 | expect(r.random.int(u0) == 0); | ||
| 321 | |||
| 322 | r.next_value = 0; | ||
| 323 | expect(r.random.int(u1) == 0); | ||
| 324 | expect(r.random.int(u1) == 1); | ||
| 325 | expect(r.random.int(u2) == 2); | ||
| 326 | expect(r.random.int(u2) == 3); | ||
| 327 | expect(r.random.int(u2) == 0); | ||
| 328 | |||
| 329 | r.next_value = 0xff; | ||
| 330 | expect(r.random.int(u8) == 0xff); | ||
| 331 | r.next_value = 0x11; | ||
| 332 | expect(r.random.int(u8) == 0x11); | ||
| 333 | |||
| 334 | r.next_value = 0xff; | ||
| 335 | expect(r.random.int(u32) == 0xffffffff); | ||
| 336 | r.next_value = 0x11; | ||
| 337 | expect(r.random.int(u32) == 0x11111111); | ||
| 338 | |||
| 339 | r.next_value = 0xff; | ||
| 340 | expect(r.random.int(i32) == -1); | ||
| 341 | r.next_value = 0x11; | ||
| 342 | expect(r.random.int(i32) == 0x11111111); | ||
| 343 | |||
| 344 | r.next_value = 0xff; | ||
| 345 | expect(r.random.int(i8) == -1); | ||
| 346 | r.next_value = 0x11; | ||
| 347 | expect(r.random.int(i8) == 0x11); | ||
| 348 | |||
| 349 | r.next_value = 0xff; | ||
| 350 | expect(r.random.int(u33) == 0x1ffffffff); | ||
| 351 | r.next_value = 0xff; | ||
| 352 | expect(r.random.int(i1) == -1); | ||
| 353 | r.next_value = 0xff; | ||
| 354 | expect(r.random.int(i2) == -1); | ||
| 355 | r.next_value = 0xff; | ||
| 356 | expect(r.random.int(i33) == -1); | ||
| 357 | } | ||
| 358 | |||
| 359 | test "Random boolean" { | ||
| 360 | testRandomBoolean(); | ||
| 361 | comptime testRandomBoolean(); | ||
| 362 | } | ||
| 363 | fn testRandomBoolean() void { | ||
| 364 | var r = SequentialPrng.init(); | ||
| 365 | expect(r.random.boolean() == false); | ||
| 366 | expect(r.random.boolean() == true); | ||
| 367 | expect(r.random.boolean() == false); | ||
| 368 | expect(r.random.boolean() == true); | ||
| 369 | } | ||
| 370 | |||
| 371 | test "Random intLessThan" { | ||
| 372 | @setEvalBranchQuota(10000); | ||
| 373 | testRandomIntLessThan(); | ||
| 374 | comptime testRandomIntLessThan(); | ||
| 375 | } | ||
| 376 | fn testRandomIntLessThan() void { | ||
| 377 | var r = SequentialPrng.init(); | ||
| 378 | r.next_value = 0xff; | ||
| 379 | expect(r.random.uintLessThan(u8, 4) == 3); | ||
| 380 | expect(r.next_value == 0); | ||
| 381 | expect(r.random.uintLessThan(u8, 4) == 0); | ||
| 382 | expect(r.next_value == 1); | ||
| 383 | |||
| 384 | r.next_value = 0; | ||
| 385 | expect(r.random.uintLessThan(u64, 32) == 0); | ||
| 386 | |||
| 387 | // trigger the bias rejection code path | ||
| 388 | r.next_value = 0; | ||
| 389 | expect(r.random.uintLessThan(u8, 3) == 0); | ||
| 390 | // verify we incremented twice | ||
| 391 | expect(r.next_value == 2); | ||
| 392 | |||
| 393 | r.next_value = 0xff; | ||
| 394 | expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); | ||
| 395 | r.next_value = 0xff; | ||
| 396 | expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); | ||
| 397 | |||
| 398 | r.next_value = 0xff; | ||
| 399 | expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); | ||
| 400 | r.next_value = 0xff; | ||
| 401 | expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); | ||
| 402 | r.next_value = 0xff; | ||
| 403 | expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1); | ||
| 404 | |||
| 405 | r.next_value = 0xff; | ||
| 406 | expect(r.random.intRangeLessThan(i3, -4, 0) == -1); | ||
| 407 | r.next_value = 0xff; | ||
| 408 | expect(r.random.intRangeLessThan(i3, -2, 2) == 1); | ||
| 409 | } | ||
| 410 | |||
| 411 | test "Random intAtMost" { | ||
| 412 | @setEvalBranchQuota(10000); | ||
| 413 | testRandomIntAtMost(); | ||
| 414 | comptime testRandomIntAtMost(); | ||
| 415 | } | ||
| 416 | fn testRandomIntAtMost() void { | ||
| 417 | var r = SequentialPrng.init(); | ||
| 418 | r.next_value = 0xff; | ||
| 419 | expect(r.random.uintAtMost(u8, 3) == 3); | ||
| 420 | expect(r.next_value == 0); | ||
| 421 | expect(r.random.uintAtMost(u8, 3) == 0); | ||
| 422 | |||
| 423 | // trigger the bias rejection code path | ||
| 424 | r.next_value = 0; | ||
| 425 | expect(r.random.uintAtMost(u8, 2) == 0); | ||
| 426 | // verify we incremented twice | ||
| 427 | expect(r.next_value == 2); | ||
| 428 | |||
| 429 | r.next_value = 0xff; | ||
| 430 | expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); | ||
| 431 | r.next_value = 0xff; | ||
| 432 | expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); | ||
| 433 | |||
| 434 | r.next_value = 0xff; | ||
| 435 | expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); | ||
| 436 | r.next_value = 0xff; | ||
| 437 | expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); | ||
| 438 | r.next_value = 0xff; | ||
| 439 | expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1); | ||
| 440 | |||
| 441 | r.next_value = 0xff; | ||
| 442 | expect(r.random.intRangeAtMost(i3, -4, -1) == -1); | ||
| 443 | r.next_value = 0xff; | ||
| 444 | expect(r.random.intRangeAtMost(i3, -2, 1) == 1); | ||
| 445 | |||
| 446 | expect(r.random.uintAtMost(u0, 0) == 0); | ||
| 447 | } | ||
| 448 | |||
| 449 | test "Random Biased" { | ||
| 450 | var r = DefaultPrng.init(0); | ||
| 451 | // Not thoroughly checking the logic here. | ||
| 452 | // Just want to execute all the paths with different types. | ||
| 453 | |||
| 454 | expect(r.random.uintLessThanBiased(u1, 1) == 0); | ||
| 455 | expect(r.random.uintLessThanBiased(u32, 10) < 10); | ||
| 456 | expect(r.random.uintLessThanBiased(u64, 20) < 20); | ||
| 457 | |||
| 458 | expect(r.random.uintAtMostBiased(u0, 0) == 0); | ||
| 459 | expect(r.random.uintAtMostBiased(u1, 0) <= 0); | ||
| 460 | expect(r.random.uintAtMostBiased(u32, 10) <= 10); | ||
| 461 | expect(r.random.uintAtMostBiased(u64, 20) <= 20); | ||
| 462 | |||
| 463 | expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); | ||
| 464 | expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); | ||
| 465 | expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); | ||
| 466 | expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); | ||
| 467 | expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); | ||
| 468 | expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); | ||
| 469 | |||
| 470 | // uncomment for broken module error: | ||
| 471 | //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); | ||
| 472 | expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); | ||
| 473 | expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); | ||
| 474 | expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); | ||
| 475 | expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); | ||
| 476 | expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); | ||
| 477 | expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); | ||
| 478 | } | ||
| 479 | |||
| 480 | // Generator to extend 64-bit seed values into longer sequences. | ||
| 481 | // | ||
| 482 | // The number of cycles is thus limited to 64-bits regardless of the engine, but this | ||
| 483 | // is still plenty for practical purposes. | ||
| 484 | const SplitMix64 = struct { | ||
| 485 | s: u64, | ||
| 486 | |||
| 487 | pub fn init(seed: u64) SplitMix64 { | ||
| 488 | return SplitMix64{ .s = seed }; | ||
| 489 | } | ||
| 490 | |||
| 491 | pub fn next(self: *SplitMix64) u64 { | ||
| 492 | self.s +%= 0x9e3779b97f4a7c15; | ||
| 493 | |||
| 494 | var z = self.s; | ||
| 495 | z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9; | ||
| 496 | z = (z ^ (z >> 27)) *% 0x94d049bb133111eb; | ||
| 497 | return z ^ (z >> 31); | ||
| 498 | } | ||
| 499 | }; | ||
| 500 | |||
| 501 | test "splitmix64 sequence" { | ||
| 502 | var r = SplitMix64.init(0xaeecf86f7878dd75); | ||
| 503 | |||
| 504 | const seq = []const u64{ | ||
| 505 | 0x5dbd39db0178eb44, | ||
| 506 | 0xa9900fb66b397da3, | ||
| 507 | 0x5c1a28b1aeebcf5c, | ||
| 508 | 0x64a963238f776912, | ||
| 509 | 0xc6d4177b21d1c0ab, | ||
| 510 | 0xb2cbdbdb5ea35394, | ||
| 511 | }; | ||
| 512 | |||
| 513 | for (seq) |s| { | ||
| 514 | expect(s == r.next()); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | // PCG32 - http://www.pcg-random.org/ | ||
| 519 | // | ||
| 520 | // PRNG | ||
| 521 | pub const Pcg = struct { | ||
| 522 | const default_multiplier = 6364136223846793005; | ||
| 523 | |||
| 524 | random: Random, | ||
| 525 | |||
| 526 | s: u64, | ||
| 527 | i: u64, | ||
| 528 | |||
| 529 | pub fn init(init_s: u64) Pcg { | ||
| 530 | var pcg = Pcg{ | ||
| 531 | .random = Random{ .fillFn = fill }, | ||
| 532 | .s = undefined, | ||
| 533 | .i = undefined, | ||
| 534 | }; | ||
| 535 | |||
| 536 | pcg.seed(init_s); | ||
| 537 | return pcg; | ||
| 538 | } | ||
| 539 | |||
| 540 | fn next(self: *Pcg) u32 { | ||
| 541 | const l = self.s; | ||
| 542 | self.s = l *% default_multiplier +% (self.i | 1); | ||
| 543 | |||
| 544 | const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27); | ||
| 545 | const rot = @intCast(u32, l >> 59); | ||
| 546 | |||
| 547 | return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31)); | ||
| 548 | } | ||
| 549 | |||
| 550 | fn seed(self: *Pcg, init_s: u64) void { | ||
| 551 | // Pcg requires 128-bits of seed. | ||
| 552 | var gen = SplitMix64.init(init_s); | ||
| 553 | self.seedTwo(gen.next(), gen.next()); | ||
| 554 | } | ||
| 555 | |||
| 556 | fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { | ||
| 557 | self.s = 0; | ||
| 558 | self.i = (init_s << 1) | 1; | ||
| 559 | self.s = self.s *% default_multiplier +% self.i; | ||
| 560 | self.s +%= init_i; | ||
| 561 | self.s = self.s *% default_multiplier +% self.i; | ||
| 562 | } | ||
| 563 | |||
| 564 | fn fill(r: *Random, buf: []u8) void { | ||
| 565 | const self = @fieldParentPtr(Pcg, "random", r); | ||
| 566 | |||
| 567 | var i: usize = 0; | ||
| 568 | const aligned_len = buf.len - (buf.len & 7); | ||
| 569 | |||
| 570 | // Complete 4 byte segments. | ||
| 571 | while (i < aligned_len) : (i += 4) { | ||
| 572 | var n = self.next(); | ||
| 573 | comptime var j: usize = 0; | ||
| 574 | inline while (j < 4) : (j += 1) { | ||
| 575 | buf[i + j] = @truncate(u8, n); | ||
| 576 | n >>= 8; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | // Remaining. (cuts the stream) | ||
| 581 | if (i != buf.len) { | ||
| 582 | var n = self.next(); | ||
| 583 | while (i < buf.len) : (i += 1) { | ||
| 584 | buf[i] = @truncate(u8, n); | ||
| 585 | n >>= 4; | ||
| 586 | } | ||
| 587 | } | ||
| 588 | } | ||
| 589 | }; | ||
| 590 | |||
| 591 | test "pcg sequence" { | ||
| 592 | var r = Pcg.init(0); | ||
| 593 | const s0: u64 = 0x9394bf54ce5d79de; | ||
| 594 | const s1: u64 = 0x84e9c579ef59bbf7; | ||
| 595 | r.seedTwo(s0, s1); | ||
| 596 | |||
| 597 | const seq = []const u32{ | ||
| 598 | 2881561918, | ||
| 599 | 3063928540, | ||
| 600 | 1199791034, | ||
| 601 | 2487695858, | ||
| 602 | 1479648952, | ||
| 603 | 3247963454, | ||
| 604 | }; | ||
| 605 | |||
| 606 | for (seq) |s| { | ||
| 607 | expect(s == r.next()); | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | // Xoroshiro128+ - http://xoroshiro.di.unimi.it/ | ||
| 612 | // | ||
| 613 | // PRNG | ||
| 614 | pub const Xoroshiro128 = struct { | ||
| 615 | random: Random, | ||
| 616 | |||
| 617 | s: [2]u64, | ||
| 618 | |||
| 619 | pub fn init(init_s: u64) Xoroshiro128 { | ||
| 620 | var x = Xoroshiro128{ | ||
| 621 | .random = Random{ .fillFn = fill }, | ||
| 622 | .s = undefined, | ||
| 623 | }; | ||
| 624 | |||
| 625 | x.seed(init_s); | ||
| 626 | return x; | ||
| 627 | } | ||
| 628 | |||
| 629 | fn next(self: *Xoroshiro128) u64 { | ||
| 630 | const s0 = self.s[0]; | ||
| 631 | var s1 = self.s[1]; | ||
| 632 | const r = s0 +% s1; | ||
| 633 | |||
| 634 | s1 ^= s0; | ||
| 635 | self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14); | ||
| 636 | self.s[1] = math.rotl(u64, s1, u8(36)); | ||
| 637 | |||
| 638 | return r; | ||
| 639 | } | ||
| 640 | |||
| 641 | // Skip 2^64 places ahead in the sequence | ||
| 642 | fn jump(self: *Xoroshiro128) void { | ||
| 643 | var s0: u64 = 0; | ||
| 644 | var s1: u64 = 0; | ||
| 645 | |||
| 646 | const table = []const u64{ | ||
| 647 | 0xbeac0467eba5facb, | ||
| 648 | 0xd86b048b86aa9922, | ||
| 649 | }; | ||
| 650 | |||
| 651 | inline for (table) |entry| { | ||
| 652 | var b: usize = 0; | ||
| 653 | while (b < 64) : (b += 1) { | ||
| 654 | if ((entry & (u64(1) << @intCast(u6, b))) != 0) { | ||
| 655 | s0 ^= self.s[0]; | ||
| 656 | s1 ^= self.s[1]; | ||
| 657 | } | ||
| 658 | _ = self.next(); | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | self.s[0] = s0; | ||
| 663 | self.s[1] = s1; | ||
| 664 | } | ||
| 665 | |||
| 666 | fn seed(self: *Xoroshiro128, init_s: u64) void { | ||
| 667 | // Xoroshiro requires 128-bits of seed. | ||
| 668 | var gen = SplitMix64.init(init_s); | ||
| 669 | |||
| 670 | self.s[0] = gen.next(); | ||
| 671 | self.s[1] = gen.next(); | ||
| 672 | } | ||
| 673 | |||
| 674 | fn fill(r: *Random, buf: []u8) void { | ||
| 675 | const self = @fieldParentPtr(Xoroshiro128, "random", r); | ||
| 676 | |||
| 677 | var i: usize = 0; | ||
| 678 | const aligned_len = buf.len - (buf.len & 7); | ||
| 679 | |||
| 680 | // Complete 8 byte segments. | ||
| 681 | while (i < aligned_len) : (i += 8) { | ||
| 682 | var n = self.next(); | ||
| 683 | comptime var j: usize = 0; | ||
| 684 | inline while (j < 8) : (j += 1) { | ||
| 685 | buf[i + j] = @truncate(u8, n); | ||
| 686 | n >>= 8; | ||
| 687 | } | ||
| 688 | } | ||
| 689 | |||
| 690 | // Remaining. (cuts the stream) | ||
| 691 | if (i != buf.len) { | ||
| 692 | var n = self.next(); | ||
| 693 | while (i < buf.len) : (i += 1) { | ||
| 694 | buf[i] = @truncate(u8, n); | ||
| 695 | n >>= 8; | ||
| 696 | } | ||
| 697 | } | ||
| 698 | } | ||
| 699 | }; | ||
| 700 | |||
| 701 | test "xoroshiro sequence" { | ||
| 702 | var r = Xoroshiro128.init(0); | ||
| 703 | r.s[0] = 0xaeecf86f7878dd75; | ||
| 704 | r.s[1] = 0x01cd153642e72622; | ||
| 705 | |||
| 706 | const seq1 = []const u64{ | ||
| 707 | 0xb0ba0da5bb600397, | ||
| 708 | 0x18a08afde614dccc, | ||
| 709 | 0xa2635b956a31b929, | ||
| 710 | 0xabe633c971efa045, | ||
| 711 | 0x9ac19f9706ca3cac, | ||
| 712 | 0xf62b426578c1e3fb, | ||
| 713 | }; | ||
| 714 | |||
| 715 | for (seq1) |s| { | ||
| 716 | expect(s == r.next()); | ||
| 717 | } | ||
| 718 | |||
| 719 | r.jump(); | ||
| 720 | |||
| 721 | const seq2 = []const u64{ | ||
| 722 | 0x95344a13556d3e22, | ||
| 723 | 0xb4fb32dafa4d00df, | ||
| 724 | 0xb2011d9ccdcfe2dd, | ||
| 725 | 0x05679a9b2119b908, | ||
| 726 | 0xa860a1da7c9cd8a0, | ||
| 727 | 0x658a96efe3f86550, | ||
| 728 | }; | ||
| 729 | |||
| 730 | for (seq2) |s| { | ||
| 731 | expect(s == r.next()); | ||
| 732 | } | ||
| 733 | } | ||
| 734 | |||
| 735 | // ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html | ||
| 736 | // | ||
| 737 | // CSPRNG | ||
| 738 | // | ||
| 739 | // Follows the general idea of the implementation from here with a few shortcuts. | ||
| 740 | // https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html | ||
| 741 | pub const Isaac64 = struct { | ||
| 742 | random: Random, | ||
| 743 | |||
| 744 | r: [256]u64, | ||
| 745 | m: [256]u64, | ||
| 746 | a: u64, | ||
| 747 | b: u64, | ||
| 748 | c: u64, | ||
| 749 | i: usize, | ||
| 750 | |||
| 751 | pub fn init(init_s: u64) Isaac64 { | ||
| 752 | var isaac = Isaac64{ | ||
| 753 | .random = Random{ .fillFn = fill }, | ||
| 754 | .r = undefined, | ||
| 755 | .m = undefined, | ||
| 756 | .a = undefined, | ||
| 757 | .b = undefined, | ||
| 758 | .c = undefined, | ||
| 759 | .i = undefined, | ||
| 760 | }; | ||
| 761 | |||
| 762 | // seed == 0 => same result as the unseeded reference implementation | ||
| 763 | isaac.seed(init_s, 1); | ||
| 764 | return isaac; | ||
| 765 | } | ||
| 766 | |||
| 767 | fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { | ||
| 768 | const x = self.m[base + m1]; | ||
| 769 | self.a = mix +% self.m[base + m2]; | ||
| 770 | |||
| 771 | const y = self.a +% self.b +% self.m[(x >> 3) % self.m.len]; | ||
| 772 | self.m[base + m1] = y; | ||
| 773 | |||
| 774 | self.b = x +% self.m[(y >> 11) % self.m.len]; | ||
| 775 | self.r[self.r.len - 1 - base - m1] = self.b; | ||
| 776 | } | ||
| 777 | |||
| 778 | fn refill(self: *Isaac64) void { | ||
| 779 | const midpoint = self.r.len / 2; | ||
| 780 | |||
| 781 | self.c +%= 1; | ||
| 782 | self.b +%= self.c; | ||
| 783 | |||
| 784 | { | ||
| 785 | var i: usize = 0; | ||
| 786 | while (i < midpoint) : (i += 4) { | ||
| 787 | self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); | ||
| 788 | self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); | ||
| 789 | self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); | ||
| 790 | self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); | ||
| 791 | } | ||
| 792 | } | ||
| 793 | |||
| 794 | { | ||
| 795 | var i: usize = 0; | ||
| 796 | while (i < midpoint) : (i += 4) { | ||
| 797 | self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); | ||
| 798 | self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); | ||
| 799 | self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); | ||
| 800 | self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); | ||
| 801 | } | ||
| 802 | } | ||
| 803 | |||
| 804 | self.i = 0; | ||
| 805 | } | ||
| 806 | |||
| 807 | fn next(self: *Isaac64) u64 { | ||
| 808 | if (self.i >= self.r.len) { | ||
| 809 | self.refill(); | ||
| 810 | } | ||
| 811 | |||
| 812 | const value = self.r[self.i]; | ||
| 813 | self.i += 1; | ||
| 814 | return value; | ||
| 815 | } | ||
| 816 | |||
| 817 | fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { | ||
| 818 | // We ignore the multi-pass requirement since we don't currently expose full access to | ||
| 819 | // seeding the self.m array completely. | ||
| 820 | mem.set(u64, self.m[0..], 0); | ||
| 821 | self.m[0] = init_s; | ||
| 822 | |||
| 823 | // prescrambled golden ratio constants | ||
| 824 | var a = []const u64{ | ||
| 825 | 0x647c4677a2884b7c, | ||
| 826 | 0xb9f8b322c73ac862, | ||
| 827 | 0x8c0ea5053d4712a0, | ||
| 828 | 0xb29b2e824a595524, | ||
| 829 | 0x82f053db8355e0ce, | ||
| 830 | 0x48fe4a0fa5a09315, | ||
| 831 | 0xae985bf2cbfc89ed, | ||
| 832 | 0x98f5704f6c44c0ab, | ||
| 833 | }; | ||
| 834 | |||
| 835 | comptime var i: usize = 0; | ||
| 836 | inline while (i < rounds) : (i += 1) { | ||
| 837 | var j: usize = 0; | ||
| 838 | while (j < self.m.len) : (j += 8) { | ||
| 839 | comptime var x1: usize = 0; | ||
| 840 | inline while (x1 < 8) : (x1 += 1) { | ||
| 841 | a[x1] +%= self.m[j + x1]; | ||
| 842 | } | ||
| 843 | |||
| 844 | a[0] -%= a[4]; | ||
| 845 | a[5] ^= a[7] >> 9; | ||
| 846 | a[7] +%= a[0]; | ||
| 847 | a[1] -%= a[5]; | ||
| 848 | a[6] ^= a[0] << 9; | ||
| 849 | a[0] +%= a[1]; | ||
| 850 | a[2] -%= a[6]; | ||
| 851 | a[7] ^= a[1] >> 23; | ||
| 852 | a[1] +%= a[2]; | ||
| 853 | a[3] -%= a[7]; | ||
| 854 | a[0] ^= a[2] << 15; | ||
| 855 | a[2] +%= a[3]; | ||
| 856 | a[4] -%= a[0]; | ||
| 857 | a[1] ^= a[3] >> 14; | ||
| 858 | a[3] +%= a[4]; | ||
| 859 | a[5] -%= a[1]; | ||
| 860 | a[2] ^= a[4] << 20; | ||
| 861 | a[4] +%= a[5]; | ||
| 862 | a[6] -%= a[2]; | ||
| 863 | a[3] ^= a[5] >> 17; | ||
| 864 | a[5] +%= a[6]; | ||
| 865 | a[7] -%= a[3]; | ||
| 866 | a[4] ^= a[6] << 14; | ||
| 867 | a[6] +%= a[7]; | ||
| 868 | |||
| 869 | comptime var x2: usize = 0; | ||
| 870 | inline while (x2 < 8) : (x2 += 1) { | ||
| 871 | self.m[j + x2] = a[x2]; | ||
| 872 | } | ||
| 873 | } | ||
| 874 | } | ||
| 875 | |||
| 876 | mem.set(u64, self.r[0..], 0); | ||
| 877 | self.a = 0; | ||
| 878 | self.b = 0; | ||
| 879 | self.c = 0; | ||
| 880 | self.i = self.r.len; // trigger refill on first value | ||
| 881 | } | ||
| 882 | |||
| 883 | fn fill(r: *Random, buf: []u8) void { | ||
| 884 | const self = @fieldParentPtr(Isaac64, "random", r); | ||
| 885 | |||
| 886 | var i: usize = 0; | ||
| 887 | const aligned_len = buf.len - (buf.len & 7); | ||
| 888 | |||
| 889 | // Fill complete 64-byte segments | ||
| 890 | while (i < aligned_len) : (i += 8) { | ||
| 891 | var n = self.next(); | ||
| 892 | comptime var j: usize = 0; | ||
| 893 | inline while (j < 8) : (j += 1) { | ||
| 894 | buf[i + j] = @truncate(u8, n); | ||
| 895 | n >>= 8; | ||
| 896 | } | ||
| 897 | } | ||
| 898 | |||
| 899 | // Fill trailing, ignoring excess (cut the stream). | ||
| 900 | if (i != buf.len) { | ||
| 901 | var n = self.next(); | ||
| 902 | while (i < buf.len) : (i += 1) { | ||
| 903 | buf[i] = @truncate(u8, n); | ||
| 904 | n >>= 8; | ||
| 905 | } | ||
| 906 | } | ||
| 907 | } | ||
| 908 | }; | ||
| 909 | |||
| 910 | test "isaac64 sequence" { | ||
| 911 | var r = Isaac64.init(0); | ||
| 912 | |||
| 913 | // from reference implementation | ||
| 914 | const seq = []const u64{ | ||
| 915 | 0xf67dfba498e4937c, | ||
| 916 | 0x84a5066a9204f380, | ||
| 917 | 0xfee34bd5f5514dbb, | ||
| 918 | 0x4d1664739b8f80d6, | ||
| 919 | 0x8607459ab52a14aa, | ||
| 920 | 0x0e78bc5a98529e49, | ||
| 921 | 0xfe5332822ad13777, | ||
| 922 | 0x556c27525e33d01a, | ||
| 923 | 0x08643ca615f3149f, | ||
| 924 | 0xd0771faf3cb04714, | ||
| 925 | 0x30e86f68a37b008d, | ||
| 926 | 0x3074ebc0488a3adf, | ||
| 927 | 0x270645ea7a2790bc, | ||
| 928 | 0x5601a0a8d3763c6a, | ||
| 929 | 0x2f83071f53f325dd, | ||
| 930 | 0xb9090f3d42d2d2ea, | ||
| 931 | }; | ||
| 932 | |||
| 933 | for (seq) |s| { | ||
| 934 | expect(s == r.next()); | ||
| 935 | } | ||
| 936 | } | ||
| 937 | |||
| 938 | // Actual Random helper function tests, pcg engine is assumed correct. | ||
| 939 | test "Random float" { | ||
| 940 | var prng = DefaultPrng.init(0); | ||
| 941 | |||
| 942 | var i: usize = 0; | ||
| 943 | while (i < 1000) : (i += 1) { | ||
| 944 | const val1 = prng.random.float(f32); | ||
| 945 | expect(val1 >= 0.0); | ||
| 946 | expect(val1 < 1.0); | ||
| 947 | |||
| 948 | const val2 = prng.random.float(f64); | ||
| 949 | expect(val2 >= 0.0); | ||
| 950 | expect(val2 < 1.0); | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | test "Random shuffle" { | ||
| 955 | var prng = DefaultPrng.init(0); | ||
| 956 | |||
| 957 | var seq = []const u8{ 0, 1, 2, 3, 4 }; | ||
| 958 | var seen = []bool{false} ** 5; | ||
| 959 | |||
| 960 | var i: usize = 0; | ||
| 961 | while (i < 1000) : (i += 1) { | ||
| 962 | prng.random.shuffle(u8, seq[0..]); | ||
| 963 | seen[seq[0]] = true; | ||
| 964 | expect(sumArray(seq[0..]) == 10); | ||
| 965 | } | ||
| 966 | |||
| 967 | // we should see every entry at the head at least once | ||
| 968 | for (seen) |e| { | ||
| 969 | expect(e == true); | ||
| 970 | } | ||
| 971 | } | ||
| 972 | |||
| 973 | fn sumArray(s: []const u8) u32 { | ||
| 974 | var r: u32 = 0; | ||
| 975 | for (s) |e| | ||
| 976 | r += e; | ||
| 977 | return r; | ||
| 978 | } | ||
| 979 | |||
| 980 | test "Random range" { | ||
| 981 | var prng = DefaultPrng.init(0); | ||
| 982 | testRange(&prng.random, -4, 3); | ||
| 983 | testRange(&prng.random, -4, -1); | ||
| 984 | testRange(&prng.random, 10, 14); | ||
| 985 | testRange(&prng.random, -0x80, 0x7f); | ||
| 986 | } | ||
| 987 | |||
| 988 | fn testRange(r: *Random, start: i8, end: i8) void { | ||
| 989 | testRangeBias(r, start, end, true); | ||
| 990 | testRangeBias(r, start, end, false); | ||
| 991 | } | ||
| 992 | fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void { | ||
| 993 | const count = @intCast(usize, i32(end) - i32(start)); | ||
| 994 | var values_buffer = []bool{false} ** 0x100; | ||
| 995 | const values = values_buffer[0..count]; | ||
| 996 | var i: usize = 0; | ||
| 997 | while (i < count) { | ||
| 998 | const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); | ||
| 999 | const index = @intCast(usize, value - start); | ||
| 1000 | if (!values[index]) { | ||
| 1001 | i += 1; | ||
| 1002 | values[index] = true; | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | } | ||
std/rand/index.zig deleted-1005| ... | @@ -1,1005 +0,0 @@ | ||
| 1 | // The engines provided here should be initialized from an external source. For now, getRandomBytes | ||
| 2 | // from the os package is the most suitable. Be sure to use a CSPRNG when required, otherwise using | ||
| 3 | // a normal PRNG will be faster and use substantially less stack space. | ||
| 4 | // | ||
| 5 | // ``` | ||
| 6 | // var buf: [8]u8 = undefined; | ||
| 7 | // try std.os.getRandomBytes(buf[0..]); | ||
| 8 | // const seed = mem.readIntSliceLittle(u64, buf[0..8]); | ||
| 9 | // | ||
| 10 | // var r = DefaultPrng.init(seed); | ||
| 11 | // | ||
| 12 | // const s = r.random.int(u64); | ||
| 13 | // ``` | ||
| 14 | // | ||
| 15 | // TODO(tiehuis): Benchmark these against other reference implementations. | ||
| 16 | |||
| 17 | const std = @import("../index.zig"); | ||
| 18 | const builtin = @import("builtin"); | ||
| 19 | const assert = std.debug.assert; | ||
| 20 | const expect = std.testing.expect; | ||
| 21 | const mem = std.mem; | ||
| 22 | const math = std.math; | ||
| 23 | const ziggurat = @import("ziggurat.zig"); | ||
| 24 | const maxInt = std.math.maxInt; | ||
| 25 | |||
| 26 | // When you need fast unbiased random numbers | ||
| 27 | pub const DefaultPrng = Xoroshiro128; | ||
| 28 | |||
| 29 | // When you need cryptographically secure random numbers | ||
| 30 | pub const DefaultCsprng = Isaac64; | ||
| 31 | |||
| 32 | pub const Random = struct { | ||
| 33 | fillFn: fn (r: *Random, buf: []u8) void, | ||
| 34 | |||
| 35 | /// Read random bytes into the specified buffer until full. | ||
| 36 | pub fn bytes(r: *Random, buf: []u8) void { | ||
| 37 | r.fillFn(r, buf); | ||
| 38 | } | ||
| 39 | |||
| 40 | pub fn boolean(r: *Random) bool { | ||
| 41 | return r.int(u1) != 0; | ||
| 42 | } | ||
| 43 | |||
| 44 | /// Returns a random int `i` such that `0 <= i <= maxInt(T)`. | ||
| 45 | /// `i` is evenly distributed. | ||
| 46 | pub fn int(r: *Random, comptime T: type) T { | ||
| 47 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 48 | const ByteAlignedT = @IntType(false, @divTrunc(T.bit_count + 7, 8) * 8); | ||
| 49 | |||
| 50 | var rand_bytes: [@sizeOf(ByteAlignedT)]u8 = undefined; | ||
| 51 | r.bytes(rand_bytes[0..]); | ||
| 52 | |||
| 53 | // use LE instead of native endian for better portability maybe? | ||
| 54 | // TODO: endian portability is pointless if the underlying prng isn't endian portable. | ||
| 55 | // TODO: document the endian portability of this library. | ||
| 56 | const byte_aligned_result = mem.readIntSliceLittle(ByteAlignedT, rand_bytes); | ||
| 57 | const unsigned_result = @truncate(UnsignedT, byte_aligned_result); | ||
| 58 | return @bitCast(T, unsigned_result); | ||
| 59 | } | ||
| 60 | |||
| 61 | /// Constant-time implementation off ::uintLessThan. | ||
| 62 | /// The results of this function may be biased. | ||
| 63 | pub fn uintLessThanBiased(r: *Random, comptime T: type, less_than: T) T { | ||
| 64 | comptime assert(T.is_signed == false); | ||
| 65 | comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! | ||
| 66 | assert(0 < less_than); | ||
| 67 | if (T.bit_count <= 32) { | ||
| 68 | return @intCast(T, limitRangeBiased(u32, r.int(u32), less_than)); | ||
| 69 | } else { | ||
| 70 | return @intCast(T, limitRangeBiased(u64, r.int(u64), less_than)); | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Returns an evenly distributed random unsigned integer `0 <= i < less_than`. | ||
| 75 | /// This function assumes that the underlying ::fillFn produces evenly distributed values. | ||
| 76 | /// Within this assumption, the runtime of this function is exponentially distributed. | ||
| 77 | /// If ::fillFn were backed by a true random generator, | ||
| 78 | /// the runtime of this function would technically be unbounded. | ||
| 79 | /// However, if ::fillFn is backed by any evenly distributed pseudo random number generator, | ||
| 80 | /// this function is guaranteed to return. | ||
| 81 | /// If you need deterministic runtime bounds, use `::uintLessThanBiased`. | ||
| 82 | pub fn uintLessThan(r: *Random, comptime T: type, less_than: T) T { | ||
| 83 | comptime assert(T.is_signed == false); | ||
| 84 | comptime assert(T.bit_count <= 64); // TODO: workaround: LLVM ERROR: Unsupported library call operation! | ||
| 85 | assert(0 < less_than); | ||
| 86 | // Small is typically u32 | ||
| 87 | const Small = @IntType(false, @divTrunc(T.bit_count + 31, 32) * 32); | ||
| 88 | // Large is typically u64 | ||
| 89 | const Large = @IntType(false, Small.bit_count * 2); | ||
| 90 | |||
| 91 | // adapted from: | ||
| 92 | // http://www.pcg-random.org/posts/bounded-rands.html | ||
| 93 | // "Lemire's (with an extra tweak from me)" | ||
| 94 | var x: Small = r.int(Small); | ||
| 95 | var m: Large = Large(x) * Large(less_than); | ||
| 96 | var l: Small = @truncate(Small, m); | ||
| 97 | if (l < less_than) { | ||
| 98 | // TODO: workaround for https://github.com/ziglang/zig/issues/1770 | ||
| 99 | // should be: | ||
| 100 | // var t: Small = -%less_than; | ||
| 101 | var t: Small = @bitCast(Small, -%@bitCast(@IntType(true, Small.bit_count), Small(less_than))); | ||
| 102 | |||
| 103 | if (t >= less_than) { | ||
| 104 | t -= less_than; | ||
| 105 | if (t >= less_than) { | ||
| 106 | t %= less_than; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | while (l < t) { | ||
| 110 | x = r.int(Small); | ||
| 111 | m = Large(x) * Large(less_than); | ||
| 112 | l = @truncate(Small, m); | ||
| 113 | } | ||
| 114 | } | ||
| 115 | return @intCast(T, m >> Small.bit_count); | ||
| 116 | } | ||
| 117 | |||
| 118 | /// Constant-time implementation off ::uintAtMost. | ||
| 119 | /// The results of this function may be biased. | ||
| 120 | pub fn uintAtMostBiased(r: *Random, comptime T: type, at_most: T) T { | ||
| 121 | assert(T.is_signed == false); | ||
| 122 | if (at_most == maxInt(T)) { | ||
| 123 | // have the full range | ||
| 124 | return r.int(T); | ||
| 125 | } | ||
| 126 | return r.uintLessThanBiased(T, at_most + 1); | ||
| 127 | } | ||
| 128 | |||
| 129 | /// Returns an evenly distributed random unsigned integer `0 <= i <= at_most`. | ||
| 130 | /// See ::uintLessThan, which this function uses in most cases, | ||
| 131 | /// for commentary on the runtime of this function. | ||
| 132 | pub fn uintAtMost(r: *Random, comptime T: type, at_most: T) T { | ||
| 133 | assert(T.is_signed == false); | ||
| 134 | if (at_most == maxInt(T)) { | ||
| 135 | // have the full range | ||
| 136 | return r.int(T); | ||
| 137 | } | ||
| 138 | return r.uintLessThan(T, at_most + 1); | ||
| 139 | } | ||
| 140 | |||
| 141 | /// Constant-time implementation off ::intRangeLessThan. | ||
| 142 | /// The results of this function may be biased. | ||
| 143 | pub fn intRangeLessThanBiased(r: *Random, comptime T: type, at_least: T, less_than: T) T { | ||
| 144 | assert(at_least < less_than); | ||
| 145 | if (T.is_signed) { | ||
| 146 | // Two's complement makes this math pretty easy. | ||
| 147 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 148 | const lo = @bitCast(UnsignedT, at_least); | ||
| 149 | const hi = @bitCast(UnsignedT, less_than); | ||
| 150 | const result = lo +% r.uintLessThanBiased(UnsignedT, hi -% lo); | ||
| 151 | return @bitCast(T, result); | ||
| 152 | } else { | ||
| 153 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 154 | return at_least + r.uintLessThanBiased(T, less_than - at_least); | ||
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | /// Returns an evenly distributed random integer `at_least <= i < less_than`. | ||
| 159 | /// See ::uintLessThan, which this function uses in most cases, | ||
| 160 | /// for commentary on the runtime of this function. | ||
| 161 | pub fn intRangeLessThan(r: *Random, comptime T: type, at_least: T, less_than: T) T { | ||
| 162 | assert(at_least < less_than); | ||
| 163 | if (T.is_signed) { | ||
| 164 | // Two's complement makes this math pretty easy. | ||
| 165 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 166 | const lo = @bitCast(UnsignedT, at_least); | ||
| 167 | const hi = @bitCast(UnsignedT, less_than); | ||
| 168 | const result = lo +% r.uintLessThan(UnsignedT, hi -% lo); | ||
| 169 | return @bitCast(T, result); | ||
| 170 | } else { | ||
| 171 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 172 | return at_least + r.uintLessThan(T, less_than - at_least); | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | /// Constant-time implementation off ::intRangeAtMostBiased. | ||
| 177 | /// The results of this function may be biased. | ||
| 178 | pub fn intRangeAtMostBiased(r: *Random, comptime T: type, at_least: T, at_most: T) T { | ||
| 179 | assert(at_least <= at_most); | ||
| 180 | if (T.is_signed) { | ||
| 181 | // Two's complement makes this math pretty easy. | ||
| 182 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 183 | const lo = @bitCast(UnsignedT, at_least); | ||
| 184 | const hi = @bitCast(UnsignedT, at_most); | ||
| 185 | const result = lo +% r.uintAtMostBiased(UnsignedT, hi -% lo); | ||
| 186 | return @bitCast(T, result); | ||
| 187 | } else { | ||
| 188 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 189 | return at_least + r.uintAtMostBiased(T, at_most - at_least); | ||
| 190 | } | ||
| 191 | } | ||
| 192 | |||
| 193 | /// Returns an evenly distributed random integer `at_least <= i <= at_most`. | ||
| 194 | /// See ::uintLessThan, which this function uses in most cases, | ||
| 195 | /// for commentary on the runtime of this function. | ||
| 196 | pub fn intRangeAtMost(r: *Random, comptime T: type, at_least: T, at_most: T) T { | ||
| 197 | assert(at_least <= at_most); | ||
| 198 | if (T.is_signed) { | ||
| 199 | // Two's complement makes this math pretty easy. | ||
| 200 | const UnsignedT = @IntType(false, T.bit_count); | ||
| 201 | const lo = @bitCast(UnsignedT, at_least); | ||
| 202 | const hi = @bitCast(UnsignedT, at_most); | ||
| 203 | const result = lo +% r.uintAtMost(UnsignedT, hi -% lo); | ||
| 204 | return @bitCast(T, result); | ||
| 205 | } else { | ||
| 206 | // The signed implementation would work fine, but we can use stricter arithmetic operators here. | ||
| 207 | return at_least + r.uintAtMost(T, at_most - at_least); | ||
| 208 | } | ||
| 209 | } | ||
| 210 | |||
| 211 | /// TODO: deprecated. use ::boolean or ::int instead. | ||
| 212 | pub fn scalar(r: *Random, comptime T: type) T { | ||
| 213 | return if (T == bool) r.boolean() else r.int(T); | ||
| 214 | } | ||
| 215 | |||
| 216 | /// TODO: deprecated. renamed to ::intRangeLessThan | ||
| 217 | pub fn range(r: *Random, comptime T: type, start: T, end: T) T { | ||
| 218 | return r.intRangeLessThan(T, start, end); | ||
| 219 | } | ||
| 220 | |||
| 221 | /// Return a floating point value evenly distributed in the range [0, 1). | ||
| 222 | pub fn float(r: *Random, comptime T: type) T { | ||
| 223 | // Generate a uniform value between [1, 2) and scale down to [0, 1). | ||
| 224 | // Note: The lowest mantissa bit is always set to 0 so we only use half the available range. | ||
| 225 | switch (T) { | ||
| 226 | f32 => { | ||
| 227 | const s = r.int(u32); | ||
| 228 | const repr = (0x7f << 23) | (s >> 9); | ||
| 229 | return @bitCast(f32, repr) - 1.0; | ||
| 230 | }, | ||
| 231 | f64 => { | ||
| 232 | const s = r.int(u64); | ||
| 233 | const repr = (0x3ff << 52) | (s >> 12); | ||
| 234 | return @bitCast(f64, repr) - 1.0; | ||
| 235 | }, | ||
| 236 | else => @compileError("unknown floating point type"), | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | /// Return a floating point value normally distributed with mean = 0, stddev = 1. | ||
| 241 | /// | ||
| 242 | /// To use different parameters, use: floatNorm(...) * desiredStddev + desiredMean. | ||
| 243 | pub fn floatNorm(r: *Random, comptime T: type) T { | ||
| 244 | const value = ziggurat.next_f64(r, ziggurat.NormDist); | ||
| 245 | switch (T) { | ||
| 246 | f32 => return @floatCast(f32, value), | ||
| 247 | f64 => return value, | ||
| 248 | else => @compileError("unknown floating point type"), | ||
| 249 | } | ||
| 250 | } | ||
| 251 | |||
| 252 | /// Return an exponentially distributed float with a rate parameter of 1. | ||
| 253 | /// | ||
| 254 | /// To use a different rate parameter, use: floatExp(...) / desiredRate. | ||
| 255 | pub fn floatExp(r: *Random, comptime T: type) T { | ||
| 256 | const value = ziggurat.next_f64(r, ziggurat.ExpDist); | ||
| 257 | switch (T) { | ||
| 258 | f32 => return @floatCast(f32, value), | ||
| 259 | f64 => return value, | ||
| 260 | else => @compileError("unknown floating point type"), | ||
| 261 | } | ||
| 262 | } | ||
| 263 | |||
| 264 | /// Shuffle a slice into a random order. | ||
| 265 | pub fn shuffle(r: *Random, comptime T: type, buf: []T) void { | ||
| 266 | if (buf.len < 2) { | ||
| 267 | return; | ||
| 268 | } | ||
| 269 | |||
| 270 | var i: usize = 0; | ||
| 271 | while (i < buf.len - 1) : (i += 1) { | ||
| 272 | const j = r.intRangeLessThan(usize, i, buf.len); | ||
| 273 | mem.swap(T, &buf[i], &buf[j]); | ||
| 274 | } | ||
| 275 | } | ||
| 276 | }; | ||
| 277 | |||
| 278 | /// Convert a random integer 0 <= random_int <= maxValue(T), | ||
| 279 | /// into an integer 0 <= result < less_than. | ||
| 280 | /// This function introduces a minor bias. | ||
| 281 | pub fn limitRangeBiased(comptime T: type, random_int: T, less_than: T) T { | ||
| 282 | comptime assert(T.is_signed == false); | ||
| 283 | const T2 = @IntType(false, T.bit_count * 2); | ||
| 284 | |||
| 285 | // adapted from: | ||
| 286 | // http://www.pcg-random.org/posts/bounded-rands.html | ||
| 287 | // "Integer Multiplication (Biased)" | ||
| 288 | var m: T2 = T2(random_int) * T2(less_than); | ||
| 289 | return @intCast(T, m >> T.bit_count); | ||
| 290 | } | ||
| 291 | |||
| 292 | const SequentialPrng = struct { | ||
| 293 | const Self = @This(); | ||
| 294 | random: Random, | ||
| 295 | next_value: u8, | ||
| 296 | |||
| 297 | pub fn init() Self { | ||
| 298 | return Self{ | ||
| 299 | .random = Random{ .fillFn = fill }, | ||
| 300 | .next_value = 0, | ||
| 301 | }; | ||
| 302 | } | ||
| 303 | |||
| 304 | fn fill(r: *Random, buf: []u8) void { | ||
| 305 | const self = @fieldParentPtr(Self, "random", r); | ||
| 306 | for (buf) |*b| { | ||
| 307 | b.* = self.next_value; | ||
| 308 | } | ||
| 309 | self.next_value +%= 1; | ||
| 310 | } | ||
| 311 | }; | ||
| 312 | |||
| 313 | test "Random int" { | ||
| 314 | testRandomInt(); | ||
| 315 | comptime testRandomInt(); | ||
| 316 | } | ||
| 317 | fn testRandomInt() void { | ||
| 318 | var r = SequentialPrng.init(); | ||
| 319 | |||
| 320 | expect(r.random.int(u0) == 0); | ||
| 321 | |||
| 322 | r.next_value = 0; | ||
| 323 | expect(r.random.int(u1) == 0); | ||
| 324 | expect(r.random.int(u1) == 1); | ||
| 325 | expect(r.random.int(u2) == 2); | ||
| 326 | expect(r.random.int(u2) == 3); | ||
| 327 | expect(r.random.int(u2) == 0); | ||
| 328 | |||
| 329 | r.next_value = 0xff; | ||
| 330 | expect(r.random.int(u8) == 0xff); | ||
| 331 | r.next_value = 0x11; | ||
| 332 | expect(r.random.int(u8) == 0x11); | ||
| 333 | |||
| 334 | r.next_value = 0xff; | ||
| 335 | expect(r.random.int(u32) == 0xffffffff); | ||
| 336 | r.next_value = 0x11; | ||
| 337 | expect(r.random.int(u32) == 0x11111111); | ||
| 338 | |||
| 339 | r.next_value = 0xff; | ||
| 340 | expect(r.random.int(i32) == -1); | ||
| 341 | r.next_value = 0x11; | ||
| 342 | expect(r.random.int(i32) == 0x11111111); | ||
| 343 | |||
| 344 | r.next_value = 0xff; | ||
| 345 | expect(r.random.int(i8) == -1); | ||
| 346 | r.next_value = 0x11; | ||
| 347 | expect(r.random.int(i8) == 0x11); | ||
| 348 | |||
| 349 | r.next_value = 0xff; | ||
| 350 | expect(r.random.int(u33) == 0x1ffffffff); | ||
| 351 | r.next_value = 0xff; | ||
| 352 | expect(r.random.int(i1) == -1); | ||
| 353 | r.next_value = 0xff; | ||
| 354 | expect(r.random.int(i2) == -1); | ||
| 355 | r.next_value = 0xff; | ||
| 356 | expect(r.random.int(i33) == -1); | ||
| 357 | } | ||
| 358 | |||
| 359 | test "Random boolean" { | ||
| 360 | testRandomBoolean(); | ||
| 361 | comptime testRandomBoolean(); | ||
| 362 | } | ||
| 363 | fn testRandomBoolean() void { | ||
| 364 | var r = SequentialPrng.init(); | ||
| 365 | expect(r.random.boolean() == false); | ||
| 366 | expect(r.random.boolean() == true); | ||
| 367 | expect(r.random.boolean() == false); | ||
| 368 | expect(r.random.boolean() == true); | ||
| 369 | } | ||
| 370 | |||
| 371 | test "Random intLessThan" { | ||
| 372 | @setEvalBranchQuota(10000); | ||
| 373 | testRandomIntLessThan(); | ||
| 374 | comptime testRandomIntLessThan(); | ||
| 375 | } | ||
| 376 | fn testRandomIntLessThan() void { | ||
| 377 | var r = SequentialPrng.init(); | ||
| 378 | r.next_value = 0xff; | ||
| 379 | expect(r.random.uintLessThan(u8, 4) == 3); | ||
| 380 | expect(r.next_value == 0); | ||
| 381 | expect(r.random.uintLessThan(u8, 4) == 0); | ||
| 382 | expect(r.next_value == 1); | ||
| 383 | |||
| 384 | r.next_value = 0; | ||
| 385 | expect(r.random.uintLessThan(u64, 32) == 0); | ||
| 386 | |||
| 387 | // trigger the bias rejection code path | ||
| 388 | r.next_value = 0; | ||
| 389 | expect(r.random.uintLessThan(u8, 3) == 0); | ||
| 390 | // verify we incremented twice | ||
| 391 | expect(r.next_value == 2); | ||
| 392 | |||
| 393 | r.next_value = 0xff; | ||
| 394 | expect(r.random.intRangeLessThan(u8, 0, 0x80) == 0x7f); | ||
| 395 | r.next_value = 0xff; | ||
| 396 | expect(r.random.intRangeLessThan(u8, 0x7f, 0xff) == 0xfe); | ||
| 397 | |||
| 398 | r.next_value = 0xff; | ||
| 399 | expect(r.random.intRangeLessThan(i8, 0, 0x40) == 0x3f); | ||
| 400 | r.next_value = 0xff; | ||
| 401 | expect(r.random.intRangeLessThan(i8, -0x40, 0x40) == 0x3f); | ||
| 402 | r.next_value = 0xff; | ||
| 403 | expect(r.random.intRangeLessThan(i8, -0x80, 0) == -1); | ||
| 404 | |||
| 405 | r.next_value = 0xff; | ||
| 406 | expect(r.random.intRangeLessThan(i3, -4, 0) == -1); | ||
| 407 | r.next_value = 0xff; | ||
| 408 | expect(r.random.intRangeLessThan(i3, -2, 2) == 1); | ||
| 409 | } | ||
| 410 | |||
| 411 | test "Random intAtMost" { | ||
| 412 | @setEvalBranchQuota(10000); | ||
| 413 | testRandomIntAtMost(); | ||
| 414 | comptime testRandomIntAtMost(); | ||
| 415 | } | ||
| 416 | fn testRandomIntAtMost() void { | ||
| 417 | var r = SequentialPrng.init(); | ||
| 418 | r.next_value = 0xff; | ||
| 419 | expect(r.random.uintAtMost(u8, 3) == 3); | ||
| 420 | expect(r.next_value == 0); | ||
| 421 | expect(r.random.uintAtMost(u8, 3) == 0); | ||
| 422 | |||
| 423 | // trigger the bias rejection code path | ||
| 424 | r.next_value = 0; | ||
| 425 | expect(r.random.uintAtMost(u8, 2) == 0); | ||
| 426 | // verify we incremented twice | ||
| 427 | expect(r.next_value == 2); | ||
| 428 | |||
| 429 | r.next_value = 0xff; | ||
| 430 | expect(r.random.intRangeAtMost(u8, 0, 0x7f) == 0x7f); | ||
| 431 | r.next_value = 0xff; | ||
| 432 | expect(r.random.intRangeAtMost(u8, 0x7f, 0xfe) == 0xfe); | ||
| 433 | |||
| 434 | r.next_value = 0xff; | ||
| 435 | expect(r.random.intRangeAtMost(i8, 0, 0x3f) == 0x3f); | ||
| 436 | r.next_value = 0xff; | ||
| 437 | expect(r.random.intRangeAtMost(i8, -0x40, 0x3f) == 0x3f); | ||
| 438 | r.next_value = 0xff; | ||
| 439 | expect(r.random.intRangeAtMost(i8, -0x80, -1) == -1); | ||
| 440 | |||
| 441 | r.next_value = 0xff; | ||
| 442 | expect(r.random.intRangeAtMost(i3, -4, -1) == -1); | ||
| 443 | r.next_value = 0xff; | ||
| 444 | expect(r.random.intRangeAtMost(i3, -2, 1) == 1); | ||
| 445 | |||
| 446 | expect(r.random.uintAtMost(u0, 0) == 0); | ||
| 447 | } | ||
| 448 | |||
| 449 | test "Random Biased" { | ||
| 450 | var r = DefaultPrng.init(0); | ||
| 451 | // Not thoroughly checking the logic here. | ||
| 452 | // Just want to execute all the paths with different types. | ||
| 453 | |||
| 454 | expect(r.random.uintLessThanBiased(u1, 1) == 0); | ||
| 455 | expect(r.random.uintLessThanBiased(u32, 10) < 10); | ||
| 456 | expect(r.random.uintLessThanBiased(u64, 20) < 20); | ||
| 457 | |||
| 458 | expect(r.random.uintAtMostBiased(u0, 0) == 0); | ||
| 459 | expect(r.random.uintAtMostBiased(u1, 0) <= 0); | ||
| 460 | expect(r.random.uintAtMostBiased(u32, 10) <= 10); | ||
| 461 | expect(r.random.uintAtMostBiased(u64, 20) <= 20); | ||
| 462 | |||
| 463 | expect(r.random.intRangeLessThanBiased(u1, 0, 1) == 0); | ||
| 464 | expect(r.random.intRangeLessThanBiased(i1, -1, 0) == -1); | ||
| 465 | expect(r.random.intRangeLessThanBiased(u32, 10, 20) >= 10); | ||
| 466 | expect(r.random.intRangeLessThanBiased(i32, 10, 20) >= 10); | ||
| 467 | expect(r.random.intRangeLessThanBiased(u64, 20, 40) >= 20); | ||
| 468 | expect(r.random.intRangeLessThanBiased(i64, 20, 40) >= 20); | ||
| 469 | |||
| 470 | // uncomment for broken module error: | ||
| 471 | //expect(r.random.intRangeAtMostBiased(u0, 0, 0) == 0); | ||
| 472 | expect(r.random.intRangeAtMostBiased(u1, 0, 1) >= 0); | ||
| 473 | expect(r.random.intRangeAtMostBiased(i1, -1, 0) >= -1); | ||
| 474 | expect(r.random.intRangeAtMostBiased(u32, 10, 20) >= 10); | ||
| 475 | expect(r.random.intRangeAtMostBiased(i32, 10, 20) >= 10); | ||
| 476 | expect(r.random.intRangeAtMostBiased(u64, 20, 40) >= 20); | ||
| 477 | expect(r.random.intRangeAtMostBiased(i64, 20, 40) >= 20); | ||
| 478 | } | ||
| 479 | |||
| 480 | // Generator to extend 64-bit seed values into longer sequences. | ||
| 481 | // | ||
| 482 | // The number of cycles is thus limited to 64-bits regardless of the engine, but this | ||
| 483 | // is still plenty for practical purposes. | ||
| 484 | const SplitMix64 = struct { | ||
| 485 | s: u64, | ||
| 486 | |||
| 487 | pub fn init(seed: u64) SplitMix64 { | ||
| 488 | return SplitMix64{ .s = seed }; | ||
| 489 | } | ||
| 490 | |||
| 491 | pub fn next(self: *SplitMix64) u64 { | ||
| 492 | self.s +%= 0x9e3779b97f4a7c15; | ||
| 493 | |||
| 494 | var z = self.s; | ||
| 495 | z = (z ^ (z >> 30)) *% 0xbf58476d1ce4e5b9; | ||
| 496 | z = (z ^ (z >> 27)) *% 0x94d049bb133111eb; | ||
| 497 | return z ^ (z >> 31); | ||
| 498 | } | ||
| 499 | }; | ||
| 500 | |||
| 501 | test "splitmix64 sequence" { | ||
| 502 | var r = SplitMix64.init(0xaeecf86f7878dd75); | ||
| 503 | |||
| 504 | const seq = []const u64{ | ||
| 505 | 0x5dbd39db0178eb44, | ||
| 506 | 0xa9900fb66b397da3, | ||
| 507 | 0x5c1a28b1aeebcf5c, | ||
| 508 | 0x64a963238f776912, | ||
| 509 | 0xc6d4177b21d1c0ab, | ||
| 510 | 0xb2cbdbdb5ea35394, | ||
| 511 | }; | ||
| 512 | |||
| 513 | for (seq) |s| { | ||
| 514 | expect(s == r.next()); | ||
| 515 | } | ||
| 516 | } | ||
| 517 | |||
| 518 | // PCG32 - http://www.pcg-random.org/ | ||
| 519 | // | ||
| 520 | // PRNG | ||
| 521 | pub const Pcg = struct { | ||
| 522 | const default_multiplier = 6364136223846793005; | ||
| 523 | |||
| 524 | random: Random, | ||
| 525 | |||
| 526 | s: u64, | ||
| 527 | i: u64, | ||
| 528 | |||
| 529 | pub fn init(init_s: u64) Pcg { | ||
| 530 | var pcg = Pcg{ | ||
| 531 | .random = Random{ .fillFn = fill }, | ||
| 532 | .s = undefined, | ||
| 533 | .i = undefined, | ||
| 534 | }; | ||
| 535 | |||
| 536 | pcg.seed(init_s); | ||
| 537 | return pcg; | ||
| 538 | } | ||
| 539 | |||
| 540 | fn next(self: *Pcg) u32 { | ||
| 541 | const l = self.s; | ||
| 542 | self.s = l *% default_multiplier +% (self.i | 1); | ||
| 543 | |||
| 544 | const xor_s = @truncate(u32, ((l >> 18) ^ l) >> 27); | ||
| 545 | const rot = @intCast(u32, l >> 59); | ||
| 546 | |||
| 547 | return (xor_s >> @intCast(u5, rot)) | (xor_s << @intCast(u5, (0 -% rot) & 31)); | ||
| 548 | } | ||
| 549 | |||
| 550 | fn seed(self: *Pcg, init_s: u64) void { | ||
| 551 | // Pcg requires 128-bits of seed. | ||
| 552 | var gen = SplitMix64.init(init_s); | ||
| 553 | self.seedTwo(gen.next(), gen.next()); | ||
| 554 | } | ||
| 555 | |||
| 556 | fn seedTwo(self: *Pcg, init_s: u64, init_i: u64) void { | ||
| 557 | self.s = 0; | ||
| 558 | self.i = (init_s << 1) | 1; | ||
| 559 | self.s = self.s *% default_multiplier +% self.i; | ||
| 560 | self.s +%= init_i; | ||
| 561 | self.s = self.s *% default_multiplier +% self.i; | ||
| 562 | } | ||
| 563 | |||
| 564 | fn fill(r: *Random, buf: []u8) void { | ||
| 565 | const self = @fieldParentPtr(Pcg, "random", r); | ||
| 566 | |||
| 567 | var i: usize = 0; | ||
| 568 | const aligned_len = buf.len - (buf.len & 7); | ||
| 569 | |||
| 570 | // Complete 4 byte segments. | ||
| 571 | while (i < aligned_len) : (i += 4) { | ||
| 572 | var n = self.next(); | ||
| 573 | comptime var j: usize = 0; | ||
| 574 | inline while (j < 4) : (j += 1) { | ||
| 575 | buf[i + j] = @truncate(u8, n); | ||
| 576 | n >>= 8; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | |||
| 580 | // Remaining. (cuts the stream) | ||
| 581 | if (i != buf.len) { | ||
| 582 | var n = self.next(); | ||
| 583 | while (i < buf.len) : (i += 1) { | ||
| 584 | buf[i] = @truncate(u8, n); | ||
| 585 | n >>= 4; | ||
| 586 | } | ||
| 587 | } | ||
| 588 | } | ||
| 589 | }; | ||
| 590 | |||
| 591 | test "pcg sequence" { | ||
| 592 | var r = Pcg.init(0); | ||
| 593 | const s0: u64 = 0x9394bf54ce5d79de; | ||
| 594 | const s1: u64 = 0x84e9c579ef59bbf7; | ||
| 595 | r.seedTwo(s0, s1); | ||
| 596 | |||
| 597 | const seq = []const u32{ | ||
| 598 | 2881561918, | ||
| 599 | 3063928540, | ||
| 600 | 1199791034, | ||
| 601 | 2487695858, | ||
| 602 | 1479648952, | ||
| 603 | 3247963454, | ||
| 604 | }; | ||
| 605 | |||
| 606 | for (seq) |s| { | ||
| 607 | expect(s == r.next()); | ||
| 608 | } | ||
| 609 | } | ||
| 610 | |||
| 611 | // Xoroshiro128+ - http://xoroshiro.di.unimi.it/ | ||
| 612 | // | ||
| 613 | // PRNG | ||
| 614 | pub const Xoroshiro128 = struct { | ||
| 615 | random: Random, | ||
| 616 | |||
| 617 | s: [2]u64, | ||
| 618 | |||
| 619 | pub fn init(init_s: u64) Xoroshiro128 { | ||
| 620 | var x = Xoroshiro128{ | ||
| 621 | .random = Random{ .fillFn = fill }, | ||
| 622 | .s = undefined, | ||
| 623 | }; | ||
| 624 | |||
| 625 | x.seed(init_s); | ||
| 626 | return x; | ||
| 627 | } | ||
| 628 | |||
| 629 | fn next(self: *Xoroshiro128) u64 { | ||
| 630 | const s0 = self.s[0]; | ||
| 631 | var s1 = self.s[1]; | ||
| 632 | const r = s0 +% s1; | ||
| 633 | |||
| 634 | s1 ^= s0; | ||
| 635 | self.s[0] = math.rotl(u64, s0, u8(55)) ^ s1 ^ (s1 << 14); | ||
| 636 | self.s[1] = math.rotl(u64, s1, u8(36)); | ||
| 637 | |||
| 638 | return r; | ||
| 639 | } | ||
| 640 | |||
| 641 | // Skip 2^64 places ahead in the sequence | ||
| 642 | fn jump(self: *Xoroshiro128) void { | ||
| 643 | var s0: u64 = 0; | ||
| 644 | var s1: u64 = 0; | ||
| 645 | |||
| 646 | const table = []const u64{ | ||
| 647 | 0xbeac0467eba5facb, | ||
| 648 | 0xd86b048b86aa9922, | ||
| 649 | }; | ||
| 650 | |||
| 651 | inline for (table) |entry| { | ||
| 652 | var b: usize = 0; | ||
| 653 | while (b < 64) : (b += 1) { | ||
| 654 | if ((entry & (u64(1) << @intCast(u6, b))) != 0) { | ||
| 655 | s0 ^= self.s[0]; | ||
| 656 | s1 ^= self.s[1]; | ||
| 657 | } | ||
| 658 | _ = self.next(); | ||
| 659 | } | ||
| 660 | } | ||
| 661 | |||
| 662 | self.s[0] = s0; | ||
| 663 | self.s[1] = s1; | ||
| 664 | } | ||
| 665 | |||
| 666 | fn seed(self: *Xoroshiro128, init_s: u64) void { | ||
| 667 | // Xoroshiro requires 128-bits of seed. | ||
| 668 | var gen = SplitMix64.init(init_s); | ||
| 669 | |||
| 670 | self.s[0] = gen.next(); | ||
| 671 | self.s[1] = gen.next(); | ||
| 672 | } | ||
| 673 | |||
| 674 | fn fill(r: *Random, buf: []u8) void { | ||
| 675 | const self = @fieldParentPtr(Xoroshiro128, "random", r); | ||
| 676 | |||
| 677 | var i: usize = 0; | ||
| 678 | const aligned_len = buf.len - (buf.len & 7); | ||
| 679 | |||
| 680 | // Complete 8 byte segments. | ||
| 681 | while (i < aligned_len) : (i += 8) { | ||
| 682 | var n = self.next(); | ||
| 683 | comptime var j: usize = 0; | ||
| 684 | inline while (j < 8) : (j += 1) { | ||
| 685 | buf[i + j] = @truncate(u8, n); | ||
| 686 | n >>= 8; | ||
| 687 | } | ||
| 688 | } | ||
| 689 | |||
| 690 | // Remaining. (cuts the stream) | ||
| 691 | if (i != buf.len) { | ||
| 692 | var n = self.next(); | ||
| 693 | while (i < buf.len) : (i += 1) { | ||
| 694 | buf[i] = @truncate(u8, n); | ||
| 695 | n >>= 8; | ||
| 696 | } | ||
| 697 | } | ||
| 698 | } | ||
| 699 | }; | ||
| 700 | |||
| 701 | test "xoroshiro sequence" { | ||
| 702 | var r = Xoroshiro128.init(0); | ||
| 703 | r.s[0] = 0xaeecf86f7878dd75; | ||
| 704 | r.s[1] = 0x01cd153642e72622; | ||
| 705 | |||
| 706 | const seq1 = []const u64{ | ||
| 707 | 0xb0ba0da5bb600397, | ||
| 708 | 0x18a08afde614dccc, | ||
| 709 | 0xa2635b956a31b929, | ||
| 710 | 0xabe633c971efa045, | ||
| 711 | 0x9ac19f9706ca3cac, | ||
| 712 | 0xf62b426578c1e3fb, | ||
| 713 | }; | ||
| 714 | |||
| 715 | for (seq1) |s| { | ||
| 716 | expect(s == r.next()); | ||
| 717 | } | ||
| 718 | |||
| 719 | r.jump(); | ||
| 720 | |||
| 721 | const seq2 = []const u64{ | ||
| 722 | 0x95344a13556d3e22, | ||
| 723 | 0xb4fb32dafa4d00df, | ||
| 724 | 0xb2011d9ccdcfe2dd, | ||
| 725 | 0x05679a9b2119b908, | ||
| 726 | 0xa860a1da7c9cd8a0, | ||
| 727 | 0x658a96efe3f86550, | ||
| 728 | }; | ||
| 729 | |||
| 730 | for (seq2) |s| { | ||
| 731 | expect(s == r.next()); | ||
| 732 | } | ||
| 733 | } | ||
| 734 | |||
| 735 | // ISAAC64 - http://www.burtleburtle.net/bob/rand/isaacafa.html | ||
| 736 | // | ||
| 737 | // CSPRNG | ||
| 738 | // | ||
| 739 | // Follows the general idea of the implementation from here with a few shortcuts. | ||
| 740 | // https://doc.rust-lang.org/rand/src/rand/prng/isaac64.rs.html | ||
| 741 | pub const Isaac64 = struct { | ||
| 742 | random: Random, | ||
| 743 | |||
| 744 | r: [256]u64, | ||
| 745 | m: [256]u64, | ||
| 746 | a: u64, | ||
| 747 | b: u64, | ||
| 748 | c: u64, | ||
| 749 | i: usize, | ||
| 750 | |||
| 751 | pub fn init(init_s: u64) Isaac64 { | ||
| 752 | var isaac = Isaac64{ | ||
| 753 | .random = Random{ .fillFn = fill }, | ||
| 754 | .r = undefined, | ||
| 755 | .m = undefined, | ||
| 756 | .a = undefined, | ||
| 757 | .b = undefined, | ||
| 758 | .c = undefined, | ||
| 759 | .i = undefined, | ||
| 760 | }; | ||
| 761 | |||
| 762 | // seed == 0 => same result as the unseeded reference implementation | ||
| 763 | isaac.seed(init_s, 1); | ||
| 764 | return isaac; | ||
| 765 | } | ||
| 766 | |||
| 767 | fn step(self: *Isaac64, mix: u64, base: usize, comptime m1: usize, comptime m2: usize) void { | ||
| 768 | const x = self.m[base + m1]; | ||
| 769 | self.a = mix +% self.m[base + m2]; | ||
| 770 | |||
| 771 | const y = self.a +% self.b +% self.m[(x >> 3) % self.m.len]; | ||
| 772 | self.m[base + m1] = y; | ||
| 773 | |||
| 774 | self.b = x +% self.m[(y >> 11) % self.m.len]; | ||
| 775 | self.r[self.r.len - 1 - base - m1] = self.b; | ||
| 776 | } | ||
| 777 | |||
| 778 | fn refill(self: *Isaac64) void { | ||
| 779 | const midpoint = self.r.len / 2; | ||
| 780 | |||
| 781 | self.c +%= 1; | ||
| 782 | self.b +%= self.c; | ||
| 783 | |||
| 784 | { | ||
| 785 | var i: usize = 0; | ||
| 786 | while (i < midpoint) : (i += 4) { | ||
| 787 | self.step(~(self.a ^ (self.a << 21)), i + 0, 0, midpoint); | ||
| 788 | self.step(self.a ^ (self.a >> 5), i + 1, 0, midpoint); | ||
| 789 | self.step(self.a ^ (self.a << 12), i + 2, 0, midpoint); | ||
| 790 | self.step(self.a ^ (self.a >> 33), i + 3, 0, midpoint); | ||
| 791 | } | ||
| 792 | } | ||
| 793 | |||
| 794 | { | ||
| 795 | var i: usize = 0; | ||
| 796 | while (i < midpoint) : (i += 4) { | ||
| 797 | self.step(~(self.a ^ (self.a << 21)), i + 0, midpoint, 0); | ||
| 798 | self.step(self.a ^ (self.a >> 5), i + 1, midpoint, 0); | ||
| 799 | self.step(self.a ^ (self.a << 12), i + 2, midpoint, 0); | ||
| 800 | self.step(self.a ^ (self.a >> 33), i + 3, midpoint, 0); | ||
| 801 | } | ||
| 802 | } | ||
| 803 | |||
| 804 | self.i = 0; | ||
| 805 | } | ||
| 806 | |||
| 807 | fn next(self: *Isaac64) u64 { | ||
| 808 | if (self.i >= self.r.len) { | ||
| 809 | self.refill(); | ||
| 810 | } | ||
| 811 | |||
| 812 | const value = self.r[self.i]; | ||
| 813 | self.i += 1; | ||
| 814 | return value; | ||
| 815 | } | ||
| 816 | |||
| 817 | fn seed(self: *Isaac64, init_s: u64, comptime rounds: usize) void { | ||
| 818 | // We ignore the multi-pass requirement since we don't currently expose full access to | ||
| 819 | // seeding the self.m array completely. | ||
| 820 | mem.set(u64, self.m[0..], 0); | ||
| 821 | self.m[0] = init_s; | ||
| 822 | |||
| 823 | // prescrambled golden ratio constants | ||
| 824 | var a = []const u64{ | ||
| 825 | 0x647c4677a2884b7c, | ||
| 826 | 0xb9f8b322c73ac862, | ||
| 827 | 0x8c0ea5053d4712a0, | ||
| 828 | 0xb29b2e824a595524, | ||
| 829 | 0x82f053db8355e0ce, | ||
| 830 | 0x48fe4a0fa5a09315, | ||
| 831 | 0xae985bf2cbfc89ed, | ||
| 832 | 0x98f5704f6c44c0ab, | ||
| 833 | }; | ||
| 834 | |||
| 835 | comptime var i: usize = 0; | ||
| 836 | inline while (i < rounds) : (i += 1) { | ||
| 837 | var j: usize = 0; | ||
| 838 | while (j < self.m.len) : (j += 8) { | ||
| 839 | comptime var x1: usize = 0; | ||
| 840 | inline while (x1 < 8) : (x1 += 1) { | ||
| 841 | a[x1] +%= self.m[j + x1]; | ||
| 842 | } | ||
| 843 | |||
| 844 | a[0] -%= a[4]; | ||
| 845 | a[5] ^= a[7] >> 9; | ||
| 846 | a[7] +%= a[0]; | ||
| 847 | a[1] -%= a[5]; | ||
| 848 | a[6] ^= a[0] << 9; | ||
| 849 | a[0] +%= a[1]; | ||
| 850 | a[2] -%= a[6]; | ||
| 851 | a[7] ^= a[1] >> 23; | ||
| 852 | a[1] +%= a[2]; | ||
| 853 | a[3] -%= a[7]; | ||
| 854 | a[0] ^= a[2] << 15; | ||
| 855 | a[2] +%= a[3]; | ||
| 856 | a[4] -%= a[0]; | ||
| 857 | a[1] ^= a[3] >> 14; | ||
| 858 | a[3] +%= a[4]; | ||
| 859 | a[5] -%= a[1]; | ||
| 860 | a[2] ^= a[4] << 20; | ||
| 861 | a[4] +%= a[5]; | ||
| 862 | a[6] -%= a[2]; | ||
| 863 | a[3] ^= a[5] >> 17; | ||
| 864 | a[5] +%= a[6]; | ||
| 865 | a[7] -%= a[3]; | ||
| 866 | a[4] ^= a[6] << 14; | ||
| 867 | a[6] +%= a[7]; | ||
| 868 | |||
| 869 | comptime var x2: usize = 0; | ||
| 870 | inline while (x2 < 8) : (x2 += 1) { | ||
| 871 | self.m[j + x2] = a[x2]; | ||
| 872 | } | ||
| 873 | } | ||
| 874 | } | ||
| 875 | |||
| 876 | mem.set(u64, self.r[0..], 0); | ||
| 877 | self.a = 0; | ||
| 878 | self.b = 0; | ||
| 879 | self.c = 0; | ||
| 880 | self.i = self.r.len; // trigger refill on first value | ||
| 881 | } | ||
| 882 | |||
| 883 | fn fill(r: *Random, buf: []u8) void { | ||
| 884 | const self = @fieldParentPtr(Isaac64, "random", r); | ||
| 885 | |||
| 886 | var i: usize = 0; | ||
| 887 | const aligned_len = buf.len - (buf.len & 7); | ||
| 888 | |||
| 889 | // Fill complete 64-byte segments | ||
| 890 | while (i < aligned_len) : (i += 8) { | ||
| 891 | var n = self.next(); | ||
| 892 | comptime var j: usize = 0; | ||
| 893 | inline while (j < 8) : (j += 1) { | ||
| 894 | buf[i + j] = @truncate(u8, n); | ||
| 895 | n >>= 8; | ||
| 896 | } | ||
| 897 | } | ||
| 898 | |||
| 899 | // Fill trailing, ignoring excess (cut the stream). | ||
| 900 | if (i != buf.len) { | ||
| 901 | var n = self.next(); | ||
| 902 | while (i < buf.len) : (i += 1) { | ||
| 903 | buf[i] = @truncate(u8, n); | ||
| 904 | n >>= 8; | ||
| 905 | } | ||
| 906 | } | ||
| 907 | } | ||
| 908 | }; | ||
| 909 | |||
| 910 | test "isaac64 sequence" { | ||
| 911 | var r = Isaac64.init(0); | ||
| 912 | |||
| 913 | // from reference implementation | ||
| 914 | const seq = []const u64{ | ||
| 915 | 0xf67dfba498e4937c, | ||
| 916 | 0x84a5066a9204f380, | ||
| 917 | 0xfee34bd5f5514dbb, | ||
| 918 | 0x4d1664739b8f80d6, | ||
| 919 | 0x8607459ab52a14aa, | ||
| 920 | 0x0e78bc5a98529e49, | ||
| 921 | 0xfe5332822ad13777, | ||
| 922 | 0x556c27525e33d01a, | ||
| 923 | 0x08643ca615f3149f, | ||
| 924 | 0xd0771faf3cb04714, | ||
| 925 | 0x30e86f68a37b008d, | ||
| 926 | 0x3074ebc0488a3adf, | ||
| 927 | 0x270645ea7a2790bc, | ||
| 928 | 0x5601a0a8d3763c6a, | ||
| 929 | 0x2f83071f53f325dd, | ||
| 930 | 0xb9090f3d42d2d2ea, | ||
| 931 | }; | ||
| 932 | |||
| 933 | for (seq) |s| { | ||
| 934 | expect(s == r.next()); | ||
| 935 | } | ||
| 936 | } | ||
| 937 | |||
| 938 | // Actual Random helper function tests, pcg engine is assumed correct. | ||
| 939 | test "Random float" { | ||
| 940 | var prng = DefaultPrng.init(0); | ||
| 941 | |||
| 942 | var i: usize = 0; | ||
| 943 | while (i < 1000) : (i += 1) { | ||
| 944 | const val1 = prng.random.float(f32); | ||
| 945 | expect(val1 >= 0.0); | ||
| 946 | expect(val1 < 1.0); | ||
| 947 | |||
| 948 | const val2 = prng.random.float(f64); | ||
| 949 | expect(val2 >= 0.0); | ||
| 950 | expect(val2 < 1.0); | ||
| 951 | } | ||
| 952 | } | ||
| 953 | |||
| 954 | test "Random shuffle" { | ||
| 955 | var prng = DefaultPrng.init(0); | ||
| 956 | |||
| 957 | var seq = []const u8{ 0, 1, 2, 3, 4 }; | ||
| 958 | var seen = []bool{false} ** 5; | ||
| 959 | |||
| 960 | var i: usize = 0; | ||
| 961 | while (i < 1000) : (i += 1) { | ||
| 962 | prng.random.shuffle(u8, seq[0..]); | ||
| 963 | seen[seq[0]] = true; | ||
| 964 | expect(sumArray(seq[0..]) == 10); | ||
| 965 | } | ||
| 966 | |||
| 967 | // we should see every entry at the head at least once | ||
| 968 | for (seen) |e| { | ||
| 969 | expect(e == true); | ||
| 970 | } | ||
| 971 | } | ||
| 972 | |||
| 973 | fn sumArray(s: []const u8) u32 { | ||
| 974 | var r: u32 = 0; | ||
| 975 | for (s) |e| | ||
| 976 | r += e; | ||
| 977 | return r; | ||
| 978 | } | ||
| 979 | |||
| 980 | test "Random range" { | ||
| 981 | var prng = DefaultPrng.init(0); | ||
| 982 | testRange(&prng.random, -4, 3); | ||
| 983 | testRange(&prng.random, -4, -1); | ||
| 984 | testRange(&prng.random, 10, 14); | ||
| 985 | testRange(&prng.random, -0x80, 0x7f); | ||
| 986 | } | ||
| 987 | |||
| 988 | fn testRange(r: *Random, start: i8, end: i8) void { | ||
| 989 | testRangeBias(r, start, end, true); | ||
| 990 | testRangeBias(r, start, end, false); | ||
| 991 | } | ||
| 992 | fn testRangeBias(r: *Random, start: i8, end: i8, biased: bool) void { | ||
| 993 | const count = @intCast(usize, i32(end) - i32(start)); | ||
| 994 | var values_buffer = []bool{false} ** 0x100; | ||
| 995 | const values = values_buffer[0..count]; | ||
| 996 | var i: usize = 0; | ||
| 997 | while (i < count) { | ||
| 998 | const value: i32 = if (biased) r.intRangeLessThanBiased(i8, start, end) else r.intRangeLessThan(i8, start, end); | ||
| 999 | const index = @intCast(usize, value - start); | ||
| 1000 | if (!values[index]) { | ||
| 1001 | i += 1; | ||
| 1002 | values[index] = true; | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | } | ||
std/rand/ziggurat.zig+1-1| ... | @@ -8,7 +8,7 @@ | ... | @@ -8,7 +8,7 @@ |
| 8 | // NOTE: This seems interesting but reference code is a bit hard to grok: | 8 | // NOTE: This seems interesting but reference code is a bit hard to grok: |
| 9 | // https://sbarral.github.io/etf. | 9 | // https://sbarral.github.io/etf. |
| 10 | 10 | ||
| 11 | const std = @import("../index.zig"); | 11 | const std = @import("../std.zig"); |
| 12 | const math = std.math; | 12 | const math = std.math; |
| 13 | const Random = std.rand.Random; | 13 | const Random = std.rand.Random; |
| 14 | 14 |
std/rb.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const mem = std.mem; // For mem.Compare | 4 | const mem = std.mem; // For mem.Compare |
std/segmented_list.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
std/sort.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/special/compiler_rt.zig created+1050| ... | @@ -0,0 +1,1050 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const is_test = builtin.is_test; | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; | ||
| 6 | const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong; | ||
| 7 | |||
| 8 | @export("__letf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); | ||
| 9 | @export("__getf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage); | ||
| 10 | |||
| 11 | if (!is_test) { | ||
| 12 | // only create these aliases when not testing | ||
| 13 | @export("__cmptf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); | ||
| 14 | @export("__eqtf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); | ||
| 15 | @export("__lttf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); | ||
| 16 | @export("__netf2", @import("compiler_rt/comparetf2.zig").__letf2, linkage); | ||
| 17 | @export("__gttf2", @import("compiler_rt/comparetf2.zig").__getf2, linkage); | ||
| 18 | @export("__gnu_h2f_ieee", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); | ||
| 19 | @export("__gnu_f2h_ieee", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); | ||
| 20 | } | ||
| 21 | |||
| 22 | @export("__unordtf2", @import("compiler_rt/comparetf2.zig").__unordtf2, linkage); | ||
| 23 | |||
| 24 | @export("__addtf3", @import("compiler_rt/addXf3.zig").__addtf3, linkage); | ||
| 25 | @export("__subtf3", @import("compiler_rt/addXf3.zig").__subtf3, linkage); | ||
| 26 | |||
| 27 | @export("__floattitf", @import("compiler_rt/floattitf.zig").__floattitf, linkage); | ||
| 28 | @export("__floattidf", @import("compiler_rt/floattidf.zig").__floattidf, linkage); | ||
| 29 | @export("__floattisf", @import("compiler_rt/floattisf.zig").__floattisf, linkage); | ||
| 30 | |||
| 31 | @export("__floatunditf", @import("compiler_rt/floatunditf.zig").__floatunditf, linkage); | ||
| 32 | @export("__floatunsitf", @import("compiler_rt/floatunsitf.zig").__floatunsitf, linkage); | ||
| 33 | |||
| 34 | @export("__floatuntitf", @import("compiler_rt/floatuntitf.zig").__floatuntitf, linkage); | ||
| 35 | @export("__floatuntidf", @import("compiler_rt/floatuntidf.zig").__floatuntidf, linkage); | ||
| 36 | @export("__floatuntisf", @import("compiler_rt/floatuntisf.zig").__floatuntisf, linkage); | ||
| 37 | |||
| 38 | @export("__extenddftf2", @import("compiler_rt/extendXfYf2.zig").__extenddftf2, linkage); | ||
| 39 | @export("__extendsftf2", @import("compiler_rt/extendXfYf2.zig").__extendsftf2, linkage); | ||
| 40 | @export("__extendhfsf2", @import("compiler_rt/extendXfYf2.zig").__extendhfsf2, linkage); | ||
| 41 | |||
| 42 | @export("__truncsfhf2", @import("compiler_rt/truncXfYf2.zig").__truncsfhf2, linkage); | ||
| 43 | @export("__truncdfhf2", @import("compiler_rt/truncXfYf2.zig").__truncdfhf2, linkage); | ||
| 44 | @export("__trunctfdf2", @import("compiler_rt/truncXfYf2.zig").__trunctfdf2, linkage); | ||
| 45 | @export("__trunctfsf2", @import("compiler_rt/truncXfYf2.zig").__trunctfsf2, linkage); | ||
| 46 | |||
| 47 | @export("__fixunssfsi", @import("compiler_rt/fixunssfsi.zig").__fixunssfsi, linkage); | ||
| 48 | @export("__fixunssfdi", @import("compiler_rt/fixunssfdi.zig").__fixunssfdi, linkage); | ||
| 49 | @export("__fixunssfti", @import("compiler_rt/fixunssfti.zig").__fixunssfti, linkage); | ||
| 50 | |||
| 51 | @export("__fixunsdfsi", @import("compiler_rt/fixunsdfsi.zig").__fixunsdfsi, linkage); | ||
| 52 | @export("__fixunsdfdi", @import("compiler_rt/fixunsdfdi.zig").__fixunsdfdi, linkage); | ||
| 53 | @export("__fixunsdfti", @import("compiler_rt/fixunsdfti.zig").__fixunsdfti, linkage); | ||
| 54 | |||
| 55 | @export("__fixunstfsi", @import("compiler_rt/fixunstfsi.zig").__fixunstfsi, linkage); | ||
| 56 | @export("__fixunstfdi", @import("compiler_rt/fixunstfdi.zig").__fixunstfdi, linkage); | ||
| 57 | @export("__fixunstfti", @import("compiler_rt/fixunstfti.zig").__fixunstfti, linkage); | ||
| 58 | |||
| 59 | @export("__fixdfdi", @import("compiler_rt/fixdfdi.zig").__fixdfdi, linkage); | ||
| 60 | @export("__fixdfsi", @import("compiler_rt/fixdfsi.zig").__fixdfsi, linkage); | ||
| 61 | @export("__fixdfti", @import("compiler_rt/fixdfti.zig").__fixdfti, linkage); | ||
| 62 | @export("__fixsfdi", @import("compiler_rt/fixsfdi.zig").__fixsfdi, linkage); | ||
| 63 | @export("__fixsfsi", @import("compiler_rt/fixsfsi.zig").__fixsfsi, linkage); | ||
| 64 | @export("__fixsfti", @import("compiler_rt/fixsfti.zig").__fixsfti, linkage); | ||
| 65 | @export("__fixtfdi", @import("compiler_rt/fixtfdi.zig").__fixtfdi, linkage); | ||
| 66 | @export("__fixtfsi", @import("compiler_rt/fixtfsi.zig").__fixtfsi, linkage); | ||
| 67 | @export("__fixtfti", @import("compiler_rt/fixtfti.zig").__fixtfti, linkage); | ||
| 68 | |||
| 69 | @export("__udivmoddi4", @import("compiler_rt/udivmoddi4.zig").__udivmoddi4, linkage); | ||
| 70 | |||
| 71 | @export("__udivsi3", __udivsi3, linkage); | ||
| 72 | @export("__udivdi3", __udivdi3, linkage); | ||
| 73 | @export("__umoddi3", __umoddi3, linkage); | ||
| 74 | @export("__udivmodsi4", __udivmodsi4, linkage); | ||
| 75 | |||
| 76 | if (is_arm_arch and !is_arm_64) { | ||
| 77 | @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); | ||
| 78 | @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); | ||
| 79 | @export("__aeabi_uidiv", __udivsi3, linkage); | ||
| 80 | } | ||
| 81 | if (builtin.os == builtin.Os.windows) { | ||
| 82 | switch (builtin.arch) { | ||
| 83 | builtin.Arch.i386 => { | ||
| 84 | if (!builtin.link_libc) { | ||
| 85 | @export("_chkstk", _chkstk, strong_linkage); | ||
| 86 | @export("__chkstk_ms", __chkstk_ms, linkage); | ||
| 87 | } | ||
| 88 | @export("_aulldiv", @import("compiler_rt/aulldiv.zig")._aulldiv, strong_linkage); | ||
| 89 | @export("_aullrem", @import("compiler_rt/aullrem.zig")._aullrem, strong_linkage); | ||
| 90 | }, | ||
| 91 | builtin.Arch.x86_64 => { | ||
| 92 | if (!builtin.link_libc) { | ||
| 93 | @export("__chkstk", __chkstk, strong_linkage); | ||
| 94 | @export("___chkstk_ms", ___chkstk_ms, linkage); | ||
| 95 | } | ||
| 96 | @export("__divti3", @import("compiler_rt/divti3.zig").__divti3_windows_x86_64, linkage); | ||
| 97 | @export("__multi3", @import("compiler_rt/multi3.zig").__multi3_windows_x86_64, linkage); | ||
| 98 | @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4_windows_x86_64, linkage); | ||
| 99 | @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3_windows_x86_64, linkage); | ||
| 100 | @export("__udivmodti4", @import("compiler_rt/udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); | ||
| 101 | @export("__umodti3", @import("compiler_rt/umodti3.zig").__umodti3_windows_x86_64, linkage); | ||
| 102 | }, | ||
| 103 | else => {}, | ||
| 104 | } | ||
| 105 | } else { | ||
| 106 | @export("__divti3", @import("compiler_rt/divti3.zig").__divti3, linkage); | ||
| 107 | @export("__multi3", @import("compiler_rt/multi3.zig").__multi3, linkage); | ||
| 108 | @export("__muloti4", @import("compiler_rt/muloti4.zig").__muloti4, linkage); | ||
| 109 | @export("__udivti3", @import("compiler_rt/udivti3.zig").__udivti3, linkage); | ||
| 110 | @export("__udivmodti4", @import("compiler_rt/udivmodti4.zig").__udivmodti4, linkage); | ||
| 111 | @export("__umodti3", @import("compiler_rt/umodti3.zig").__umodti3, linkage); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | const std = @import("std"); | ||
| 116 | const assert = std.debug.assert; | ||
| 117 | const testing = std.testing; | ||
| 118 | |||
| 119 | const __udivmoddi4 = @import("compiler_rt/udivmoddi4.zig").__udivmoddi4; | ||
| 120 | |||
| 121 | // Avoid dragging in the runtime safety mechanisms into this .o file, | ||
| 122 | // unless we're trying to test this file. | ||
| 123 | pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | ||
| 124 | @setCold(true); | ||
| 125 | if (is_test) { | ||
| 126 | std.debug.panic("{}", msg); | ||
| 127 | } else { | ||
| 128 | unreachable; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | pub fn setXmm0(comptime T: type, value: T) void { | ||
| 133 | comptime assert(builtin.arch == builtin.Arch.x86_64); | ||
| 134 | const aligned_value: T align(16) = value; | ||
| 135 | asm volatile ( | ||
| 136 | \\movaps (%[ptr]), %%xmm0 | ||
| 137 | : | ||
| 138 | : [ptr] "r" (&aligned_value) | ||
| 139 | : "xmm0" | ||
| 140 | ); | ||
| 141 | } | ||
| 142 | |||
| 143 | extern fn __udivdi3(a: u64, b: u64) u64 { | ||
| 144 | @setRuntimeSafety(is_test); | ||
| 145 | return __udivmoddi4(a, b, null); | ||
| 146 | } | ||
| 147 | |||
| 148 | extern fn __umoddi3(a: u64, b: u64) u64 { | ||
| 149 | @setRuntimeSafety(is_test); | ||
| 150 | |||
| 151 | var r: u64 = undefined; | ||
| 152 | _ = __udivmoddi4(a, b, &r); | ||
| 153 | return r; | ||
| 154 | } | ||
| 155 | |||
| 156 | const AeabiUlDivModResult = extern struct { | ||
| 157 | quot: u64, | ||
| 158 | rem: u64, | ||
| 159 | }; | ||
| 160 | extern fn __aeabi_uldivmod(numerator: u64, denominator: u64) AeabiUlDivModResult { | ||
| 161 | @setRuntimeSafety(is_test); | ||
| 162 | var result: AeabiUlDivModResult = undefined; | ||
| 163 | result.quot = __udivmoddi4(numerator, denominator, &result.rem); | ||
| 164 | return result; | ||
| 165 | } | ||
| 166 | |||
| 167 | const is_arm_64 = switch (builtin.arch) { | ||
| 168 | builtin.Arch.aarch64v8_3a, | ||
| 169 | builtin.Arch.aarch64v8_2a, | ||
| 170 | builtin.Arch.aarch64v8_1a, | ||
| 171 | builtin.Arch.aarch64v8, | ||
| 172 | builtin.Arch.aarch64v8r, | ||
| 173 | builtin.Arch.aarch64v8m_baseline, | ||
| 174 | builtin.Arch.aarch64v8m_mainline, | ||
| 175 | builtin.Arch.aarch64_bev8_3a, | ||
| 176 | builtin.Arch.aarch64_bev8_2a, | ||
| 177 | builtin.Arch.aarch64_bev8_1a, | ||
| 178 | builtin.Arch.aarch64_bev8, | ||
| 179 | builtin.Arch.aarch64_bev8r, | ||
| 180 | builtin.Arch.aarch64_bev8m_baseline, | ||
| 181 | builtin.Arch.aarch64_bev8m_mainline, | ||
| 182 | => true, | ||
| 183 | else => false, | ||
| 184 | }; | ||
| 185 | |||
| 186 | const is_arm_arch = switch (builtin.arch) { | ||
| 187 | builtin.Arch.arm, | ||
| 188 | builtin.Arch.armeb, | ||
| 189 | builtin.Arch.aarch64, | ||
| 190 | builtin.Arch.aarch64_be, | ||
| 191 | builtin.Arch.thumb, | ||
| 192 | builtin.Arch.thumbeb, | ||
| 193 | => true, | ||
| 194 | else => false, | ||
| 195 | }; | ||
| 196 | |||
| 197 | nakedcc fn __aeabi_uidivmod() void { | ||
| 198 | @setRuntimeSafety(false); | ||
| 199 | asm volatile ( | ||
| 200 | \\ push { lr } | ||
| 201 | \\ sub sp, sp, #4 | ||
| 202 | \\ mov r2, sp | ||
| 203 | \\ bl __udivmodsi4 | ||
| 204 | \\ ldr r1, [sp] | ||
| 205 | \\ add sp, sp, #4 | ||
| 206 | \\ pop { pc } | ||
| 207 | : | ||
| 208 | : | ||
| 209 | : "r2", "r1" | ||
| 210 | ); | ||
| 211 | } | ||
| 212 | |||
| 213 | // _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, | ||
| 214 | // then decrement %esp by %eax. Preserves all registers except %esp and flags. | ||
| 215 | // This routine is windows specific | ||
| 216 | // http://msdn.microsoft.com/en-us/library/ms648426.aspx | ||
| 217 | nakedcc fn _chkstk() align(4) void { | ||
| 218 | @setRuntimeSafety(false); | ||
| 219 | |||
| 220 | asm volatile ( | ||
| 221 | \\ push %%ecx | ||
| 222 | \\ push %%eax | ||
| 223 | \\ cmp $0x1000,%%eax | ||
| 224 | \\ lea 12(%%esp),%%ecx | ||
| 225 | \\ jb 1f | ||
| 226 | \\ 2: | ||
| 227 | \\ sub $0x1000,%%ecx | ||
| 228 | \\ test %%ecx,(%%ecx) | ||
| 229 | \\ sub $0x1000,%%eax | ||
| 230 | \\ cmp $0x1000,%%eax | ||
| 231 | \\ ja 2b | ||
| 232 | \\ 1: | ||
| 233 | \\ sub %%eax,%%ecx | ||
| 234 | \\ test %%ecx,(%%ecx) | ||
| 235 | \\ pop %%eax | ||
| 236 | \\ pop %%ecx | ||
| 237 | \\ ret | ||
| 238 | ); | ||
| 239 | } | ||
| 240 | |||
| 241 | nakedcc fn __chkstk() align(4) void { | ||
| 242 | @setRuntimeSafety(false); | ||
| 243 | |||
| 244 | asm volatile ( | ||
| 245 | \\ push %%rcx | ||
| 246 | \\ push %%rax | ||
| 247 | \\ cmp $0x1000,%%rax | ||
| 248 | \\ lea 24(%%rsp),%%rcx | ||
| 249 | \\ jb 1f | ||
| 250 | \\2: | ||
| 251 | \\ sub $0x1000,%%rcx | ||
| 252 | \\ test %%rcx,(%%rcx) | ||
| 253 | \\ sub $0x1000,%%rax | ||
| 254 | \\ cmp $0x1000,%%rax | ||
| 255 | \\ ja 2b | ||
| 256 | \\1: | ||
| 257 | \\ sub %%rax,%%rcx | ||
| 258 | \\ test %%rcx,(%%rcx) | ||
| 259 | \\ pop %%rax | ||
| 260 | \\ pop %%rcx | ||
| 261 | \\ ret | ||
| 262 | ); | ||
| 263 | } | ||
| 264 | |||
| 265 | // _chkstk routine | ||
| 266 | // This routine is windows specific | ||
| 267 | // http://msdn.microsoft.com/en-us/library/ms648426.aspx | ||
| 268 | nakedcc fn __chkstk_ms() align(4) void { | ||
| 269 | @setRuntimeSafety(false); | ||
| 270 | |||
| 271 | asm volatile ( | ||
| 272 | \\ push %%ecx | ||
| 273 | \\ push %%eax | ||
| 274 | \\ cmp $0x1000,%%eax | ||
| 275 | \\ lea 12(%%esp),%%ecx | ||
| 276 | \\ jb 1f | ||
| 277 | \\ 2: | ||
| 278 | \\ sub $0x1000,%%ecx | ||
| 279 | \\ test %%ecx,(%%ecx) | ||
| 280 | \\ sub $0x1000,%%eax | ||
| 281 | \\ cmp $0x1000,%%eax | ||
| 282 | \\ ja 2b | ||
| 283 | \\ 1: | ||
| 284 | \\ sub %%eax,%%ecx | ||
| 285 | \\ test %%ecx,(%%ecx) | ||
| 286 | \\ pop %%eax | ||
| 287 | \\ pop %%ecx | ||
| 288 | \\ ret | ||
| 289 | ); | ||
| 290 | } | ||
| 291 | |||
| 292 | nakedcc fn ___chkstk_ms() align(4) void { | ||
| 293 | @setRuntimeSafety(false); | ||
| 294 | |||
| 295 | asm volatile ( | ||
| 296 | \\ push %%rcx | ||
| 297 | \\ push %%rax | ||
| 298 | \\ cmp $0x1000,%%rax | ||
| 299 | \\ lea 24(%%rsp),%%rcx | ||
| 300 | \\ jb 1f | ||
| 301 | \\2: | ||
| 302 | \\ sub $0x1000,%%rcx | ||
| 303 | \\ test %%rcx,(%%rcx) | ||
| 304 | \\ sub $0x1000,%%rax | ||
| 305 | \\ cmp $0x1000,%%rax | ||
| 306 | \\ ja 2b | ||
| 307 | \\1: | ||
| 308 | \\ sub %%rax,%%rcx | ||
| 309 | \\ test %%rcx,(%%rcx) | ||
| 310 | \\ pop %%rax | ||
| 311 | \\ pop %%rcx | ||
| 312 | \\ ret | ||
| 313 | ); | ||
| 314 | } | ||
| 315 | |||
| 316 | extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { | ||
| 317 | @setRuntimeSafety(is_test); | ||
| 318 | |||
| 319 | const d = __udivsi3(a, b); | ||
| 320 | rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); | ||
| 321 | return d; | ||
| 322 | } | ||
| 323 | |||
| 324 | extern fn __udivsi3(n: u32, d: u32) u32 { | ||
| 325 | @setRuntimeSafety(is_test); | ||
| 326 | |||
| 327 | const n_uword_bits: c_uint = u32.bit_count; | ||
| 328 | // special cases | ||
| 329 | if (d == 0) return 0; // ?! | ||
| 330 | if (n == 0) return 0; | ||
| 331 | var sr = @bitCast(c_uint, c_int(@clz(d)) - c_int(@clz(n))); | ||
| 332 | // 0 <= sr <= n_uword_bits - 1 or sr large | ||
| 333 | if (sr > n_uword_bits - 1) { | ||
| 334 | // d > r | ||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | if (sr == n_uword_bits - 1) { | ||
| 338 | // d == 1 | ||
| 339 | return n; | ||
| 340 | } | ||
| 341 | sr += 1; | ||
| 342 | // 1 <= sr <= n_uword_bits - 1 | ||
| 343 | // Not a special case | ||
| 344 | var q: u32 = n << @intCast(u5, n_uword_bits - sr); | ||
| 345 | var r: u32 = n >> @intCast(u5, sr); | ||
| 346 | var carry: u32 = 0; | ||
| 347 | while (sr > 0) : (sr -= 1) { | ||
| 348 | // r:q = ((r:q) << 1) | carry | ||
| 349 | r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); | ||
| 350 | q = (q << 1) | carry; | ||
| 351 | // carry = 0; | ||
| 352 | // if (r.all >= d.all) | ||
| 353 | // { | ||
| 354 | // r.all -= d.all; | ||
| 355 | // carry = 1; | ||
| 356 | // } | ||
| 357 | const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); | ||
| 358 | carry = @intCast(u32, s & 1); | ||
| 359 | r -= d & @bitCast(u32, s); | ||
| 360 | } | ||
| 361 | q = (q << 1) | carry; | ||
| 362 | return q; | ||
| 363 | } | ||
| 364 | |||
| 365 | test "test_umoddi3" { | ||
| 366 | test_one_umoddi3(0, 1, 0); | ||
| 367 | test_one_umoddi3(2, 1, 0); | ||
| 368 | test_one_umoddi3(0x8000000000000000, 1, 0x0); | ||
| 369 | test_one_umoddi3(0x8000000000000000, 2, 0x0); | ||
| 370 | test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); | ||
| 371 | } | ||
| 372 | |||
| 373 | fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { | ||
| 374 | const r = __umoddi3(a, b); | ||
| 375 | testing.expect(r == expected_r); | ||
| 376 | } | ||
| 377 | |||
| 378 | test "test_udivsi3" { | ||
| 379 | const cases = [][3]u32{ | ||
| 380 | []u32{ | ||
| 381 | 0x00000000, | ||
| 382 | 0x00000001, | ||
| 383 | 0x00000000, | ||
| 384 | }, | ||
| 385 | []u32{ | ||
| 386 | 0x00000000, | ||
| 387 | 0x00000002, | ||
| 388 | 0x00000000, | ||
| 389 | }, | ||
| 390 | []u32{ | ||
| 391 | 0x00000000, | ||
| 392 | 0x00000003, | ||
| 393 | 0x00000000, | ||
| 394 | }, | ||
| 395 | []u32{ | ||
| 396 | 0x00000000, | ||
| 397 | 0x00000010, | ||
| 398 | 0x00000000, | ||
| 399 | }, | ||
| 400 | []u32{ | ||
| 401 | 0x00000000, | ||
| 402 | 0x078644FA, | ||
| 403 | 0x00000000, | ||
| 404 | }, | ||
| 405 | []u32{ | ||
| 406 | 0x00000000, | ||
| 407 | 0x0747AE14, | ||
| 408 | 0x00000000, | ||
| 409 | }, | ||
| 410 | []u32{ | ||
| 411 | 0x00000000, | ||
| 412 | 0x7FFFFFFF, | ||
| 413 | 0x00000000, | ||
| 414 | }, | ||
| 415 | []u32{ | ||
| 416 | 0x00000000, | ||
| 417 | 0x80000000, | ||
| 418 | 0x00000000, | ||
| 419 | }, | ||
| 420 | []u32{ | ||
| 421 | 0x00000000, | ||
| 422 | 0xFFFFFFFD, | ||
| 423 | 0x00000000, | ||
| 424 | }, | ||
| 425 | []u32{ | ||
| 426 | 0x00000000, | ||
| 427 | 0xFFFFFFFE, | ||
| 428 | 0x00000000, | ||
| 429 | }, | ||
| 430 | []u32{ | ||
| 431 | 0x00000000, | ||
| 432 | 0xFFFFFFFF, | ||
| 433 | 0x00000000, | ||
| 434 | }, | ||
| 435 | []u32{ | ||
| 436 | 0x00000001, | ||
| 437 | 0x00000001, | ||
| 438 | 0x00000001, | ||
| 439 | }, | ||
| 440 | []u32{ | ||
| 441 | 0x00000001, | ||
| 442 | 0x00000002, | ||
| 443 | 0x00000000, | ||
| 444 | }, | ||
| 445 | []u32{ | ||
| 446 | 0x00000001, | ||
| 447 | 0x00000003, | ||
| 448 | 0x00000000, | ||
| 449 | }, | ||
| 450 | []u32{ | ||
| 451 | 0x00000001, | ||
| 452 | 0x00000010, | ||
| 453 | 0x00000000, | ||
| 454 | }, | ||
| 455 | []u32{ | ||
| 456 | 0x00000001, | ||
| 457 | 0x078644FA, | ||
| 458 | 0x00000000, | ||
| 459 | }, | ||
| 460 | []u32{ | ||
| 461 | 0x00000001, | ||
| 462 | 0x0747AE14, | ||
| 463 | 0x00000000, | ||
| 464 | }, | ||
| 465 | []u32{ | ||
| 466 | 0x00000001, | ||
| 467 | 0x7FFFFFFF, | ||
| 468 | 0x00000000, | ||
| 469 | }, | ||
| 470 | []u32{ | ||
| 471 | 0x00000001, | ||
| 472 | 0x80000000, | ||
| 473 | 0x00000000, | ||
| 474 | }, | ||
| 475 | []u32{ | ||
| 476 | 0x00000001, | ||
| 477 | 0xFFFFFFFD, | ||
| 478 | 0x00000000, | ||
| 479 | }, | ||
| 480 | []u32{ | ||
| 481 | 0x00000001, | ||
| 482 | 0xFFFFFFFE, | ||
| 483 | 0x00000000, | ||
| 484 | }, | ||
| 485 | []u32{ | ||
| 486 | 0x00000001, | ||
| 487 | 0xFFFFFFFF, | ||
| 488 | 0x00000000, | ||
| 489 | }, | ||
| 490 | []u32{ | ||
| 491 | 0x00000002, | ||
| 492 | 0x00000001, | ||
| 493 | 0x00000002, | ||
| 494 | }, | ||
| 495 | []u32{ | ||
| 496 | 0x00000002, | ||
| 497 | 0x00000002, | ||
| 498 | 0x00000001, | ||
| 499 | }, | ||
| 500 | []u32{ | ||
| 501 | 0x00000002, | ||
| 502 | 0x00000003, | ||
| 503 | 0x00000000, | ||
| 504 | }, | ||
| 505 | []u32{ | ||
| 506 | 0x00000002, | ||
| 507 | 0x00000010, | ||
| 508 | 0x00000000, | ||
| 509 | }, | ||
| 510 | []u32{ | ||
| 511 | 0x00000002, | ||
| 512 | 0x078644FA, | ||
| 513 | 0x00000000, | ||
| 514 | }, | ||
| 515 | []u32{ | ||
| 516 | 0x00000002, | ||
| 517 | 0x0747AE14, | ||
| 518 | 0x00000000, | ||
| 519 | }, | ||
| 520 | []u32{ | ||
| 521 | 0x00000002, | ||
| 522 | 0x7FFFFFFF, | ||
| 523 | 0x00000000, | ||
| 524 | }, | ||
| 525 | []u32{ | ||
| 526 | 0x00000002, | ||
| 527 | 0x80000000, | ||
| 528 | 0x00000000, | ||
| 529 | }, | ||
| 530 | []u32{ | ||
| 531 | 0x00000002, | ||
| 532 | 0xFFFFFFFD, | ||
| 533 | 0x00000000, | ||
| 534 | }, | ||
| 535 | []u32{ | ||
| 536 | 0x00000002, | ||
| 537 | 0xFFFFFFFE, | ||
| 538 | 0x00000000, | ||
| 539 | }, | ||
| 540 | []u32{ | ||
| 541 | 0x00000002, | ||
| 542 | 0xFFFFFFFF, | ||
| 543 | 0x00000000, | ||
| 544 | }, | ||
| 545 | []u32{ | ||
| 546 | 0x00000003, | ||
| 547 | 0x00000001, | ||
| 548 | 0x00000003, | ||
| 549 | }, | ||
| 550 | []u32{ | ||
| 551 | 0x00000003, | ||
| 552 | 0x00000002, | ||
| 553 | 0x00000001, | ||
| 554 | }, | ||
| 555 | []u32{ | ||
| 556 | 0x00000003, | ||
| 557 | 0x00000003, | ||
| 558 | 0x00000001, | ||
| 559 | }, | ||
| 560 | []u32{ | ||
| 561 | 0x00000003, | ||
| 562 | 0x00000010, | ||
| 563 | 0x00000000, | ||
| 564 | }, | ||
| 565 | []u32{ | ||
| 566 | 0x00000003, | ||
| 567 | 0x078644FA, | ||
| 568 | 0x00000000, | ||
| 569 | }, | ||
| 570 | []u32{ | ||
| 571 | 0x00000003, | ||
| 572 | 0x0747AE14, | ||
| 573 | 0x00000000, | ||
| 574 | }, | ||
| 575 | []u32{ | ||
| 576 | 0x00000003, | ||
| 577 | 0x7FFFFFFF, | ||
| 578 | 0x00000000, | ||
| 579 | }, | ||
| 580 | []u32{ | ||
| 581 | 0x00000003, | ||
| 582 | 0x80000000, | ||
| 583 | 0x00000000, | ||
| 584 | }, | ||
| 585 | []u32{ | ||
| 586 | 0x00000003, | ||
| 587 | 0xFFFFFFFD, | ||
| 588 | 0x00000000, | ||
| 589 | }, | ||
| 590 | []u32{ | ||
| 591 | 0x00000003, | ||
| 592 | 0xFFFFFFFE, | ||
| 593 | 0x00000000, | ||
| 594 | }, | ||
| 595 | []u32{ | ||
| 596 | 0x00000003, | ||
| 597 | 0xFFFFFFFF, | ||
| 598 | 0x00000000, | ||
| 599 | }, | ||
| 600 | []u32{ | ||
| 601 | 0x00000010, | ||
| 602 | 0x00000001, | ||
| 603 | 0x00000010, | ||
| 604 | }, | ||
| 605 | []u32{ | ||
| 606 | 0x00000010, | ||
| 607 | 0x00000002, | ||
| 608 | 0x00000008, | ||
| 609 | }, | ||
| 610 | []u32{ | ||
| 611 | 0x00000010, | ||
| 612 | 0x00000003, | ||
| 613 | 0x00000005, | ||
| 614 | }, | ||
| 615 | []u32{ | ||
| 616 | 0x00000010, | ||
| 617 | 0x00000010, | ||
| 618 | 0x00000001, | ||
| 619 | }, | ||
| 620 | []u32{ | ||
| 621 | 0x00000010, | ||
| 622 | 0x078644FA, | ||
| 623 | 0x00000000, | ||
| 624 | }, | ||
| 625 | []u32{ | ||
| 626 | 0x00000010, | ||
| 627 | 0x0747AE14, | ||
| 628 | 0x00000000, | ||
| 629 | }, | ||
| 630 | []u32{ | ||
| 631 | 0x00000010, | ||
| 632 | 0x7FFFFFFF, | ||
| 633 | 0x00000000, | ||
| 634 | }, | ||
| 635 | []u32{ | ||
| 636 | 0x00000010, | ||
| 637 | 0x80000000, | ||
| 638 | 0x00000000, | ||
| 639 | }, | ||
| 640 | []u32{ | ||
| 641 | 0x00000010, | ||
| 642 | 0xFFFFFFFD, | ||
| 643 | 0x00000000, | ||
| 644 | }, | ||
| 645 | []u32{ | ||
| 646 | 0x00000010, | ||
| 647 | 0xFFFFFFFE, | ||
| 648 | 0x00000000, | ||
| 649 | }, | ||
| 650 | []u32{ | ||
| 651 | 0x00000010, | ||
| 652 | 0xFFFFFFFF, | ||
| 653 | 0x00000000, | ||
| 654 | }, | ||
| 655 | []u32{ | ||
| 656 | 0x078644FA, | ||
| 657 | 0x00000001, | ||
| 658 | 0x078644FA, | ||
| 659 | }, | ||
| 660 | []u32{ | ||
| 661 | 0x078644FA, | ||
| 662 | 0x00000002, | ||
| 663 | 0x03C3227D, | ||
| 664 | }, | ||
| 665 | []u32{ | ||
| 666 | 0x078644FA, | ||
| 667 | 0x00000003, | ||
| 668 | 0x028216FE, | ||
| 669 | }, | ||
| 670 | []u32{ | ||
| 671 | 0x078644FA, | ||
| 672 | 0x00000010, | ||
| 673 | 0x0078644F, | ||
| 674 | }, | ||
| 675 | []u32{ | ||
| 676 | 0x078644FA, | ||
| 677 | 0x078644FA, | ||
| 678 | 0x00000001, | ||
| 679 | }, | ||
| 680 | []u32{ | ||
| 681 | 0x078644FA, | ||
| 682 | 0x0747AE14, | ||
| 683 | 0x00000001, | ||
| 684 | }, | ||
| 685 | []u32{ | ||
| 686 | 0x078644FA, | ||
| 687 | 0x7FFFFFFF, | ||
| 688 | 0x00000000, | ||
| 689 | }, | ||
| 690 | []u32{ | ||
| 691 | 0x078644FA, | ||
| 692 | 0x80000000, | ||
| 693 | 0x00000000, | ||
| 694 | }, | ||
| 695 | []u32{ | ||
| 696 | 0x078644FA, | ||
| 697 | 0xFFFFFFFD, | ||
| 698 | 0x00000000, | ||
| 699 | }, | ||
| 700 | []u32{ | ||
| 701 | 0x078644FA, | ||
| 702 | 0xFFFFFFFE, | ||
| 703 | 0x00000000, | ||
| 704 | }, | ||
| 705 | []u32{ | ||
| 706 | 0x078644FA, | ||
| 707 | 0xFFFFFFFF, | ||
| 708 | 0x00000000, | ||
| 709 | }, | ||
| 710 | []u32{ | ||
| 711 | 0x0747AE14, | ||
| 712 | 0x00000001, | ||
| 713 | 0x0747AE14, | ||
| 714 | }, | ||
| 715 | []u32{ | ||
| 716 | 0x0747AE14, | ||
| 717 | 0x00000002, | ||
| 718 | 0x03A3D70A, | ||
| 719 | }, | ||
| 720 | []u32{ | ||
| 721 | 0x0747AE14, | ||
| 722 | 0x00000003, | ||
| 723 | 0x026D3A06, | ||
| 724 | }, | ||
| 725 | []u32{ | ||
| 726 | 0x0747AE14, | ||
| 727 | 0x00000010, | ||
| 728 | 0x00747AE1, | ||
| 729 | }, | ||
| 730 | []u32{ | ||
| 731 | 0x0747AE14, | ||
| 732 | 0x078644FA, | ||
| 733 | 0x00000000, | ||
| 734 | }, | ||
| 735 | []u32{ | ||
| 736 | 0x0747AE14, | ||
| 737 | 0x0747AE14, | ||
| 738 | 0x00000001, | ||
| 739 | }, | ||
| 740 | []u32{ | ||
| 741 | 0x0747AE14, | ||
| 742 | 0x7FFFFFFF, | ||
| 743 | 0x00000000, | ||
| 744 | }, | ||
| 745 | []u32{ | ||
| 746 | 0x0747AE14, | ||
| 747 | 0x80000000, | ||
| 748 | 0x00000000, | ||
| 749 | }, | ||
| 750 | []u32{ | ||
| 751 | 0x0747AE14, | ||
| 752 | 0xFFFFFFFD, | ||
| 753 | 0x00000000, | ||
| 754 | }, | ||
| 755 | []u32{ | ||
| 756 | 0x0747AE14, | ||
| 757 | 0xFFFFFFFE, | ||
| 758 | 0x00000000, | ||
| 759 | }, | ||
| 760 | []u32{ | ||
| 761 | 0x0747AE14, | ||
| 762 | 0xFFFFFFFF, | ||
| 763 | 0x00000000, | ||
| 764 | }, | ||
| 765 | []u32{ | ||
| 766 | 0x7FFFFFFF, | ||
| 767 | 0x00000001, | ||
| 768 | 0x7FFFFFFF, | ||
| 769 | }, | ||
| 770 | []u32{ | ||
| 771 | 0x7FFFFFFF, | ||
| 772 | 0x00000002, | ||
| 773 | 0x3FFFFFFF, | ||
| 774 | }, | ||
| 775 | []u32{ | ||
| 776 | 0x7FFFFFFF, | ||
| 777 | 0x00000003, | ||
| 778 | 0x2AAAAAAA, | ||
| 779 | }, | ||
| 780 | []u32{ | ||
| 781 | 0x7FFFFFFF, | ||
| 782 | 0x00000010, | ||
| 783 | 0x07FFFFFF, | ||
| 784 | }, | ||
| 785 | []u32{ | ||
| 786 | 0x7FFFFFFF, | ||
| 787 | 0x078644FA, | ||
| 788 | 0x00000011, | ||
| 789 | }, | ||
| 790 | []u32{ | ||
| 791 | 0x7FFFFFFF, | ||
| 792 | 0x0747AE14, | ||
| 793 | 0x00000011, | ||
| 794 | }, | ||
| 795 | []u32{ | ||
| 796 | 0x7FFFFFFF, | ||
| 797 | 0x7FFFFFFF, | ||
| 798 | 0x00000001, | ||
| 799 | }, | ||
| 800 | []u32{ | ||
| 801 | 0x7FFFFFFF, | ||
| 802 | 0x80000000, | ||
| 803 | 0x00000000, | ||
| 804 | }, | ||
| 805 | []u32{ | ||
| 806 | 0x7FFFFFFF, | ||
| 807 | 0xFFFFFFFD, | ||
| 808 | 0x00000000, | ||
| 809 | }, | ||
| 810 | []u32{ | ||
| 811 | 0x7FFFFFFF, | ||
| 812 | 0xFFFFFFFE, | ||
| 813 | 0x00000000, | ||
| 814 | }, | ||
| 815 | []u32{ | ||
| 816 | 0x7FFFFFFF, | ||
| 817 | 0xFFFFFFFF, | ||
| 818 | 0x00000000, | ||
| 819 | }, | ||
| 820 | []u32{ | ||
| 821 | 0x80000000, | ||
| 822 | 0x00000001, | ||
| 823 | 0x80000000, | ||
| 824 | }, | ||
| 825 | []u32{ | ||
| 826 | 0x80000000, | ||
| 827 | 0x00000002, | ||
| 828 | 0x40000000, | ||
| 829 | }, | ||
| 830 | []u32{ | ||
| 831 | 0x80000000, | ||
| 832 | 0x00000003, | ||
| 833 | 0x2AAAAAAA, | ||
| 834 | }, | ||
| 835 | []u32{ | ||
| 836 | 0x80000000, | ||
| 837 | 0x00000010, | ||
| 838 | 0x08000000, | ||
| 839 | }, | ||
| 840 | []u32{ | ||
| 841 | 0x80000000, | ||
| 842 | 0x078644FA, | ||
| 843 | 0x00000011, | ||
| 844 | }, | ||
| 845 | []u32{ | ||
| 846 | 0x80000000, | ||
| 847 | 0x0747AE14, | ||
| 848 | 0x00000011, | ||
| 849 | }, | ||
| 850 | []u32{ | ||
| 851 | 0x80000000, | ||
| 852 | 0x7FFFFFFF, | ||
| 853 | 0x00000001, | ||
| 854 | }, | ||
| 855 | []u32{ | ||
| 856 | 0x80000000, | ||
| 857 | 0x80000000, | ||
| 858 | 0x00000001, | ||
| 859 | }, | ||
| 860 | []u32{ | ||
| 861 | 0x80000000, | ||
| 862 | 0xFFFFFFFD, | ||
| 863 | 0x00000000, | ||
| 864 | }, | ||
| 865 | []u32{ | ||
| 866 | 0x80000000, | ||
| 867 | 0xFFFFFFFE, | ||
| 868 | 0x00000000, | ||
| 869 | }, | ||
| 870 | []u32{ | ||
| 871 | 0x80000000, | ||
| 872 | 0xFFFFFFFF, | ||
| 873 | 0x00000000, | ||
| 874 | }, | ||
| 875 | []u32{ | ||
| 876 | 0xFFFFFFFD, | ||
| 877 | 0x00000001, | ||
| 878 | 0xFFFFFFFD, | ||
| 879 | }, | ||
| 880 | []u32{ | ||
| 881 | 0xFFFFFFFD, | ||
| 882 | 0x00000002, | ||
| 883 | 0x7FFFFFFE, | ||
| 884 | }, | ||
| 885 | []u32{ | ||
| 886 | 0xFFFFFFFD, | ||
| 887 | 0x00000003, | ||
| 888 | 0x55555554, | ||
| 889 | }, | ||
| 890 | []u32{ | ||
| 891 | 0xFFFFFFFD, | ||
| 892 | 0x00000010, | ||
| 893 | 0x0FFFFFFF, | ||
| 894 | }, | ||
| 895 | []u32{ | ||
| 896 | 0xFFFFFFFD, | ||
| 897 | 0x078644FA, | ||
| 898 | 0x00000022, | ||
| 899 | }, | ||
| 900 | []u32{ | ||
| 901 | 0xFFFFFFFD, | ||
| 902 | 0x0747AE14, | ||
| 903 | 0x00000023, | ||
| 904 | }, | ||
| 905 | []u32{ | ||
| 906 | 0xFFFFFFFD, | ||
| 907 | 0x7FFFFFFF, | ||
| 908 | 0x00000001, | ||
| 909 | }, | ||
| 910 | []u32{ | ||
| 911 | 0xFFFFFFFD, | ||
| 912 | 0x80000000, | ||
| 913 | 0x00000001, | ||
| 914 | }, | ||
| 915 | []u32{ | ||
| 916 | 0xFFFFFFFD, | ||
| 917 | 0xFFFFFFFD, | ||
| 918 | 0x00000001, | ||
| 919 | }, | ||
| 920 | []u32{ | ||
| 921 | 0xFFFFFFFD, | ||
| 922 | 0xFFFFFFFE, | ||
| 923 | 0x00000000, | ||
| 924 | }, | ||
| 925 | []u32{ | ||
| 926 | 0xFFFFFFFD, | ||
| 927 | 0xFFFFFFFF, | ||
| 928 | 0x00000000, | ||
| 929 | }, | ||
| 930 | []u32{ | ||
| 931 | 0xFFFFFFFE, | ||
| 932 | 0x00000001, | ||
| 933 | 0xFFFFFFFE, | ||
| 934 | }, | ||
| 935 | []u32{ | ||
| 936 | 0xFFFFFFFE, | ||
| 937 | 0x00000002, | ||
| 938 | 0x7FFFFFFF, | ||
| 939 | }, | ||
| 940 | []u32{ | ||
| 941 | 0xFFFFFFFE, | ||
| 942 | 0x00000003, | ||
| 943 | 0x55555554, | ||
| 944 | }, | ||
| 945 | []u32{ | ||
| 946 | 0xFFFFFFFE, | ||
| 947 | 0x00000010, | ||
| 948 | 0x0FFFFFFF, | ||
| 949 | }, | ||
| 950 | []u32{ | ||
| 951 | 0xFFFFFFFE, | ||
| 952 | 0x078644FA, | ||
| 953 | 0x00000022, | ||
| 954 | }, | ||
| 955 | []u32{ | ||
| 956 | 0xFFFFFFFE, | ||
| 957 | 0x0747AE14, | ||
| 958 | 0x00000023, | ||
| 959 | }, | ||
| 960 | []u32{ | ||
| 961 | 0xFFFFFFFE, | ||
| 962 | 0x7FFFFFFF, | ||
| 963 | 0x00000002, | ||
| 964 | }, | ||
| 965 | []u32{ | ||
| 966 | 0xFFFFFFFE, | ||
| 967 | 0x80000000, | ||
| 968 | 0x00000001, | ||
| 969 | }, | ||
| 970 | []u32{ | ||
| 971 | 0xFFFFFFFE, | ||
| 972 | 0xFFFFFFFD, | ||
| 973 | 0x00000001, | ||
| 974 | }, | ||
| 975 | []u32{ | ||
| 976 | 0xFFFFFFFE, | ||
| 977 | 0xFFFFFFFE, | ||
| 978 | 0x00000001, | ||
| 979 | }, | ||
| 980 | []u32{ | ||
| 981 | 0xFFFFFFFE, | ||
| 982 | 0xFFFFFFFF, | ||
| 983 | 0x00000000, | ||
| 984 | }, | ||
| 985 | []u32{ | ||
| 986 | 0xFFFFFFFF, | ||
| 987 | 0x00000001, | ||
| 988 | 0xFFFFFFFF, | ||
| 989 | }, | ||
| 990 | []u32{ | ||
| 991 | 0xFFFFFFFF, | ||
| 992 | 0x00000002, | ||
| 993 | 0x7FFFFFFF, | ||
| 994 | }, | ||
| 995 | []u32{ | ||
| 996 | 0xFFFFFFFF, | ||
| 997 | 0x00000003, | ||
| 998 | 0x55555555, | ||
| 999 | }, | ||
| 1000 | []u32{ | ||
| 1001 | 0xFFFFFFFF, | ||
| 1002 | 0x00000010, | ||
| 1003 | 0x0FFFFFFF, | ||
| 1004 | }, | ||
| 1005 | []u32{ | ||
| 1006 | 0xFFFFFFFF, | ||
| 1007 | 0x078644FA, | ||
| 1008 | 0x00000022, | ||
| 1009 | }, | ||
| 1010 | []u32{ | ||
| 1011 | 0xFFFFFFFF, | ||
| 1012 | 0x0747AE14, | ||
| 1013 | 0x00000023, | ||
| 1014 | }, | ||
| 1015 | []u32{ | ||
| 1016 | 0xFFFFFFFF, | ||
| 1017 | 0x7FFFFFFF, | ||
| 1018 | 0x00000002, | ||
| 1019 | }, | ||
| 1020 | []u32{ | ||
| 1021 | 0xFFFFFFFF, | ||
| 1022 | 0x80000000, | ||
| 1023 | 0x00000001, | ||
| 1024 | }, | ||
| 1025 | []u32{ | ||
| 1026 | 0xFFFFFFFF, | ||
| 1027 | 0xFFFFFFFD, | ||
| 1028 | 0x00000001, | ||
| 1029 | }, | ||
| 1030 | []u32{ | ||
| 1031 | 0xFFFFFFFF, | ||
| 1032 | 0xFFFFFFFE, | ||
| 1033 | 0x00000001, | ||
| 1034 | }, | ||
| 1035 | []u32{ | ||
| 1036 | 0xFFFFFFFF, | ||
| 1037 | 0xFFFFFFFF, | ||
| 1038 | 0x00000001, | ||
| 1039 | }, | ||
| 1040 | }; | ||
| 1041 | |||
| 1042 | for (cases) |case| { | ||
| 1043 | test_one_udivsi3(case[0], case[1], case[2]); | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { | ||
| 1048 | const q: u32 = __udivsi3(a, b); | ||
| 1049 | testing.expect(q == expected_q); | ||
| 1050 | } | ||
std/special/compiler_rt/addXf3.zig+1-1| ... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | ||
| 5 | const std = @import("std"); | 5 | const std = @import("std"); |
| 6 | const builtin = @import("builtin"); | 6 | const builtin = @import("builtin"); |
| 7 | const compiler_rt = @import("index.zig"); | 7 | const compiler_rt = @import("../compiler_rt.zig"); |
| 8 | 8 | ||
| 9 | pub extern fn __addtf3(a: f128, b: f128) f128 { | 9 | pub extern fn __addtf3(a: f128, b: f128) f128 { |
| 10 | return addXf3(f128, a, b); | 10 | return addXf3(f128, a, b); |
std/special/compiler_rt/divti3.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const udivmod = @import("udivmod.zig").udivmod; | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | 3 | const compiler_rt = @import("../compiler_rt.zig"); |
| 4 | 4 | ||
| 5 | pub extern fn __divti3(a: i128, b: i128) i128 { | 5 | pub extern fn __divti3(a: i128, b: i128) i128 { |
| 6 | @setRuntimeSafety(builtin.is_test); | 6 | @setRuntimeSafety(builtin.is_test); |
std/special/compiler_rt/index.zig deleted-1050| ... | @@ -1,1050 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const is_test = builtin.is_test; | ||
| 3 | |||
| 4 | comptime { | ||
| 5 | const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak; | ||
| 6 | const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong; | ||
| 7 | |||
| 8 | @export("__letf2", @import("comparetf2.zig").__letf2, linkage); | ||
| 9 | @export("__getf2", @import("comparetf2.zig").__getf2, linkage); | ||
| 10 | |||
| 11 | if (!is_test) { | ||
| 12 | // only create these aliases when not testing | ||
| 13 | @export("__cmptf2", @import("comparetf2.zig").__letf2, linkage); | ||
| 14 | @export("__eqtf2", @import("comparetf2.zig").__letf2, linkage); | ||
| 15 | @export("__lttf2", @import("comparetf2.zig").__letf2, linkage); | ||
| 16 | @export("__netf2", @import("comparetf2.zig").__letf2, linkage); | ||
| 17 | @export("__gttf2", @import("comparetf2.zig").__getf2, linkage); | ||
| 18 | @export("__gnu_h2f_ieee", @import("extendXfYf2.zig").__extendhfsf2, linkage); | ||
| 19 | @export("__gnu_f2h_ieee", @import("truncXfYf2.zig").__truncsfhf2, linkage); | ||
| 20 | } | ||
| 21 | |||
| 22 | @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage); | ||
| 23 | |||
| 24 | @export("__addtf3", @import("addXf3.zig").__addtf3, linkage); | ||
| 25 | @export("__subtf3", @import("addXf3.zig").__subtf3, linkage); | ||
| 26 | |||
| 27 | @export("__floattitf", @import("floattitf.zig").__floattitf, linkage); | ||
| 28 | @export("__floattidf", @import("floattidf.zig").__floattidf, linkage); | ||
| 29 | @export("__floattisf", @import("floattisf.zig").__floattisf, linkage); | ||
| 30 | |||
| 31 | @export("__floatunditf", @import("floatunditf.zig").__floatunditf, linkage); | ||
| 32 | @export("__floatunsitf", @import("floatunsitf.zig").__floatunsitf, linkage); | ||
| 33 | |||
| 34 | @export("__floatuntitf", @import("floatuntitf.zig").__floatuntitf, linkage); | ||
| 35 | @export("__floatuntidf", @import("floatuntidf.zig").__floatuntidf, linkage); | ||
| 36 | @export("__floatuntisf", @import("floatuntisf.zig").__floatuntisf, linkage); | ||
| 37 | |||
| 38 | @export("__extenddftf2", @import("extendXfYf2.zig").__extenddftf2, linkage); | ||
| 39 | @export("__extendsftf2", @import("extendXfYf2.zig").__extendsftf2, linkage); | ||
| 40 | @export("__extendhfsf2", @import("extendXfYf2.zig").__extendhfsf2, linkage); | ||
| 41 | |||
| 42 | @export("__truncsfhf2", @import("truncXfYf2.zig").__truncsfhf2, linkage); | ||
| 43 | @export("__truncdfhf2", @import("truncXfYf2.zig").__truncdfhf2, linkage); | ||
| 44 | @export("__trunctfdf2", @import("truncXfYf2.zig").__trunctfdf2, linkage); | ||
| 45 | @export("__trunctfsf2", @import("truncXfYf2.zig").__trunctfsf2, linkage); | ||
| 46 | |||
| 47 | @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage); | ||
| 48 | @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage); | ||
| 49 | @export("__fixunssfti", @import("fixunssfti.zig").__fixunssfti, linkage); | ||
| 50 | |||
| 51 | @export("__fixunsdfsi", @import("fixunsdfsi.zig").__fixunsdfsi, linkage); | ||
| 52 | @export("__fixunsdfdi", @import("fixunsdfdi.zig").__fixunsdfdi, linkage); | ||
| 53 | @export("__fixunsdfti", @import("fixunsdfti.zig").__fixunsdfti, linkage); | ||
| 54 | |||
| 55 | @export("__fixunstfsi", @import("fixunstfsi.zig").__fixunstfsi, linkage); | ||
| 56 | @export("__fixunstfdi", @import("fixunstfdi.zig").__fixunstfdi, linkage); | ||
| 57 | @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage); | ||
| 58 | |||
| 59 | @export("__fixdfdi", @import("fixdfdi.zig").__fixdfdi, linkage); | ||
| 60 | @export("__fixdfsi", @import("fixdfsi.zig").__fixdfsi, linkage); | ||
| 61 | @export("__fixdfti", @import("fixdfti.zig").__fixdfti, linkage); | ||
| 62 | @export("__fixsfdi", @import("fixsfdi.zig").__fixsfdi, linkage); | ||
| 63 | @export("__fixsfsi", @import("fixsfsi.zig").__fixsfsi, linkage); | ||
| 64 | @export("__fixsfti", @import("fixsfti.zig").__fixsfti, linkage); | ||
| 65 | @export("__fixtfdi", @import("fixtfdi.zig").__fixtfdi, linkage); | ||
| 66 | @export("__fixtfsi", @import("fixtfsi.zig").__fixtfsi, linkage); | ||
| 67 | @export("__fixtfti", @import("fixtfti.zig").__fixtfti, linkage); | ||
| 68 | |||
| 69 | @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage); | ||
| 70 | |||
| 71 | @export("__udivsi3", __udivsi3, linkage); | ||
| 72 | @export("__udivdi3", __udivdi3, linkage); | ||
| 73 | @export("__umoddi3", __umoddi3, linkage); | ||
| 74 | @export("__udivmodsi4", __udivmodsi4, linkage); | ||
| 75 | |||
| 76 | if (is_arm_arch and !is_arm_64) { | ||
| 77 | @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage); | ||
| 78 | @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage); | ||
| 79 | @export("__aeabi_uidiv", __udivsi3, linkage); | ||
| 80 | } | ||
| 81 | if (builtin.os == builtin.Os.windows) { | ||
| 82 | switch (builtin.arch) { | ||
| 83 | builtin.Arch.i386 => { | ||
| 84 | if (!builtin.link_libc) { | ||
| 85 | @export("_chkstk", _chkstk, strong_linkage); | ||
| 86 | @export("__chkstk_ms", __chkstk_ms, linkage); | ||
| 87 | } | ||
| 88 | @export("_aulldiv", @import("aulldiv.zig")._aulldiv, strong_linkage); | ||
| 89 | @export("_aullrem", @import("aullrem.zig")._aullrem, strong_linkage); | ||
| 90 | }, | ||
| 91 | builtin.Arch.x86_64 => { | ||
| 92 | if (!builtin.link_libc) { | ||
| 93 | @export("__chkstk", __chkstk, strong_linkage); | ||
| 94 | @export("___chkstk_ms", ___chkstk_ms, linkage); | ||
| 95 | } | ||
| 96 | @export("__divti3", @import("divti3.zig").__divti3_windows_x86_64, linkage); | ||
| 97 | @export("__multi3", @import("multi3.zig").__multi3_windows_x86_64, linkage); | ||
| 98 | @export("__muloti4", @import("muloti4.zig").__muloti4_windows_x86_64, linkage); | ||
| 99 | @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage); | ||
| 100 | @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); | ||
| 101 | @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage); | ||
| 102 | }, | ||
| 103 | else => {}, | ||
| 104 | } | ||
| 105 | } else { | ||
| 106 | @export("__divti3", @import("divti3.zig").__divti3, linkage); | ||
| 107 | @export("__multi3", @import("multi3.zig").__multi3, linkage); | ||
| 108 | @export("__muloti4", @import("muloti4.zig").__muloti4, linkage); | ||
| 109 | @export("__udivti3", @import("udivti3.zig").__udivti3, linkage); | ||
| 110 | @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); | ||
| 111 | @export("__umodti3", @import("umodti3.zig").__umodti3, linkage); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | const std = @import("std"); | ||
| 116 | const assert = std.debug.assert; | ||
| 117 | const testing = std.testing; | ||
| 118 | |||
| 119 | const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4; | ||
| 120 | |||
| 121 | // Avoid dragging in the runtime safety mechanisms into this .o file, | ||
| 122 | // unless we're trying to test this file. | ||
| 123 | pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | ||
| 124 | @setCold(true); | ||
| 125 | if (is_test) { | ||
| 126 | std.debug.panic("{}", msg); | ||
| 127 | } else { | ||
| 128 | unreachable; | ||
| 129 | } | ||
| 130 | } | ||
| 131 | |||
| 132 | pub fn setXmm0(comptime T: type, value: T) void { | ||
| 133 | comptime assert(builtin.arch == builtin.Arch.x86_64); | ||
| 134 | const aligned_value: T align(16) = value; | ||
| 135 | asm volatile ( | ||
| 136 | \\movaps (%[ptr]), %%xmm0 | ||
| 137 | : | ||
| 138 | : [ptr] "r" (&aligned_value) | ||
| 139 | : "xmm0" | ||
| 140 | ); | ||
| 141 | } | ||
| 142 | |||
| 143 | extern fn __udivdi3(a: u64, b: u64) u64 { | ||
| 144 | @setRuntimeSafety(is_test); | ||
| 145 | return __udivmoddi4(a, b, null); | ||
| 146 | } | ||
| 147 | |||
| 148 | extern fn __umoddi3(a: u64, b: u64) u64 { | ||
| 149 | @setRuntimeSafety(is_test); | ||
| 150 | |||
| 151 | var r: u64 = undefined; | ||
| 152 | _ = __udivmoddi4(a, b, &r); | ||
| 153 | return r; | ||
| 154 | } | ||
| 155 | |||
| 156 | const AeabiUlDivModResult = extern struct { | ||
| 157 | quot: u64, | ||
| 158 | rem: u64, | ||
| 159 | }; | ||
| 160 | extern fn __aeabi_uldivmod(numerator: u64, denominator: u64) AeabiUlDivModResult { | ||
| 161 | @setRuntimeSafety(is_test); | ||
| 162 | var result: AeabiUlDivModResult = undefined; | ||
| 163 | result.quot = __udivmoddi4(numerator, denominator, &result.rem); | ||
| 164 | return result; | ||
| 165 | } | ||
| 166 | |||
| 167 | const is_arm_64 = switch (builtin.arch) { | ||
| 168 | builtin.Arch.aarch64v8_3a, | ||
| 169 | builtin.Arch.aarch64v8_2a, | ||
| 170 | builtin.Arch.aarch64v8_1a, | ||
| 171 | builtin.Arch.aarch64v8, | ||
| 172 | builtin.Arch.aarch64v8r, | ||
| 173 | builtin.Arch.aarch64v8m_baseline, | ||
| 174 | builtin.Arch.aarch64v8m_mainline, | ||
| 175 | builtin.Arch.aarch64_bev8_3a, | ||
| 176 | builtin.Arch.aarch64_bev8_2a, | ||
| 177 | builtin.Arch.aarch64_bev8_1a, | ||
| 178 | builtin.Arch.aarch64_bev8, | ||
| 179 | builtin.Arch.aarch64_bev8r, | ||
| 180 | builtin.Arch.aarch64_bev8m_baseline, | ||
| 181 | builtin.Arch.aarch64_bev8m_mainline, | ||
| 182 | => true, | ||
| 183 | else => false, | ||
| 184 | }; | ||
| 185 | |||
| 186 | const is_arm_arch = switch (builtin.arch) { | ||
| 187 | builtin.Arch.arm, | ||
| 188 | builtin.Arch.armeb, | ||
| 189 | builtin.Arch.aarch64, | ||
| 190 | builtin.Arch.aarch64_be, | ||
| 191 | builtin.Arch.thumb, | ||
| 192 | builtin.Arch.thumbeb, | ||
| 193 | => true, | ||
| 194 | else => false, | ||
| 195 | }; | ||
| 196 | |||
| 197 | nakedcc fn __aeabi_uidivmod() void { | ||
| 198 | @setRuntimeSafety(false); | ||
| 199 | asm volatile ( | ||
| 200 | \\ push { lr } | ||
| 201 | \\ sub sp, sp, #4 | ||
| 202 | \\ mov r2, sp | ||
| 203 | \\ bl __udivmodsi4 | ||
| 204 | \\ ldr r1, [sp] | ||
| 205 | \\ add sp, sp, #4 | ||
| 206 | \\ pop { pc } | ||
| 207 | : | ||
| 208 | : | ||
| 209 | : "r2", "r1" | ||
| 210 | ); | ||
| 211 | } | ||
| 212 | |||
| 213 | // _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, | ||
| 214 | // then decrement %esp by %eax. Preserves all registers except %esp and flags. | ||
| 215 | // This routine is windows specific | ||
| 216 | // http://msdn.microsoft.com/en-us/library/ms648426.aspx | ||
| 217 | nakedcc fn _chkstk() align(4) void { | ||
| 218 | @setRuntimeSafety(false); | ||
| 219 | |||
| 220 | asm volatile ( | ||
| 221 | \\ push %%ecx | ||
| 222 | \\ push %%eax | ||
| 223 | \\ cmp $0x1000,%%eax | ||
| 224 | \\ lea 12(%%esp),%%ecx | ||
| 225 | \\ jb 1f | ||
| 226 | \\ 2: | ||
| 227 | \\ sub $0x1000,%%ecx | ||
| 228 | \\ test %%ecx,(%%ecx) | ||
| 229 | \\ sub $0x1000,%%eax | ||
| 230 | \\ cmp $0x1000,%%eax | ||
| 231 | \\ ja 2b | ||
| 232 | \\ 1: | ||
| 233 | \\ sub %%eax,%%ecx | ||
| 234 | \\ test %%ecx,(%%ecx) | ||
| 235 | \\ pop %%eax | ||
| 236 | \\ pop %%ecx | ||
| 237 | \\ ret | ||
| 238 | ); | ||
| 239 | } | ||
| 240 | |||
| 241 | nakedcc fn __chkstk() align(4) void { | ||
| 242 | @setRuntimeSafety(false); | ||
| 243 | |||
| 244 | asm volatile ( | ||
| 245 | \\ push %%rcx | ||
| 246 | \\ push %%rax | ||
| 247 | \\ cmp $0x1000,%%rax | ||
| 248 | \\ lea 24(%%rsp),%%rcx | ||
| 249 | \\ jb 1f | ||
| 250 | \\2: | ||
| 251 | \\ sub $0x1000,%%rcx | ||
| 252 | \\ test %%rcx,(%%rcx) | ||
| 253 | \\ sub $0x1000,%%rax | ||
| 254 | \\ cmp $0x1000,%%rax | ||
| 255 | \\ ja 2b | ||
| 256 | \\1: | ||
| 257 | \\ sub %%rax,%%rcx | ||
| 258 | \\ test %%rcx,(%%rcx) | ||
| 259 | \\ pop %%rax | ||
| 260 | \\ pop %%rcx | ||
| 261 | \\ ret | ||
| 262 | ); | ||
| 263 | } | ||
| 264 | |||
| 265 | // _chkstk routine | ||
| 266 | // This routine is windows specific | ||
| 267 | // http://msdn.microsoft.com/en-us/library/ms648426.aspx | ||
| 268 | nakedcc fn __chkstk_ms() align(4) void { | ||
| 269 | @setRuntimeSafety(false); | ||
| 270 | |||
| 271 | asm volatile ( | ||
| 272 | \\ push %%ecx | ||
| 273 | \\ push %%eax | ||
| 274 | \\ cmp $0x1000,%%eax | ||
| 275 | \\ lea 12(%%esp),%%ecx | ||
| 276 | \\ jb 1f | ||
| 277 | \\ 2: | ||
| 278 | \\ sub $0x1000,%%ecx | ||
| 279 | \\ test %%ecx,(%%ecx) | ||
| 280 | \\ sub $0x1000,%%eax | ||
| 281 | \\ cmp $0x1000,%%eax | ||
| 282 | \\ ja 2b | ||
| 283 | \\ 1: | ||
| 284 | \\ sub %%eax,%%ecx | ||
| 285 | \\ test %%ecx,(%%ecx) | ||
| 286 | \\ pop %%eax | ||
| 287 | \\ pop %%ecx | ||
| 288 | \\ ret | ||
| 289 | ); | ||
| 290 | } | ||
| 291 | |||
| 292 | nakedcc fn ___chkstk_ms() align(4) void { | ||
| 293 | @setRuntimeSafety(false); | ||
| 294 | |||
| 295 | asm volatile ( | ||
| 296 | \\ push %%rcx | ||
| 297 | \\ push %%rax | ||
| 298 | \\ cmp $0x1000,%%rax | ||
| 299 | \\ lea 24(%%rsp),%%rcx | ||
| 300 | \\ jb 1f | ||
| 301 | \\2: | ||
| 302 | \\ sub $0x1000,%%rcx | ||
| 303 | \\ test %%rcx,(%%rcx) | ||
| 304 | \\ sub $0x1000,%%rax | ||
| 305 | \\ cmp $0x1000,%%rax | ||
| 306 | \\ ja 2b | ||
| 307 | \\1: | ||
| 308 | \\ sub %%rax,%%rcx | ||
| 309 | \\ test %%rcx,(%%rcx) | ||
| 310 | \\ pop %%rax | ||
| 311 | \\ pop %%rcx | ||
| 312 | \\ ret | ||
| 313 | ); | ||
| 314 | } | ||
| 315 | |||
| 316 | extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { | ||
| 317 | @setRuntimeSafety(is_test); | ||
| 318 | |||
| 319 | const d = __udivsi3(a, b); | ||
| 320 | rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); | ||
| 321 | return d; | ||
| 322 | } | ||
| 323 | |||
| 324 | extern fn __udivsi3(n: u32, d: u32) u32 { | ||
| 325 | @setRuntimeSafety(is_test); | ||
| 326 | |||
| 327 | const n_uword_bits: c_uint = u32.bit_count; | ||
| 328 | // special cases | ||
| 329 | if (d == 0) return 0; // ?! | ||
| 330 | if (n == 0) return 0; | ||
| 331 | var sr = @bitCast(c_uint, c_int(@clz(d)) - c_int(@clz(n))); | ||
| 332 | // 0 <= sr <= n_uword_bits - 1 or sr large | ||
| 333 | if (sr > n_uword_bits - 1) { | ||
| 334 | // d > r | ||
| 335 | return 0; | ||
| 336 | } | ||
| 337 | if (sr == n_uword_bits - 1) { | ||
| 338 | // d == 1 | ||
| 339 | return n; | ||
| 340 | } | ||
| 341 | sr += 1; | ||
| 342 | // 1 <= sr <= n_uword_bits - 1 | ||
| 343 | // Not a special case | ||
| 344 | var q: u32 = n << @intCast(u5, n_uword_bits - sr); | ||
| 345 | var r: u32 = n >> @intCast(u5, sr); | ||
| 346 | var carry: u32 = 0; | ||
| 347 | while (sr > 0) : (sr -= 1) { | ||
| 348 | // r:q = ((r:q) << 1) | carry | ||
| 349 | r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); | ||
| 350 | q = (q << 1) | carry; | ||
| 351 | // carry = 0; | ||
| 352 | // if (r.all >= d.all) | ||
| 353 | // { | ||
| 354 | // r.all -= d.all; | ||
| 355 | // carry = 1; | ||
| 356 | // } | ||
| 357 | const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); | ||
| 358 | carry = @intCast(u32, s & 1); | ||
| 359 | r -= d & @bitCast(u32, s); | ||
| 360 | } | ||
| 361 | q = (q << 1) | carry; | ||
| 362 | return q; | ||
| 363 | } | ||
| 364 | |||
| 365 | test "test_umoddi3" { | ||
| 366 | test_one_umoddi3(0, 1, 0); | ||
| 367 | test_one_umoddi3(2, 1, 0); | ||
| 368 | test_one_umoddi3(0x8000000000000000, 1, 0x0); | ||
| 369 | test_one_umoddi3(0x8000000000000000, 2, 0x0); | ||
| 370 | test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); | ||
| 371 | } | ||
| 372 | |||
| 373 | fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { | ||
| 374 | const r = __umoddi3(a, b); | ||
| 375 | testing.expect(r == expected_r); | ||
| 376 | } | ||
| 377 | |||
| 378 | test "test_udivsi3" { | ||
| 379 | const cases = [][3]u32{ | ||
| 380 | []u32{ | ||
| 381 | 0x00000000, | ||
| 382 | 0x00000001, | ||
| 383 | 0x00000000, | ||
| 384 | }, | ||
| 385 | []u32{ | ||
| 386 | 0x00000000, | ||
| 387 | 0x00000002, | ||
| 388 | 0x00000000, | ||
| 389 | }, | ||
| 390 | []u32{ | ||
| 391 | 0x00000000, | ||
| 392 | 0x00000003, | ||
| 393 | 0x00000000, | ||
| 394 | }, | ||
| 395 | []u32{ | ||
| 396 | 0x00000000, | ||
| 397 | 0x00000010, | ||
| 398 | 0x00000000, | ||
| 399 | }, | ||
| 400 | []u32{ | ||
| 401 | 0x00000000, | ||
| 402 | 0x078644FA, | ||
| 403 | 0x00000000, | ||
| 404 | }, | ||
| 405 | []u32{ | ||
| 406 | 0x00000000, | ||
| 407 | 0x0747AE14, | ||
| 408 | 0x00000000, | ||
| 409 | }, | ||
| 410 | []u32{ | ||
| 411 | 0x00000000, | ||
| 412 | 0x7FFFFFFF, | ||
| 413 | 0x00000000, | ||
| 414 | }, | ||
| 415 | []u32{ | ||
| 416 | 0x00000000, | ||
| 417 | 0x80000000, | ||
| 418 | 0x00000000, | ||
| 419 | }, | ||
| 420 | []u32{ | ||
| 421 | 0x00000000, | ||
| 422 | 0xFFFFFFFD, | ||
| 423 | 0x00000000, | ||
| 424 | }, | ||
| 425 | []u32{ | ||
| 426 | 0x00000000, | ||
| 427 | 0xFFFFFFFE, | ||
| 428 | 0x00000000, | ||
| 429 | }, | ||
| 430 | []u32{ | ||
| 431 | 0x00000000, | ||
| 432 | 0xFFFFFFFF, | ||
| 433 | 0x00000000, | ||
| 434 | }, | ||
| 435 | []u32{ | ||
| 436 | 0x00000001, | ||
| 437 | 0x00000001, | ||
| 438 | 0x00000001, | ||
| 439 | }, | ||
| 440 | []u32{ | ||
| 441 | 0x00000001, | ||
| 442 | 0x00000002, | ||
| 443 | 0x00000000, | ||
| 444 | }, | ||
| 445 | []u32{ | ||
| 446 | 0x00000001, | ||
| 447 | 0x00000003, | ||
| 448 | 0x00000000, | ||
| 449 | }, | ||
| 450 | []u32{ | ||
| 451 | 0x00000001, | ||
| 452 | 0x00000010, | ||
| 453 | 0x00000000, | ||
| 454 | }, | ||
| 455 | []u32{ | ||
| 456 | 0x00000001, | ||
| 457 | 0x078644FA, | ||
| 458 | 0x00000000, | ||
| 459 | }, | ||
| 460 | []u32{ | ||
| 461 | 0x00000001, | ||
| 462 | 0x0747AE14, | ||
| 463 | 0x00000000, | ||
| 464 | }, | ||
| 465 | []u32{ | ||
| 466 | 0x00000001, | ||
| 467 | 0x7FFFFFFF, | ||
| 468 | 0x00000000, | ||
| 469 | }, | ||
| 470 | []u32{ | ||
| 471 | 0x00000001, | ||
| 472 | 0x80000000, | ||
| 473 | 0x00000000, | ||
| 474 | }, | ||
| 475 | []u32{ | ||
| 476 | 0x00000001, | ||
| 477 | 0xFFFFFFFD, | ||
| 478 | 0x00000000, | ||
| 479 | }, | ||
| 480 | []u32{ | ||
| 481 | 0x00000001, | ||
| 482 | 0xFFFFFFFE, | ||
| 483 | 0x00000000, | ||
| 484 | }, | ||
| 485 | []u32{ | ||
| 486 | 0x00000001, | ||
| 487 | 0xFFFFFFFF, | ||
| 488 | 0x00000000, | ||
| 489 | }, | ||
| 490 | []u32{ | ||
| 491 | 0x00000002, | ||
| 492 | 0x00000001, | ||
| 493 | 0x00000002, | ||
| 494 | }, | ||
| 495 | []u32{ | ||
| 496 | 0x00000002, | ||
| 497 | 0x00000002, | ||
| 498 | 0x00000001, | ||
| 499 | }, | ||
| 500 | []u32{ | ||
| 501 | 0x00000002, | ||
| 502 | 0x00000003, | ||
| 503 | 0x00000000, | ||
| 504 | }, | ||
| 505 | []u32{ | ||
| 506 | 0x00000002, | ||
| 507 | 0x00000010, | ||
| 508 | 0x00000000, | ||
| 509 | }, | ||
| 510 | []u32{ | ||
| 511 | 0x00000002, | ||
| 512 | 0x078644FA, | ||
| 513 | 0x00000000, | ||
| 514 | }, | ||
| 515 | []u32{ | ||
| 516 | 0x00000002, | ||
| 517 | 0x0747AE14, | ||
| 518 | 0x00000000, | ||
| 519 | }, | ||
| 520 | []u32{ | ||
| 521 | 0x00000002, | ||
| 522 | 0x7FFFFFFF, | ||
| 523 | 0x00000000, | ||
| 524 | }, | ||
| 525 | []u32{ | ||
| 526 | 0x00000002, | ||
| 527 | 0x80000000, | ||
| 528 | 0x00000000, | ||
| 529 | }, | ||
| 530 | []u32{ | ||
| 531 | 0x00000002, | ||
| 532 | 0xFFFFFFFD, | ||
| 533 | 0x00000000, | ||
| 534 | }, | ||
| 535 | []u32{ | ||
| 536 | 0x00000002, | ||
| 537 | 0xFFFFFFFE, | ||
| 538 | 0x00000000, | ||
| 539 | }, | ||
| 540 | []u32{ | ||
| 541 | 0x00000002, | ||
| 542 | 0xFFFFFFFF, | ||
| 543 | 0x00000000, | ||
| 544 | }, | ||
| 545 | []u32{ | ||
| 546 | 0x00000003, | ||
| 547 | 0x00000001, | ||
| 548 | 0x00000003, | ||
| 549 | }, | ||
| 550 | []u32{ | ||
| 551 | 0x00000003, | ||
| 552 | 0x00000002, | ||
| 553 | 0x00000001, | ||
| 554 | }, | ||
| 555 | []u32{ | ||
| 556 | 0x00000003, | ||
| 557 | 0x00000003, | ||
| 558 | 0x00000001, | ||
| 559 | }, | ||
| 560 | []u32{ | ||
| 561 | 0x00000003, | ||
| 562 | 0x00000010, | ||
| 563 | 0x00000000, | ||
| 564 | }, | ||
| 565 | []u32{ | ||
| 566 | 0x00000003, | ||
| 567 | 0x078644FA, | ||
| 568 | 0x00000000, | ||
| 569 | }, | ||
| 570 | []u32{ | ||
| 571 | 0x00000003, | ||
| 572 | 0x0747AE14, | ||
| 573 | 0x00000000, | ||
| 574 | }, | ||
| 575 | []u32{ | ||
| 576 | 0x00000003, | ||
| 577 | 0x7FFFFFFF, | ||
| 578 | 0x00000000, | ||
| 579 | }, | ||
| 580 | []u32{ | ||
| 581 | 0x00000003, | ||
| 582 | 0x80000000, | ||
| 583 | 0x00000000, | ||
| 584 | }, | ||
| 585 | []u32{ | ||
| 586 | 0x00000003, | ||
| 587 | 0xFFFFFFFD, | ||
| 588 | 0x00000000, | ||
| 589 | }, | ||
| 590 | []u32{ | ||
| 591 | 0x00000003, | ||
| 592 | 0xFFFFFFFE, | ||
| 593 | 0x00000000, | ||
| 594 | }, | ||
| 595 | []u32{ | ||
| 596 | 0x00000003, | ||
| 597 | 0xFFFFFFFF, | ||
| 598 | 0x00000000, | ||
| 599 | }, | ||
| 600 | []u32{ | ||
| 601 | 0x00000010, | ||
| 602 | 0x00000001, | ||
| 603 | 0x00000010, | ||
| 604 | }, | ||
| 605 | []u32{ | ||
| 606 | 0x00000010, | ||
| 607 | 0x00000002, | ||
| 608 | 0x00000008, | ||
| 609 | }, | ||
| 610 | []u32{ | ||
| 611 | 0x00000010, | ||
| 612 | 0x00000003, | ||
| 613 | 0x00000005, | ||
| 614 | }, | ||
| 615 | []u32{ | ||
| 616 | 0x00000010, | ||
| 617 | 0x00000010, | ||
| 618 | 0x00000001, | ||
| 619 | }, | ||
| 620 | []u32{ | ||
| 621 | 0x00000010, | ||
| 622 | 0x078644FA, | ||
| 623 | 0x00000000, | ||
| 624 | }, | ||
| 625 | []u32{ | ||
| 626 | 0x00000010, | ||
| 627 | 0x0747AE14, | ||
| 628 | 0x00000000, | ||
| 629 | }, | ||
| 630 | []u32{ | ||
| 631 | 0x00000010, | ||
| 632 | 0x7FFFFFFF, | ||
| 633 | 0x00000000, | ||
| 634 | }, | ||
| 635 | []u32{ | ||
| 636 | 0x00000010, | ||
| 637 | 0x80000000, | ||
| 638 | 0x00000000, | ||
| 639 | }, | ||
| 640 | []u32{ | ||
| 641 | 0x00000010, | ||
| 642 | 0xFFFFFFFD, | ||
| 643 | 0x00000000, | ||
| 644 | }, | ||
| 645 | []u32{ | ||
| 646 | 0x00000010, | ||
| 647 | 0xFFFFFFFE, | ||
| 648 | 0x00000000, | ||
| 649 | }, | ||
| 650 | []u32{ | ||
| 651 | 0x00000010, | ||
| 652 | 0xFFFFFFFF, | ||
| 653 | 0x00000000, | ||
| 654 | }, | ||
| 655 | []u32{ | ||
| 656 | 0x078644FA, | ||
| 657 | 0x00000001, | ||
| 658 | 0x078644FA, | ||
| 659 | }, | ||
| 660 | []u32{ | ||
| 661 | 0x078644FA, | ||
| 662 | 0x00000002, | ||
| 663 | 0x03C3227D, | ||
| 664 | }, | ||
| 665 | []u32{ | ||
| 666 | 0x078644FA, | ||
| 667 | 0x00000003, | ||
| 668 | 0x028216FE, | ||
| 669 | }, | ||
| 670 | []u32{ | ||
| 671 | 0x078644FA, | ||
| 672 | 0x00000010, | ||
| 673 | 0x0078644F, | ||
| 674 | }, | ||
| 675 | []u32{ | ||
| 676 | 0x078644FA, | ||
| 677 | 0x078644FA, | ||
| 678 | 0x00000001, | ||
| 679 | }, | ||
| 680 | []u32{ | ||
| 681 | 0x078644FA, | ||
| 682 | 0x0747AE14, | ||
| 683 | 0x00000001, | ||
| 684 | }, | ||
| 685 | []u32{ | ||
| 686 | 0x078644FA, | ||
| 687 | 0x7FFFFFFF, | ||
| 688 | 0x00000000, | ||
| 689 | }, | ||
| 690 | []u32{ | ||
| 691 | 0x078644FA, | ||
| 692 | 0x80000000, | ||
| 693 | 0x00000000, | ||
| 694 | }, | ||
| 695 | []u32{ | ||
| 696 | 0x078644FA, | ||
| 697 | 0xFFFFFFFD, | ||
| 698 | 0x00000000, | ||
| 699 | }, | ||
| 700 | []u32{ | ||
| 701 | 0x078644FA, | ||
| 702 | 0xFFFFFFFE, | ||
| 703 | 0x00000000, | ||
| 704 | }, | ||
| 705 | []u32{ | ||
| 706 | 0x078644FA, | ||
| 707 | 0xFFFFFFFF, | ||
| 708 | 0x00000000, | ||
| 709 | }, | ||
| 710 | []u32{ | ||
| 711 | 0x0747AE14, | ||
| 712 | 0x00000001, | ||
| 713 | 0x0747AE14, | ||
| 714 | }, | ||
| 715 | []u32{ | ||
| 716 | 0x0747AE14, | ||
| 717 | 0x00000002, | ||
| 718 | 0x03A3D70A, | ||
| 719 | }, | ||
| 720 | []u32{ | ||
| 721 | 0x0747AE14, | ||
| 722 | 0x00000003, | ||
| 723 | 0x026D3A06, | ||
| 724 | }, | ||
| 725 | []u32{ | ||
| 726 | 0x0747AE14, | ||
| 727 | 0x00000010, | ||
| 728 | 0x00747AE1, | ||
| 729 | }, | ||
| 730 | []u32{ | ||
| 731 | 0x0747AE14, | ||
| 732 | 0x078644FA, | ||
| 733 | 0x00000000, | ||
| 734 | }, | ||
| 735 | []u32{ | ||
| 736 | 0x0747AE14, | ||
| 737 | 0x0747AE14, | ||
| 738 | 0x00000001, | ||
| 739 | }, | ||
| 740 | []u32{ | ||
| 741 | 0x0747AE14, | ||
| 742 | 0x7FFFFFFF, | ||
| 743 | 0x00000000, | ||
| 744 | }, | ||
| 745 | []u32{ | ||
| 746 | 0x0747AE14, | ||
| 747 | 0x80000000, | ||
| 748 | 0x00000000, | ||
| 749 | }, | ||
| 750 | []u32{ | ||
| 751 | 0x0747AE14, | ||
| 752 | 0xFFFFFFFD, | ||
| 753 | 0x00000000, | ||
| 754 | }, | ||
| 755 | []u32{ | ||
| 756 | 0x0747AE14, | ||
| 757 | 0xFFFFFFFE, | ||
| 758 | 0x00000000, | ||
| 759 | }, | ||
| 760 | []u32{ | ||
| 761 | 0x0747AE14, | ||
| 762 | 0xFFFFFFFF, | ||
| 763 | 0x00000000, | ||
| 764 | }, | ||
| 765 | []u32{ | ||
| 766 | 0x7FFFFFFF, | ||
| 767 | 0x00000001, | ||
| 768 | 0x7FFFFFFF, | ||
| 769 | }, | ||
| 770 | []u32{ | ||
| 771 | 0x7FFFFFFF, | ||
| 772 | 0x00000002, | ||
| 773 | 0x3FFFFFFF, | ||
| 774 | }, | ||
| 775 | []u32{ | ||
| 776 | 0x7FFFFFFF, | ||
| 777 | 0x00000003, | ||
| 778 | 0x2AAAAAAA, | ||
| 779 | }, | ||
| 780 | []u32{ | ||
| 781 | 0x7FFFFFFF, | ||
| 782 | 0x00000010, | ||
| 783 | 0x07FFFFFF, | ||
| 784 | }, | ||
| 785 | []u32{ | ||
| 786 | 0x7FFFFFFF, | ||
| 787 | 0x078644FA, | ||
| 788 | 0x00000011, | ||
| 789 | }, | ||
| 790 | []u32{ | ||
| 791 | 0x7FFFFFFF, | ||
| 792 | 0x0747AE14, | ||
| 793 | 0x00000011, | ||
| 794 | }, | ||
| 795 | []u32{ | ||
| 796 | 0x7FFFFFFF, | ||
| 797 | 0x7FFFFFFF, | ||
| 798 | 0x00000001, | ||
| 799 | }, | ||
| 800 | []u32{ | ||
| 801 | 0x7FFFFFFF, | ||
| 802 | 0x80000000, | ||
| 803 | 0x00000000, | ||
| 804 | }, | ||
| 805 | []u32{ | ||
| 806 | 0x7FFFFFFF, | ||
| 807 | 0xFFFFFFFD, | ||
| 808 | 0x00000000, | ||
| 809 | }, | ||
| 810 | []u32{ | ||
| 811 | 0x7FFFFFFF, | ||
| 812 | 0xFFFFFFFE, | ||
| 813 | 0x00000000, | ||
| 814 | }, | ||
| 815 | []u32{ | ||
| 816 | 0x7FFFFFFF, | ||
| 817 | 0xFFFFFFFF, | ||
| 818 | 0x00000000, | ||
| 819 | }, | ||
| 820 | []u32{ | ||
| 821 | 0x80000000, | ||
| 822 | 0x00000001, | ||
| 823 | 0x80000000, | ||
| 824 | }, | ||
| 825 | []u32{ | ||
| 826 | 0x80000000, | ||
| 827 | 0x00000002, | ||
| 828 | 0x40000000, | ||
| 829 | }, | ||
| 830 | []u32{ | ||
| 831 | 0x80000000, | ||
| 832 | 0x00000003, | ||
| 833 | 0x2AAAAAAA, | ||
| 834 | }, | ||
| 835 | []u32{ | ||
| 836 | 0x80000000, | ||
| 837 | 0x00000010, | ||
| 838 | 0x08000000, | ||
| 839 | }, | ||
| 840 | []u32{ | ||
| 841 | 0x80000000, | ||
| 842 | 0x078644FA, | ||
| 843 | 0x00000011, | ||
| 844 | }, | ||
| 845 | []u32{ | ||
| 846 | 0x80000000, | ||
| 847 | 0x0747AE14, | ||
| 848 | 0x00000011, | ||
| 849 | }, | ||
| 850 | []u32{ | ||
| 851 | 0x80000000, | ||
| 852 | 0x7FFFFFFF, | ||
| 853 | 0x00000001, | ||
| 854 | }, | ||
| 855 | []u32{ | ||
| 856 | 0x80000000, | ||
| 857 | 0x80000000, | ||
| 858 | 0x00000001, | ||
| 859 | }, | ||
| 860 | []u32{ | ||
| 861 | 0x80000000, | ||
| 862 | 0xFFFFFFFD, | ||
| 863 | 0x00000000, | ||
| 864 | }, | ||
| 865 | []u32{ | ||
| 866 | 0x80000000, | ||
| 867 | 0xFFFFFFFE, | ||
| 868 | 0x00000000, | ||
| 869 | }, | ||
| 870 | []u32{ | ||
| 871 | 0x80000000, | ||
| 872 | 0xFFFFFFFF, | ||
| 873 | 0x00000000, | ||
| 874 | }, | ||
| 875 | []u32{ | ||
| 876 | 0xFFFFFFFD, | ||
| 877 | 0x00000001, | ||
| 878 | 0xFFFFFFFD, | ||
| 879 | }, | ||
| 880 | []u32{ | ||
| 881 | 0xFFFFFFFD, | ||
| 882 | 0x00000002, | ||
| 883 | 0x7FFFFFFE, | ||
| 884 | }, | ||
| 885 | []u32{ | ||
| 886 | 0xFFFFFFFD, | ||
| 887 | 0x00000003, | ||
| 888 | 0x55555554, | ||
| 889 | }, | ||
| 890 | []u32{ | ||
| 891 | 0xFFFFFFFD, | ||
| 892 | 0x00000010, | ||
| 893 | 0x0FFFFFFF, | ||
| 894 | }, | ||
| 895 | []u32{ | ||
| 896 | 0xFFFFFFFD, | ||
| 897 | 0x078644FA, | ||
| 898 | 0x00000022, | ||
| 899 | }, | ||
| 900 | []u32{ | ||
| 901 | 0xFFFFFFFD, | ||
| 902 | 0x0747AE14, | ||
| 903 | 0x00000023, | ||
| 904 | }, | ||
| 905 | []u32{ | ||
| 906 | 0xFFFFFFFD, | ||
| 907 | 0x7FFFFFFF, | ||
| 908 | 0x00000001, | ||
| 909 | }, | ||
| 910 | []u32{ | ||
| 911 | 0xFFFFFFFD, | ||
| 912 | 0x80000000, | ||
| 913 | 0x00000001, | ||
| 914 | }, | ||
| 915 | []u32{ | ||
| 916 | 0xFFFFFFFD, | ||
| 917 | 0xFFFFFFFD, | ||
| 918 | 0x00000001, | ||
| 919 | }, | ||
| 920 | []u32{ | ||
| 921 | 0xFFFFFFFD, | ||
| 922 | 0xFFFFFFFE, | ||
| 923 | 0x00000000, | ||
| 924 | }, | ||
| 925 | []u32{ | ||
| 926 | 0xFFFFFFFD, | ||
| 927 | 0xFFFFFFFF, | ||
| 928 | 0x00000000, | ||
| 929 | }, | ||
| 930 | []u32{ | ||
| 931 | 0xFFFFFFFE, | ||
| 932 | 0x00000001, | ||
| 933 | 0xFFFFFFFE, | ||
| 934 | }, | ||
| 935 | []u32{ | ||
| 936 | 0xFFFFFFFE, | ||
| 937 | 0x00000002, | ||
| 938 | 0x7FFFFFFF, | ||
| 939 | }, | ||
| 940 | []u32{ | ||
| 941 | 0xFFFFFFFE, | ||
| 942 | 0x00000003, | ||
| 943 | 0x55555554, | ||
| 944 | }, | ||
| 945 | []u32{ | ||
| 946 | 0xFFFFFFFE, | ||
| 947 | 0x00000010, | ||
| 948 | 0x0FFFFFFF, | ||
| 949 | }, | ||
| 950 | []u32{ | ||
| 951 | 0xFFFFFFFE, | ||
| 952 | 0x078644FA, | ||
| 953 | 0x00000022, | ||
| 954 | }, | ||
| 955 | []u32{ | ||
| 956 | 0xFFFFFFFE, | ||
| 957 | 0x0747AE14, | ||
| 958 | 0x00000023, | ||
| 959 | }, | ||
| 960 | []u32{ | ||
| 961 | 0xFFFFFFFE, | ||
| 962 | 0x7FFFFFFF, | ||
| 963 | 0x00000002, | ||
| 964 | }, | ||
| 965 | []u32{ | ||
| 966 | 0xFFFFFFFE, | ||
| 967 | 0x80000000, | ||
| 968 | 0x00000001, | ||
| 969 | }, | ||
| 970 | []u32{ | ||
| 971 | 0xFFFFFFFE, | ||
| 972 | 0xFFFFFFFD, | ||
| 973 | 0x00000001, | ||
| 974 | }, | ||
| 975 | []u32{ | ||
| 976 | 0xFFFFFFFE, | ||
| 977 | 0xFFFFFFFE, | ||
| 978 | 0x00000001, | ||
| 979 | }, | ||
| 980 | []u32{ | ||
| 981 | 0xFFFFFFFE, | ||
| 982 | 0xFFFFFFFF, | ||
| 983 | 0x00000000, | ||
| 984 | }, | ||
| 985 | []u32{ | ||
| 986 | 0xFFFFFFFF, | ||
| 987 | 0x00000001, | ||
| 988 | 0xFFFFFFFF, | ||
| 989 | }, | ||
| 990 | []u32{ | ||
| 991 | 0xFFFFFFFF, | ||
| 992 | 0x00000002, | ||
| 993 | 0x7FFFFFFF, | ||
| 994 | }, | ||
| 995 | []u32{ | ||
| 996 | 0xFFFFFFFF, | ||
| 997 | 0x00000003, | ||
| 998 | 0x55555555, | ||
| 999 | }, | ||
| 1000 | []u32{ | ||
| 1001 | 0xFFFFFFFF, | ||
| 1002 | 0x00000010, | ||
| 1003 | 0x0FFFFFFF, | ||
| 1004 | }, | ||
| 1005 | []u32{ | ||
| 1006 | 0xFFFFFFFF, | ||
| 1007 | 0x078644FA, | ||
| 1008 | 0x00000022, | ||
| 1009 | }, | ||
| 1010 | []u32{ | ||
| 1011 | 0xFFFFFFFF, | ||
| 1012 | 0x0747AE14, | ||
| 1013 | 0x00000023, | ||
| 1014 | }, | ||
| 1015 | []u32{ | ||
| 1016 | 0xFFFFFFFF, | ||
| 1017 | 0x7FFFFFFF, | ||
| 1018 | 0x00000002, | ||
| 1019 | }, | ||
| 1020 | []u32{ | ||
| 1021 | 0xFFFFFFFF, | ||
| 1022 | 0x80000000, | ||
| 1023 | 0x00000001, | ||
| 1024 | }, | ||
| 1025 | []u32{ | ||
| 1026 | 0xFFFFFFFF, | ||
| 1027 | 0xFFFFFFFD, | ||
| 1028 | 0x00000001, | ||
| 1029 | }, | ||
| 1030 | []u32{ | ||
| 1031 | 0xFFFFFFFF, | ||
| 1032 | 0xFFFFFFFE, | ||
| 1033 | 0x00000001, | ||
| 1034 | }, | ||
| 1035 | []u32{ | ||
| 1036 | 0xFFFFFFFF, | ||
| 1037 | 0xFFFFFFFF, | ||
| 1038 | 0x00000001, | ||
| 1039 | }, | ||
| 1040 | }; | ||
| 1041 | |||
| 1042 | for (cases) |case| { | ||
| 1043 | test_one_udivsi3(case[0], case[1], case[2]); | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { | ||
| 1048 | const q: u32 = __udivsi3(a, b); | ||
| 1049 | testing.expect(q == expected_q); | ||
| 1050 | } | ||
std/special/compiler_rt/muloti4.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const udivmod = @import("udivmod.zig").udivmod; | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | 3 | const compiler_rt = @import("../compiler_rt.zig"); |
| 4 | 4 | ||
| 5 | pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 { | 5 | pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 { |
| 6 | @setRuntimeSafety(builtin.is_test); | 6 | @setRuntimeSafety(builtin.is_test); |
std/special/compiler_rt/multi3.zig+1-1| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const compiler_rt = @import("index.zig"); | 2 | const compiler_rt = @import("../compiler_rt.zig"); |
| 3 | 3 | ||
| 4 | // Ported from git@github.com:llvm-project/llvm-project-20170507.git | 4 | // Ported from git@github.com:llvm-project/llvm-project-20170507.git |
| 5 | // ae684fad6d34858c014c94da69c15e7774a633c3 | 5 | // ae684fad6d34858c014c94da69c15e7774a633c3 |
std/special/compiler_rt/udivmodti4.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const udivmod = @import("udivmod.zig").udivmod; | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | 3 | const compiler_rt = @import("../compiler_rt.zig"); |
| 4 | 4 | ||
| 5 | pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { | 5 | pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { |
| 6 | @setRuntimeSafety(builtin.is_test); | 6 | @setRuntimeSafety(builtin.is_test); |
std/special/compiler_rt/umodti3.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const udivmodti4 = @import("udivmodti4.zig"); | 1 | const udivmodti4 = @import("udivmodti4.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | 3 | const compiler_rt = @import("../compiler_rt.zig"); |
| 4 | 4 | ||
| 5 | pub extern fn __umodti3(a: u128, b: u128) u128 { | 5 | pub extern fn __umodti3(a: u128, b: u128) u128 { |
| 6 | @setRuntimeSafety(builtin.is_test); | 6 | @setRuntimeSafety(builtin.is_test); |
std/spinlock.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
std/statically_initialized_mutex.zig+5-8| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | 1 | const std = @import("std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const AtomicOrder = builtin.AtomicOrder; | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | const AtomicRmwOp = builtin.AtomicRmwOp; | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| ... | @@ -13,7 +13,7 @@ const windows = std.os.windows; | ... | @@ -13,7 +13,7 @@ const windows = std.os.windows; |
| 13 | /// On Windows, this mutex allocates resources when it is | 13 | /// On Windows, this mutex allocates resources when it is |
| 14 | /// first used, and the resources cannot be freed. | 14 | /// first used, and the resources cannot be freed. |
| 15 | /// On Linux, this is an alias of std.Mutex. | 15 | /// On Linux, this is an alias of std.Mutex. |
| 16 | pub const StaticallyInitializedMutex = switch(builtin.os) { | 16 | pub const StaticallyInitializedMutex = switch (builtin.os) { |
| 17 | builtin.Os.linux => std.Mutex, | 17 | builtin.Os.linux => std.Mutex, |
| 18 | builtin.Os.windows => struct { | 18 | builtin.Os.windows => struct { |
| 19 | lock: windows.CRITICAL_SECTION, | 19 | lock: windows.CRITICAL_SECTION, |
| ... | @@ -28,7 +28,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { | ... | @@ -28,7 +28,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { |
| 28 | }; | 28 | }; |
| 29 | 29 | ||
| 30 | pub fn init() StaticallyInitializedMutex { | 30 | pub fn init() StaticallyInitializedMutex { |
| 31 | return StaticallyInitializedMutex { | 31 | return StaticallyInitializedMutex{ |
| 32 | .lock = undefined, | 32 | .lock = undefined, |
| 33 | .init_once = windows.INIT_ONCE_STATIC_INIT, | 33 | .init_once = windows.INIT_ONCE_STATIC_INIT, |
| 34 | }; | 34 | }; |
| ... | @@ -54,7 +54,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { | ... | @@ -54,7 +54,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { |
| 54 | pub fn acquire(self: *StaticallyInitializedMutex) Held { | 54 | pub fn acquire(self: *StaticallyInitializedMutex) Held { |
| 55 | assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); | 55 | assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); |
| 56 | windows.EnterCriticalSection(&self.lock); | 56 | windows.EnterCriticalSection(&self.lock); |
| 57 | return Held { .mutex = self }; | 57 | return Held{ .mutex = self }; |
| 58 | } | 58 | } |
| 59 | }, | 59 | }, |
| 60 | else => std.Mutex, | 60 | else => std.Mutex, |
| ... | @@ -89,10 +89,7 @@ test "std.StaticallyInitializedMutex" { | ... | @@ -89,10 +89,7 @@ test "std.StaticallyInitializedMutex" { |
| 89 | var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); | 89 | var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); |
| 90 | var a = &fixed_buffer_allocator.allocator; | 90 | var a = &fixed_buffer_allocator.allocator; |
| 91 | 91 | ||
| 92 | 92 | var context = TestContext{ .data = 0 }; | |
| 93 | var context = TestContext{ | ||
| 94 | .data = 0, | ||
| 95 | }; | ||
| 96 | 93 | ||
| 97 | if (builtin.single_threaded) { | 94 | if (builtin.single_threaded) { |
| 98 | TestContext.worker(&context); | 95 | TestContext.worker(&context); |
std/std.zig created+95| ... | @@ -0,0 +1,95 @@ | ||
| 1 | pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList; | ||
| 2 | pub const ArrayList = @import("array_list.zig").ArrayList; | ||
| 3 | pub const AutoHashMap = @import("hash_map.zig").AutoHashMap; | ||
| 4 | pub const BufMap = @import("buf_map.zig").BufMap; | ||
| 5 | pub const BufSet = @import("buf_set.zig").BufSet; | ||
| 6 | pub const Buffer = @import("buffer.zig").Buffer; | ||
| 7 | pub const BufferOutStream = @import("io.zig").BufferOutStream; | ||
| 8 | pub const DynLib = @import("dynamic_library.zig").DynLib; | ||
| 9 | pub const HashMap = @import("hash_map.zig").HashMap; | ||
| 10 | pub const LinkedList = @import("linked_list.zig").LinkedList; | ||
| 11 | pub const Mutex = @import("mutex.zig").Mutex; | ||
| 12 | pub const PriorityQueue = @import("priority_queue.zig").PriorityQueue; | ||
| 13 | pub const StaticallyInitializedMutex = @import("statically_initialized_mutex.zig").StaticallyInitializedMutex; | ||
| 14 | pub const SegmentedList = @import("segmented_list.zig").SegmentedList; | ||
| 15 | pub const SpinLock = @import("spinlock.zig").SpinLock; | ||
| 16 | |||
| 17 | pub const atomic = @import("atomic.zig"); | ||
| 18 | pub const base64 = @import("base64.zig"); | ||
| 19 | pub const build = @import("build.zig"); | ||
| 20 | pub const c = @import("c.zig"); | ||
| 21 | pub const coff = @import("coff.zig"); | ||
| 22 | pub const crypto = @import("crypto.zig"); | ||
| 23 | pub const cstr = @import("cstr.zig"); | ||
| 24 | pub const debug = @import("debug.zig"); | ||
| 25 | pub const dwarf = @import("dwarf.zig"); | ||
| 26 | pub const elf = @import("elf.zig"); | ||
| 27 | pub const empty_import = @import("empty.zig"); | ||
| 28 | pub const event = @import("event.zig"); | ||
| 29 | pub const fmt = @import("fmt.zig"); | ||
| 30 | pub const hash = @import("hash.zig"); | ||
| 31 | pub const hash_map = @import("hash_map.zig"); | ||
| 32 | pub const heap = @import("heap.zig"); | ||
| 33 | pub const io = @import("io.zig"); | ||
| 34 | pub const json = @import("json.zig"); | ||
| 35 | pub const lazyInit = @import("lazy_init.zig").lazyInit; | ||
| 36 | pub const macho = @import("macho.zig"); | ||
| 37 | pub const math = @import("math.zig"); | ||
| 38 | pub const mem = @import("mem.zig"); | ||
| 39 | pub const meta = @import("meta.zig"); | ||
| 40 | pub const net = @import("net.zig"); | ||
| 41 | pub const os = @import("os.zig"); | ||
| 42 | pub const pdb = @import("pdb.zig"); | ||
| 43 | pub const rand = @import("rand.zig"); | ||
| 44 | pub const rb = @import("rb.zig"); | ||
| 45 | pub const sort = @import("sort.zig"); | ||
| 46 | pub const testing = @import("testing.zig"); | ||
| 47 | pub const unicode = @import("unicode.zig"); | ||
| 48 | pub const zig = @import("zig.zig"); | ||
| 49 | |||
| 50 | test "std" { | ||
| 51 | // run tests from these | ||
| 52 | _ = @import("array_list.zig"); | ||
| 53 | _ = @import("atomic.zig"); | ||
| 54 | _ = @import("buf_map.zig"); | ||
| 55 | _ = @import("buf_set.zig"); | ||
| 56 | _ = @import("buffer.zig"); | ||
| 57 | _ = @import("hash_map.zig"); | ||
| 58 | _ = @import("linked_list.zig"); | ||
| 59 | _ = @import("mutex.zig"); | ||
| 60 | _ = @import("statically_initialized_mutex.zig"); | ||
| 61 | _ = @import("segmented_list.zig"); | ||
| 62 | _ = @import("spinlock.zig"); | ||
| 63 | |||
| 64 | _ = @import("base64.zig"); | ||
| 65 | _ = @import("build.zig"); | ||
| 66 | _ = @import("c.zig"); | ||
| 67 | _ = @import("coff.zig"); | ||
| 68 | _ = @import("crypto.zig"); | ||
| 69 | _ = @import("cstr.zig"); | ||
| 70 | _ = @import("debug.zig"); | ||
| 71 | _ = @import("dwarf.zig"); | ||
| 72 | _ = @import("dynamic_library.zig"); | ||
| 73 | _ = @import("elf.zig"); | ||
| 74 | _ = @import("empty.zig"); | ||
| 75 | _ = @import("event.zig"); | ||
| 76 | _ = @import("fmt.zig"); | ||
| 77 | _ = @import("hash.zig"); | ||
| 78 | _ = @import("heap.zig"); | ||
| 79 | _ = @import("io.zig"); | ||
| 80 | _ = @import("json.zig"); | ||
| 81 | _ = @import("lazy_init.zig"); | ||
| 82 | _ = @import("macho.zig"); | ||
| 83 | _ = @import("math.zig"); | ||
| 84 | _ = @import("mem.zig"); | ||
| 85 | _ = @import("meta.zig"); | ||
| 86 | _ = @import("net.zig"); | ||
| 87 | _ = @import("os.zig"); | ||
| 88 | _ = @import("pdb.zig"); | ||
| 89 | _ = @import("priority_queue.zig"); | ||
| 90 | _ = @import("rand.zig"); | ||
| 91 | _ = @import("sort.zig"); | ||
| 92 | _ = @import("testing.zig"); | ||
| 93 | _ = @import("unicode.zig"); | ||
| 94 | _ = @import("zig.zig"); | ||
| 95 | } | ||
std/testing.zig+1-1| ... | @@ -1,6 +1,6 @@ | ... | @@ -1,6 +1,6 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | const TypeId = builtin.TypeId; | 2 | const TypeId = builtin.TypeId; |
| 3 | const std = @import("index.zig"); | 3 | const std = @import("std.zig"); |
| 4 | 4 | ||
| 5 | /// This function is intended to be used only in tests. It prints diagnostics to stderr | 5 | /// This function is intended to be used only in tests. It prints diagnostics to stderr |
| 6 | /// and then aborts when actual_error_union is not expected_error. | 6 | /// and then aborts when actual_error_union is not expected_error. |
std/unicode.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("./index.zig"); | 1 | const std = @import("./std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const testing = std.testing; | 4 | const testing = std.testing; |
std/zig.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const tokenizer = @import("zig/tokenizer.zig"); | ||
| 2 | pub const Token = tokenizer.Token; | ||
| 3 | pub const Tokenizer = tokenizer.Tokenizer; | ||
| 4 | pub const parse = @import("zig/parse.zig").parse; | ||
| 5 | pub const parseStringLiteral = @import("zig/parse_string_literal.zig").parseStringLiteral; | ||
| 6 | pub const render = @import("zig/render.zig").render; | ||
| 7 | pub const ast = @import("zig/ast.zig"); | ||
| 8 | |||
| 9 | test "std.zig tests" { | ||
| 10 | _ = @import("zig/ast.zig"); | ||
| 11 | _ = @import("zig/parse.zig"); | ||
| 12 | _ = @import("zig/render.zig"); | ||
| 13 | _ = @import("zig/tokenizer.zig"); | ||
| 14 | _ = @import("zig/parse_string_literal.zig"); | ||
| 15 | } | ||
| 16 | |||
std/zig/ast.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const SegmentedList = std.SegmentedList; | 4 | const SegmentedList = std.SegmentedList; |
std/zig/bench.zig+1-1| ... | @@ -5,7 +5,7 @@ const Tokenizer = std.zig.Tokenizer; | ... | @@ -5,7 +5,7 @@ const Tokenizer = std.zig.Tokenizer; |
| 5 | const Parser = std.zig.Parser; | 5 | const Parser = std.zig.Parser; |
| 6 | const io = std.io; | 6 | const io = std.io; |
| 7 | 7 | ||
| 8 | const source = @embedFile("../os/index.zig"); | 8 | const source = @embedFile("../os.zig"); |
| 9 | var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; | 9 | var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; |
| 10 | 10 | ||
| 11 | pub fn main() !void { | 11 | pub fn main() !void { |
std/zig/index.zig deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | const tokenizer = @import("tokenizer.zig"); | ||
| 2 | pub const Token = tokenizer.Token; | ||
| 3 | pub const Tokenizer = tokenizer.Tokenizer; | ||
| 4 | pub const parse = @import("parse.zig").parse; | ||
| 5 | pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral; | ||
| 6 | pub const render = @import("render.zig").render; | ||
| 7 | pub const ast = @import("ast.zig"); | ||
| 8 | |||
| 9 | test "std.zig tests" { | ||
| 10 | _ = @import("ast.zig"); | ||
| 11 | _ = @import("parse.zig"); | ||
| 12 | _ = @import("render.zig"); | ||
| 13 | _ = @import("tokenizer.zig"); | ||
| 14 | _ = @import("parse_string_literal.zig"); | ||
| 15 | } | ||
| 16 | |||
std/zig/parse.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | const mem = std.mem; | 3 | const mem = std.mem; |
| 4 | const ast = std.zig.ast; | 4 | const ast = std.zig.ast; |
std/zig/parse_string_literal.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const assert = std.debug.assert; | 2 | const assert = std.debug.assert; |
| 3 | 3 | ||
| 4 | const State = enum { | 4 | const State = enum { |
std/zig/render.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const builtin = @import("builtin"); | 2 | const builtin = @import("builtin"); |
| 3 | const assert = std.debug.assert; | 3 | const assert = std.debug.assert; |
| 4 | const mem = std.mem; | 4 | const mem = std.mem; |
std/zig/tokenizer.zig+1-1| ... | @@ -1,4 +1,4 @@ | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | 1 | const std = @import("../std.zig"); |
| 2 | const mem = std.mem; | 2 | const mem = std.mem; |
| 3 | 3 | ||
| 4 | pub const Token = struct { | 4 | pub const Token = struct { |
test/stage1/behavior.zig+2-2| ... | @@ -56,14 +56,14 @@ comptime { | ... | @@ -56,14 +56,14 @@ comptime { |
| 56 | _ = @import("behavior/math.zig"); | 56 | _ = @import("behavior/math.zig"); |
| 57 | _ = @import("behavior/merge_error_sets.zig"); | 57 | _ = @import("behavior/merge_error_sets.zig"); |
| 58 | _ = @import("behavior/misc.zig"); | 58 | _ = @import("behavior/misc.zig"); |
| 59 | _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); | 59 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); |
| 60 | _ = @import("behavior/new_stack_call.zig"); | 60 | _ = @import("behavior/new_stack_call.zig"); |
| 61 | _ = @import("behavior/null.zig"); | 61 | _ = @import("behavior/null.zig"); |
| 62 | _ = @import("behavior/optional.zig"); | 62 | _ = @import("behavior/optional.zig"); |
| 63 | _ = @import("behavior/pointers.zig"); | 63 | _ = @import("behavior/pointers.zig"); |
| 64 | _ = @import("behavior/popcount.zig"); | 64 | _ = @import("behavior/popcount.zig"); |
| 65 | _ = @import("behavior/ptrcast.zig"); | 65 | _ = @import("behavior/ptrcast.zig"); |
| 66 | _ = @import("behavior/pub_enum/index.zig"); | 66 | _ = @import("behavior/pub_enum.zig"); |
| 67 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); | 67 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| 68 | _ = @import("behavior/reflection.zig"); | 68 | _ = @import("behavior/reflection.zig"); |
| 69 | _ = @import("behavior/sizeof_and_typeof.zig"); | 69 | _ = @import("behavior/sizeof_and_typeof.zig"); |
test/stage1/behavior/namespace_depends_on_compile_var.zig created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const expect = @import("std").testing.expect; | ||
| 3 | |||
| 4 | test "namespace depends on compile var" { | ||
| 5 | if (some_namespace.a_bool) { | ||
| 6 | expect(some_namespace.a_bool); | ||
| 7 | } else { | ||
| 8 | expect(!some_namespace.a_bool); | ||
| 9 | } | ||
| 10 | } | ||
| 11 | const some_namespace = switch (builtin.os) { | ||
| 12 | builtin.Os.linux => @import("namespace_depends_on_compile_var/a.zig"), | ||
| 13 | else => @import("namespace_depends_on_compile_var/b.zig"), | ||
| 14 | }; | ||
test/stage1/behavior/namespace_depends_on_compile_var/index.zig deleted-14| ... | @@ -1,14 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const expect = @import("std").testing.expect; | ||
| 3 | |||
| 4 | test "namespace depends on compile var" { | ||
| 5 | if (some_namespace.a_bool) { | ||
| 6 | expect(some_namespace.a_bool); | ||
| 7 | } else { | ||
| 8 | expect(!some_namespace.a_bool); | ||
| 9 | } | ||
| 10 | } | ||
| 11 | const some_namespace = switch (builtin.os) { | ||
| 12 | builtin.Os.linux => @import("a.zig"), | ||
| 13 | else => @import("b.zig"), | ||
| 14 | }; | ||
test/stage1/behavior/pub_enum.zig created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | const other = @import("pub_enum/other.zig"); | ||
| 2 | const expect = @import("std").testing.expect; | ||
| 3 | |||
| 4 | test "pub enum" { | ||
| 5 | pubEnumTest(other.APubEnum.Two); | ||
| 6 | } | ||
| 7 | fn pubEnumTest(foo: other.APubEnum) void { | ||
| 8 | expect(foo == other.APubEnum.Two); | ||
| 9 | } | ||
| 10 | |||
| 11 | test "cast with imported symbol" { | ||
| 12 | expect(other.size_t(42) == 42); | ||
| 13 | } | ||
test/stage1/behavior/pub_enum/index.zig deleted-13| ... | @@ -1,13 +0,0 @@ | ||
| 1 | const other = @import("other.zig"); | ||
| 2 | const expect = @import("std").testing.expect; | ||
| 3 | |||
| 4 | test "pub enum" { | ||
| 5 | pubEnumTest(other.APubEnum.Two); | ||
| 6 | } | ||
| 7 | fn pubEnumTest(foo: other.APubEnum) void { | ||
| 8 | expect(foo == other.APubEnum.Two); | ||
| 9 | } | ||
| 10 | |||
| 11 | test "cast with imported symbol" { | ||
| 12 | expect(other.size_t(42) == 42); | ||
| 13 | } | ||