| author | |
| committer | |
| log | 0714e19598e91f457d3d0119856fd7a24dd4f150 |
| tree | 07b65f462477aea604d6b76eb0ab930c03d5c92e |
| parent | 99b6429a89cda26305bde2c73eecc33eff022cb8 |
| parent | 9c5852aa8674320d2912627708f32fea37d7cd08 |
| signature | Commit is signed but in an unrecognized format. |
256 files changed, 16974 insertions(+), 16761 deletions(-)
.builds/freebsd.yml+8-8| ... | ... | @@ -13,28 +13,28 @@ tasks: |
| 13 | 13 | - test: | |
| 14 | 14 | cd zig/build |
| 15 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 38 | # TODO enable all tests |
| 39 | 39 | #bin/zig build --build-file ../build.zig test |
| 40 | 40 | # TODO integrate with the download page updater and make a |
CMakeLists.txt+21-21| ... | ... | @@ -442,7 +442,7 @@ set(ZIG_CPP_SOURCES |
| 442 | 442 | |
| 443 | 443 | set(ZIG_STD_FILES |
| 444 | 444 | "array_list.zig" |
| 445 | "atomic/index.zig" | |
| 445 | "atomic.zig" | |
| 446 | 446 | "atomic/int.zig" |
| 447 | 447 | "atomic/queue.zig" |
| 448 | 448 | "atomic/stack.zig" |
| ... | ... | @@ -452,17 +452,17 @@ set(ZIG_STD_FILES |
| 452 | 452 | "buffer.zig" |
| 453 | 453 | "build.zig" |
| 454 | 454 | "build/fmt.zig" |
| 455 | "c.zig" | |
| 455 | 456 | "c/darwin.zig" |
| 456 | 457 | "c/freebsd.zig" |
| 457 | "c/index.zig" | |
| 458 | 458 | "c/linux.zig" |
| 459 | 459 | "c/netbsd.zig" |
| 460 | 460 | "c/windows.zig" |
| 461 | 461 | "coff.zig" |
| 462 | "crypto.zig" | |
| 462 | 463 | "crypto/blake2.zig" |
| 463 | 464 | "crypto/chacha20.zig" |
| 464 | 465 | "crypto/hmac.zig" |
| 465 | "crypto/index.zig" | |
| 466 | 466 | "crypto/md5.zig" |
| 467 | 467 | "crypto/poly1305.zig" |
| 468 | 468 | "crypto/sha1.zig" |
| ... | ... | @@ -470,8 +470,8 @@ set(ZIG_STD_FILES |
| 470 | 470 | "crypto/sha3.zig" |
| 471 | 471 | "crypto/x25519.zig" |
| 472 | 472 | "cstr.zig" |
| 473 | "debug.zig" | |
| 473 | 474 | "debug/failing_allocator.zig" |
| 474 | "debug/index.zig" | |
| 475 | 475 | "dwarf.zig" |
| 476 | 476 | "dynamic_library.zig" |
| 477 | 477 | "elf.zig" |
| ... | ... | @@ -488,25 +488,25 @@ set(ZIG_STD_FILES |
| 488 | 488 | "event/net.zig" |
| 489 | 489 | "event/rwlock.zig" |
| 490 | 490 | "event/rwlocked.zig" |
| 491 | "fmt.zig" | |
| 492 | "fmt/errol.zig" | |
| 491 | 493 | "fmt/errol/enum3.zig" |
| 492 | "fmt/errol/index.zig" | |
| 493 | 494 | "fmt/errol/lookup.zig" |
| 494 | "fmt/index.zig" | |
| 495 | 495 | "fmt/parse_float.zig" |
| 496 | "hash.zig" | |
| 496 | 497 | "hash/adler.zig" |
| 497 | 498 | "hash/crc.zig" |
| 498 | 499 | "hash/fnv.zig" |
| 499 | "hash/index.zig" | |
| 500 | 500 | "hash/siphash.zig" |
| 501 | 501 | "hash_map.zig" |
| 502 | 502 | "heap.zig" |
| 503 | "index.zig" | |
| 504 | 503 | "io.zig" |
| 505 | 504 | "io/seekable_stream.zig" |
| 506 | 505 | "json.zig" |
| 507 | 506 | "lazy_init.zig" |
| 508 | 507 | "linked_list.zig" |
| 509 | 508 | "macho.zig" |
| 509 | "math.zig" | |
| 510 | 510 | "math/acos.zig" |
| 511 | 511 | "math/acosh.zig" |
| 512 | 512 | "math/asin.zig" |
| ... | ... | @@ -514,10 +514,11 @@ set(ZIG_STD_FILES |
| 514 | 514 | "math/atan.zig" |
| 515 | 515 | "math/atan2.zig" |
| 516 | 516 | "math/atanh.zig" |
| 517 | "math/big/index.zig" | |
| 517 | "math/big.zig" | |
| 518 | 518 | "math/big/int.zig" |
| 519 | 519 | "math/cbrt.zig" |
| 520 | 520 | "math/ceil.zig" |
| 521 | "math/complex.zig" | |
| 521 | 522 | "math/complex/abs.zig" |
| 522 | 523 | "math/complex/acos.zig" |
| 523 | 524 | "math/complex/acosh.zig" |
| ... | ... | @@ -530,7 +531,6 @@ set(ZIG_STD_FILES |
| 530 | 531 | "math/complex/cos.zig" |
| 531 | 532 | "math/complex/cosh.zig" |
| 532 | 533 | "math/complex/exp.zig" |
| 533 | "math/complex/index.zig" | |
| 534 | 534 | "math/complex/ldexp.zig" |
| 535 | 535 | "math/complex/log.zig" |
| 536 | 536 | "math/complex/pow.zig" |
| ... | ... | @@ -553,7 +553,6 @@ set(ZIG_STD_FILES |
| 553 | 553 | "math/frexp.zig" |
| 554 | 554 | "math/hypot.zig" |
| 555 | 555 | "math/ilogb.zig" |
| 556 | "math/index.zig" | |
| 557 | 556 | "math/inf.zig" |
| 558 | 557 | "math/isfinite.zig" |
| 559 | 558 | "math/isinf.zig" |
| ... | ... | @@ -578,50 +577,51 @@ set(ZIG_STD_FILES |
| 578 | 577 | "math/tanh.zig" |
| 579 | 578 | "math/trunc.zig" |
| 580 | 579 | "mem.zig" |
| 581 | "meta/index.zig" | |
| 580 | "meta.zig" | |
| 582 | 581 | "meta/trait.zig" |
| 583 | 582 | "mutex.zig" |
| 584 | 583 | "net.zig" |
| 584 | "os.zig" | |
| 585 | 585 | "os/child_process.zig" |
| 586 | 586 | "os/darwin.zig" |
| 587 | 587 | "os/darwin/errno.zig" |
| 588 | 588 | "os/epoch.zig" |
| 589 | 589 | "os/file.zig" |
| 590 | "os/freebsd.zig" | |
| 590 | 591 | "os/freebsd/errno.zig" |
| 591 | "os/freebsd/index.zig" | |
| 592 | 592 | "os/get_app_data_dir.zig" |
| 593 | 593 | "os/get_user_id.zig" |
| 594 | "os/index.zig" | |
| 594 | "os/linux.zig" | |
| 595 | 595 | "os/linux/arm64.zig" |
| 596 | 596 | "os/linux/errno.zig" |
| 597 | "os/linux/index.zig" | |
| 598 | 597 | "os/linux/vdso.zig" |
| 599 | 598 | "os/linux/x86_64.zig" |
| 599 | "os/netbsd.zig" | |
| 600 | 600 | "os/netbsd/errno.zig" |
| 601 | "os/netbsd/index.zig" | |
| 602 | 601 | "os/path.zig" |
| 603 | 602 | "os/time.zig" |
| 604 | 603 | "os/uefi.zig" |
| 604 | "os/windows.zig" | |
| 605 | 605 | "os/windows/advapi32.zig" |
| 606 | 606 | "os/windows/error.zig" |
| 607 | "os/windows/index.zig" | |
| 608 | 607 | "os/windows/kernel32.zig" |
| 609 | 608 | "os/windows/ntdll.zig" |
| 610 | 609 | "os/windows/ole32.zig" |
| 611 | 610 | "os/windows/shell32.zig" |
| 612 | "os/windows/tls.zig" | |
| 613 | 611 | "os/windows/util.zig" |
| 614 | 612 | "os/zen.zig" |
| 615 | 613 | "pdb.zig" |
| 616 | 614 | "priority_queue.zig" |
| 617 | "rand/index.zig" | |
| 615 | "rand.zig" | |
| 618 | 616 | "rand/ziggurat.zig" |
| 619 | 617 | "segmented_list.zig" |
| 620 | 618 | "sort.zig" |
| 621 | 619 | "special/bootstrap.zig" |
| 622 | 620 | "special/bootstrap_lib.zig" |
| 621 | "special/bootstrap_windows_tls.zig" | |
| 623 | 622 | "special/build_runner.zig" |
| 624 | 623 | "special/builtin.zig" |
| 624 | "special/compiler_rt.zig" | |
| 625 | 625 | "special/compiler_rt/addXf3.zig" |
| 626 | 626 | "special/compiler_rt/aulldiv.zig" |
| 627 | 627 | "special/compiler_rt/aullrem.zig" |
| ... | ... | @@ -656,7 +656,6 @@ set(ZIG_STD_FILES |
| 656 | 656 | "special/compiler_rt/floatuntidf.zig" |
| 657 | 657 | "special/compiler_rt/floatuntisf.zig" |
| 658 | 658 | "special/compiler_rt/floatuntitf.zig" |
| 659 | "special/compiler_rt/index.zig" | |
| 660 | 659 | "special/compiler_rt/muloti4.zig" |
| 661 | 660 | "special/compiler_rt/multi3.zig" |
| 662 | 661 | "special/compiler_rt/truncXfYf2.zig" |
| ... | ... | @@ -674,10 +673,11 @@ set(ZIG_STD_FILES |
| 674 | 673 | "special/test_runner.zig" |
| 675 | 674 | "spinlock.zig" |
| 676 | 675 | "statically_initialized_mutex.zig" |
| 676 | "std.zig" | |
| 677 | 677 | "testing.zig" |
| 678 | 678 | "unicode.zig" |
| 679 | "zig.zig" | |
| 679 | 680 | "zig/ast.zig" |
| 680 | "zig/index.zig" | |
| 681 | 681 | "zig/parse.zig" |
| 682 | 682 | "zig/parse_string_literal.zig" |
| 683 | 683 | "zig/render.zig" |
build.zig+2-2| ... | ... | @@ -115,9 +115,9 @@ pub fn build(b: *Builder) !void { |
| 115 | 115 | |
| 116 | 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 | 122 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); |
| 123 | 123 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter, modes)); |
doc/langref.html.in+27-23| ... | ... | @@ -6116,7 +6116,7 @@ test "main" { |
| 6116 | 6116 | {#header_close#} |
| 6117 | 6117 | |
| 6118 | 6118 | {#header_open|@frameAddress#} |
| 6119 | <pre>{#syntax#}@frameAddress(){#endsyntax#}</pre> | |
| 6119 | <pre>{#syntax#}@frameAddress() usize{#endsyntax#}</pre> | |
| 6120 | 6120 | <p> |
| 6121 | 6121 | This function returns the base pointer of the current stack frame. |
| 6122 | 6122 | </p> |
| ... | ... | @@ -6482,17 +6482,18 @@ test "call foo" { |
| 6482 | 6482 | {#header_close#} |
| 6483 | 6483 | |
| 6484 | 6484 | {#header_open|@returnAddress#} |
| 6485 | <pre>{#syntax#}@returnAddress(){#endsyntax#}</pre> | |
| 6485 | <pre>{#syntax#}@returnAddress() usize{#endsyntax#}</pre> | |
| 6486 | 6486 | <p> |
| 6487 | This function returns a pointer to the return address of the current stack | |
| 6488 | frame. | |
| 6487 | This function returns the address of the next machine code instruction that will be executed | |
| 6488 | when the current function returns. | |
| 6489 | 6489 | </p> |
| 6490 | 6490 | <p> |
| 6491 | 6491 | The implications of this are target specific and not consistent across |
| 6492 | 6492 | all platforms. |
| 6493 | 6493 | </p> |
| 6494 | 6494 | <p> |
| 6495 | This function is only valid within function scope. | |
| 6495 | This function is only valid within function scope. If the function gets inlined into | |
| 6496 | a calling function, the returned address will apply to the calling function. | |
| 6496 | 6497 | </p> |
| 6497 | 6498 | {#header_close#} |
| 6498 | 6499 | {#header_open|@setAlignStack#} |
| ... | ... | @@ -6785,7 +6786,6 @@ pub const TypeId = enum { |
| 6785 | 6786 | Enum, |
| 6786 | 6787 | Union, |
| 6787 | 6788 | Fn, |
| 6788 | Namespace, | |
| 6789 | 6789 | Block, |
| 6790 | 6790 | BoundFn, |
| 6791 | 6791 | ArgTuple, |
| ... | ... | @@ -6820,7 +6820,6 @@ pub const TypeInfo = union(TypeId) { |
| 6820 | 6820 | Enum: Enum, |
| 6821 | 6821 | Union: Union, |
| 6822 | 6822 | Fn: Fn, |
| 6823 | Namespace: void, | |
| 6824 | 6823 | BoundFn: Fn, |
| 6825 | 6824 | ArgTuple: void, |
| 6826 | 6825 | Opaque: void, |
| ... | ... | @@ -6829,18 +6828,18 @@ pub const TypeInfo = union(TypeId) { |
| 6829 | 6828 | |
| 6830 | 6829 | pub const Int = struct { |
| 6831 | 6830 | is_signed: bool, |
| 6832 | bits: u8, | |
| 6831 | bits: comptime_int, | |
| 6833 | 6832 | }; |
| 6834 | 6833 | |
| 6835 | 6834 | pub const Float = struct { |
| 6836 | bits: u8, | |
| 6835 | bits: comptime_int, | |
| 6837 | 6836 | }; |
| 6838 | 6837 | |
| 6839 | 6838 | pub const Pointer = struct { |
| 6840 | 6839 | size: Size, |
| 6841 | 6840 | is_const: bool, |
| 6842 | 6841 | is_volatile: bool, |
| 6843 | alignment: u32, | |
| 6842 | alignment: comptime_int, | |
| 6844 | 6843 | child: type, |
| 6845 | 6844 | |
| 6846 | 6845 | pub const Size = enum { |
| ... | ... | @@ -6851,7 +6850,7 @@ pub const TypeInfo = union(TypeId) { |
| 6851 | 6850 | }; |
| 6852 | 6851 | |
| 6853 | 6852 | pub const Array = struct { |
| 6854 | len: usize, | |
| 6853 | len: comptime_int, | |
| 6855 | 6854 | child: type, |
| 6856 | 6855 | }; |
| 6857 | 6856 | |
| ... | ... | @@ -6863,7 +6862,7 @@ pub const TypeInfo = union(TypeId) { |
| 6863 | 6862 | |
| 6864 | 6863 | pub const StructField = struct { |
| 6865 | 6864 | name: []const u8, |
| 6866 | offset: ?usize, | |
| 6865 | offset: ?comptime_int, | |
| 6867 | 6866 | field_type: type, |
| 6868 | 6867 | }; |
| 6869 | 6868 | |
| ... | ... | @@ -6884,7 +6883,7 @@ pub const TypeInfo = union(TypeId) { |
| 6884 | 6883 | |
| 6885 | 6884 | pub const Error = struct { |
| 6886 | 6885 | name: []const u8, |
| 6887 | value: usize, | |
| 6886 | value: comptime_int, | |
| 6888 | 6887 | }; |
| 6889 | 6888 | |
| 6890 | 6889 | pub const ErrorSet = struct { |
| ... | ... | @@ -6893,7 +6892,7 @@ pub const TypeInfo = union(TypeId) { |
| 6893 | 6892 | |
| 6894 | 6893 | pub const EnumField = struct { |
| 6895 | 6894 | name: []const u8, |
| 6896 | value: usize, | |
| 6895 | value: comptime_int, | |
| 6897 | 6896 | }; |
| 6898 | 6897 | |
| 6899 | 6898 | pub const Enum = struct { |
| ... | ... | @@ -8167,17 +8166,18 @@ coding style. |
| 8167 | 8166 | </p> |
| 8168 | 8167 | <ul> |
| 8169 | 8168 | <li> |
| 8170 | If {#syntax#}x{#endsyntax#} is a {#syntax#}struct{#endsyntax#} (or an alias of a {#syntax#}struct{#endsyntax#}), | |
| 8171 | then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}. | |
| 8172 | </li> | |
| 8173 | <li> | |
| 8174 | If {#syntax#}x{#endsyntax#} otherwise identifies a type, {#syntax#}x{#endsyntax#} should have {#syntax#}snake_case{#endsyntax#}. | |
| 8169 | If {#syntax#}x{#endsyntax#} is a {#syntax#}type{#endsyntax#} | |
| 8170 | then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}, unless it | |
| 8171 | is a {#syntax#}struct{#endsyntax#} with 0 fields and is never meant to be instantiated, | |
| 8172 | in which case it is considered to be a "namespace" and uses {#syntax#}snake_case{#endsyntax#}. | |
| 8175 | 8173 | </li> |
| 8176 | 8174 | <li> |
| 8177 | If {#syntax#}x{#endsyntax#} is callable, and {#syntax#}x{#endsyntax#}'s return type is {#syntax#}type{#endsyntax#}, then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}. | |
| 8175 | If {#syntax#}x{#endsyntax#} is callable, and {#syntax#}x{#endsyntax#}'s return type is | |
| 8176 | {#syntax#}type{#endsyntax#}, then {#syntax#}x{#endsyntax#} should be {#syntax#}TitleCase{#endsyntax#}. | |
| 8178 | 8177 | </li> |
| 8179 | 8178 | <li> |
| 8180 | If {#syntax#}x{#endsyntax#} is otherwise callable, then {#syntax#}x{#endsyntax#} should be {#syntax#}camelCase{#endsyntax#}. | |
| 8179 | If {#syntax#}x{#endsyntax#} is otherwise callable, then {#syntax#}x{#endsyntax#} should | |
| 8180 | be {#syntax#}camelCase{#endsyntax#}. | |
| 8181 | 8181 | </li> |
| 8182 | 8182 | <li> |
| 8183 | 8183 | Otherwise, {#syntax#}x{#endsyntax#} should be {#syntax#}snake_case{#endsyntax#}. |
| ... | ... | @@ -8203,7 +8203,9 @@ const const_name = 42; |
| 8203 | 8203 | const primitive_type_alias = f32; |
| 8204 | 8204 | const string_alias = []u8; |
| 8205 | 8205 | |
| 8206 | const StructName = struct {}; | |
| 8206 | const StructName = struct { | |
| 8207 | field: i32, | |
| 8208 | }; | |
| 8207 | 8209 | const StructAlias = StructName; |
| 8208 | 8210 | |
| 8209 | 8211 | fn functionName(param_name: TypeName) void { |
| ... | ... | @@ -8231,7 +8233,9 @@ const xml_document = |
| 8231 | 8233 | \\<document> |
| 8232 | 8234 | \\</document> |
| 8233 | 8235 | ; |
| 8234 | const XmlParser = struct {}; | |
| 8236 | const XmlParser = struct { | |
| 8237 | field: i32, | |
| 8238 | }; | |
| 8235 | 8239 | |
| 8236 | 8240 | // The initials BE (Big Endian) are just another word in Zig identifier names. |
| 8237 | 8241 | fn readU32Be() u32 {} |
src-self-hosted/compilation.zig+2-2| ... | ... | @@ -47,7 +47,7 @@ pub const ZigCompiler = struct { |
| 47 | 47 | |
| 48 | 48 | var lazy_init_targets = std.lazyInit(void); |
| 49 | 49 | |
| 50 | fn init(loop: *event.Loop) !ZigCompiler { | |
| 50 | pub fn init(loop: *event.Loop) !ZigCompiler { | |
| 51 | 51 | lazy_init_targets.get() orelse { |
| 52 | 52 | Target.initializeAll(); |
| 53 | 53 | lazy_init_targets.resolve(); |
| ... | ... | @@ -533,7 +533,7 @@ pub const Compilation = struct { |
| 533 | 533 | const basename = std.os.path.basename(root_src); |
| 534 | 534 | |
| 535 | 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 | 537 | try comp.root_package.add("std", comp.std_package); |
| 538 | 538 | } else { |
| 539 | 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 | 11 | const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" }); |
| 12 | 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 | 15 | defer allocator.free(test_index_file); |
| 16 | 16 | |
| 17 | 17 | var file = try os.File.openRead(test_index_file); |
src-self-hosted/type.zig-12| ... | ... | @@ -39,7 +39,6 @@ pub const Type = struct { |
| 39 | 39 | Id.ErrorSet => @fieldParentPtr(ErrorSet, "base", base).destroy(comp), |
| 40 | 40 | Id.Enum => @fieldParentPtr(Enum, "base", base).destroy(comp), |
| 41 | 41 | Id.Union => @fieldParentPtr(Union, "base", base).destroy(comp), |
| 42 | Id.Namespace => @fieldParentPtr(Namespace, "base", base).destroy(comp), | |
| 43 | 42 | Id.BoundFn => @fieldParentPtr(BoundFn, "base", base).destroy(comp), |
| 44 | 43 | Id.ArgTuple => @fieldParentPtr(ArgTuple, "base", base).destroy(comp), |
| 45 | 44 | Id.Opaque => @fieldParentPtr(Opaque, "base", base).destroy(comp), |
| ... | ... | @@ -73,7 +72,6 @@ pub const Type = struct { |
| 73 | 72 | Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context), |
| 74 | 73 | Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context), |
| 75 | 74 | Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context), |
| 76 | Id.Namespace => unreachable, | |
| 77 | 75 | Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context), |
| 78 | 76 | Id.ArgTuple => unreachable, |
| 79 | 77 | Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context), |
| ... | ... | @@ -89,7 +87,6 @@ pub const Type = struct { |
| 89 | 87 | Id.ComptimeInt, |
| 90 | 88 | Id.Undefined, |
| 91 | 89 | Id.Null, |
| 92 | Id.Namespace, | |
| 93 | 90 | Id.BoundFn, |
| 94 | 91 | Id.ArgTuple, |
| 95 | 92 | Id.Opaque, |
| ... | ... | @@ -123,7 +120,6 @@ pub const Type = struct { |
| 123 | 120 | Id.ComptimeInt, |
| 124 | 121 | Id.Undefined, |
| 125 | 122 | Id.Null, |
| 126 | Id.Namespace, | |
| 127 | 123 | Id.BoundFn, |
| 128 | 124 | Id.ArgTuple, |
| 129 | 125 | Id.Opaque, |
| ... | ... | @@ -1020,14 +1016,6 @@ pub const Type = struct { |
| 1020 | 1016 | } |
| 1021 | 1017 | }; |
| 1022 | 1018 | |
| 1023 | pub const Namespace = struct { | |
| 1024 | base: Type, | |
| 1025 | ||
| 1026 | pub fn destroy(self: *Namespace, comp: *Compilation) void { | |
| 1027 | comp.gpa().destroy(self); | |
| 1028 | } | |
| 1029 | }; | |
| 1030 | ||
| 1031 | 1019 | pub const BoundFn = struct { |
| 1032 | 1020 | base: Type, |
| 1033 | 1021 |
src/all_types.hpp+51-49| ... | ... | @@ -21,7 +21,6 @@ |
| 21 | 21 | #include "libc_installation.hpp" |
| 22 | 22 | |
| 23 | 23 | struct AstNode; |
| 24 | struct ImportTableEntry; | |
| 25 | 24 | struct ZigFn; |
| 26 | 25 | struct Scope; |
| 27 | 26 | struct ScopeBlock; |
| ... | ... | @@ -317,7 +316,6 @@ struct ConstExprValue { |
| 317 | 316 | ConstUnionValue x_union; |
| 318 | 317 | ConstArrayValue x_array; |
| 319 | 318 | ConstPtrValue x_ptr; |
| 320 | ImportTableEntry *x_import; | |
| 321 | 319 | ConstArgTuple x_arg_tuple; |
| 322 | 320 | |
| 323 | 321 | // populated if special == ConstValSpecialRuntime |
| ... | ... | @@ -369,10 +367,8 @@ struct Tld { |
| 369 | 367 | VisibMod visib_mod; |
| 370 | 368 | AstNode *source_node; |
| 371 | 369 | |
| 372 | ImportTableEntry *import; | |
| 370 | ZigType *import; | |
| 373 | 371 | Scope *parent_scope; |
| 374 | // set this flag temporarily to detect infinite loops | |
| 375 | bool dep_loop_flag; | |
| 376 | 372 | TldResolution resolution; |
| 377 | 373 | }; |
| 378 | 374 | |
| ... | ... | @@ -382,6 +378,7 @@ struct TldVar { |
| 382 | 378 | ZigVar *var; |
| 383 | 379 | Buf *extern_lib_name; |
| 384 | 380 | Buf *section_name; |
| 381 | bool analyzing_type; // flag to detect dependency loops | |
| 385 | 382 | }; |
| 386 | 383 | |
| 387 | 384 | struct TldFn { |
| ... | ... | @@ -700,7 +697,7 @@ struct AstNodeUse { |
| 700 | 697 | AstNode *expr; |
| 701 | 698 | |
| 702 | 699 | TldResolution resolution; |
| 703 | ConstExprValue *value; | |
| 700 | ConstExprValue *using_namespace_value; | |
| 704 | 701 | }; |
| 705 | 702 | |
| 706 | 703 | struct AstNodeIfBoolExpr { |
| ... | ... | @@ -937,7 +934,7 @@ struct AstNode { |
| 937 | 934 | enum NodeType type; |
| 938 | 935 | size_t line; |
| 939 | 936 | size_t column; |
| 940 | ImportTableEntry *owner; | |
| 937 | ZigType *owner; | |
| 941 | 938 | union { |
| 942 | 939 | AstNodeFnDef fn_def; |
| 943 | 940 | AstNodeFnProto fn_proto; |
| ... | ... | @@ -1075,12 +1072,32 @@ enum ResolveStatus { |
| 1075 | 1072 | ResolveStatusSizeKnown, |
| 1076 | 1073 | }; |
| 1077 | 1074 | |
| 1075 | struct ZigPackage { | |
| 1076 | Buf root_src_dir; | |
| 1077 | Buf root_src_path; // relative to root_src_dir | |
| 1078 | Buf pkg_path; // a.b.c.d which follows the package dependency chain from the root package | |
| 1079 | ||
| 1080 | // reminder: hash tables must be initialized before use | |
| 1081 | HashMap<Buf *, ZigPackage *, buf_hash, buf_eql_buf> package_table; | |
| 1082 | }; | |
| 1083 | ||
| 1084 | // Stuff that only applies to a struct which is the implicit root struct of a file | |
| 1085 | struct RootStruct { | |
| 1086 | ZigPackage *package; | |
| 1087 | Buf *path; // relative to root_package->root_src_dir | |
| 1088 | ZigList<size_t> *line_offsets; | |
| 1089 | Buf *source_code; | |
| 1090 | AstNode *c_import_node; | |
| 1091 | ZigLLVMDIFile *di_file; | |
| 1092 | }; | |
| 1093 | ||
| 1078 | 1094 | struct ZigTypeStruct { |
| 1079 | 1095 | AstNode *decl_node; |
| 1080 | 1096 | TypeStructField *fields; |
| 1081 | 1097 | ScopeDecls *decls_scope; |
| 1082 | 1098 | uint64_t size_bytes; |
| 1083 | 1099 | HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name; |
| 1100 | RootStruct *root_struct; | |
| 1084 | 1101 | |
| 1085 | 1102 | uint32_t src_field_count; |
| 1086 | 1103 | uint32_t gen_field_count; |
| ... | ... | @@ -1232,7 +1249,6 @@ enum ZigTypeId { |
| 1232 | 1249 | ZigTypeIdEnum, |
| 1233 | 1250 | ZigTypeIdUnion, |
| 1234 | 1251 | ZigTypeIdFn, |
| 1235 | ZigTypeIdNamespace, | |
| 1236 | 1252 | ZigTypeIdBoundFn, |
| 1237 | 1253 | ZigTypeIdArgTuple, |
| 1238 | 1254 | ZigTypeIdOpaque, |
| ... | ... | @@ -1246,6 +1262,10 @@ enum OnePossibleValue { |
| 1246 | 1262 | OnePossibleValueYes, |
| 1247 | 1263 | }; |
| 1248 | 1264 | |
| 1265 | struct ZigTypeOpaque { | |
| 1266 | Buf *bare_name; | |
| 1267 | }; | |
| 1268 | ||
| 1249 | 1269 | struct ZigType { |
| 1250 | 1270 | ZigTypeId id; |
| 1251 | 1271 | Buf name; |
| ... | ... | @@ -1268,6 +1288,7 @@ struct ZigType { |
| 1268 | 1288 | ZigTypeBoundFn bound_fn; |
| 1269 | 1289 | ZigTypePromise promise; |
| 1270 | 1290 | ZigTypeVector vector; |
| 1291 | ZigTypeOpaque opaque; | |
| 1271 | 1292 | } data; |
| 1272 | 1293 | |
| 1273 | 1294 | // use these fields to make sure we don't duplicate type table entries for the same type |
| ... | ... | @@ -1285,29 +1306,6 @@ struct ZigType { |
| 1285 | 1306 | bool gen_h_loop_flag; |
| 1286 | 1307 | }; |
| 1287 | 1308 | |
| 1288 | struct PackageTableEntry { | |
| 1289 | Buf root_src_dir; | |
| 1290 | Buf root_src_path; // relative to root_src_dir | |
| 1291 | ||
| 1292 | // reminder: hash tables must be initialized before use | |
| 1293 | HashMap<Buf *, PackageTableEntry *, buf_hash, buf_eql_buf> package_table; | |
| 1294 | }; | |
| 1295 | ||
| 1296 | struct ImportTableEntry { | |
| 1297 | AstNode *root; | |
| 1298 | Buf *path; // relative to root_package->root_src_dir | |
| 1299 | PackageTableEntry *package; | |
| 1300 | ZigLLVMDIFile *di_file; | |
| 1301 | Buf *source_code; | |
| 1302 | ZigList<size_t> *line_offsets; | |
| 1303 | ScopeDecls *decls_scope; | |
| 1304 | AstNode *c_import_node; | |
| 1305 | bool any_imports_failed; | |
| 1306 | bool scanned; | |
| 1307 | ||
| 1308 | ZigList<AstNode *> use_decls; | |
| 1309 | }; | |
| 1310 | ||
| 1311 | 1309 | enum FnAnalState { |
| 1312 | 1310 | FnAnalStateReady, |
| 1313 | 1311 | FnAnalStateProbing, |
| ... | ... | @@ -1670,7 +1668,7 @@ struct CodeGen { |
| 1670 | 1668 | LLVMValueRef return_err_fn; |
| 1671 | 1669 | |
| 1672 | 1670 | // reminder: hash tables must be initialized before use |
| 1673 | HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; | |
| 1671 | HashMap<Buf *, ZigType *, buf_hash, buf_eql_buf> import_table; | |
| 1674 | 1672 | HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table; |
| 1675 | 1673 | HashMap<Buf *, ZigType *, buf_hash, buf_eql_buf> primitive_type_table; |
| 1676 | 1674 | HashMap<TypeId, ZigType *, type_id_hash, type_id_eql> type_table; |
| ... | ... | @@ -1684,8 +1682,6 @@ struct CodeGen { |
| 1684 | 1682 | HashMap<Buf *, ConstExprValue *, buf_hash, buf_eql_buf> string_literals_table; |
| 1685 | 1683 | HashMap<const ZigType *, ConstExprValue *, type_ptr_hash, type_ptr_eql> type_info_cache; |
| 1686 | 1684 | |
| 1687 | ZigList<ImportTableEntry *> import_queue; | |
| 1688 | size_t import_queue_index; | |
| 1689 | 1685 | ZigList<Tld *> resolve_queue; |
| 1690 | 1686 | size_t resolve_queue_index; |
| 1691 | 1687 | ZigList<AstNode *> use_queue; |
| ... | ... | @@ -1699,14 +1695,14 @@ struct CodeGen { |
| 1699 | 1695 | ZigList<ErrorTableEntry *> errors_by_index; |
| 1700 | 1696 | size_t largest_err_name_len; |
| 1701 | 1697 | |
| 1702 | PackageTableEntry *std_package; | |
| 1703 | PackageTableEntry *panic_package; | |
| 1704 | PackageTableEntry *test_runner_package; | |
| 1705 | PackageTableEntry *compile_var_package; | |
| 1706 | ImportTableEntry *compile_var_import; | |
| 1707 | ImportTableEntry *root_import; | |
| 1708 | ImportTableEntry *bootstrap_import; | |
| 1709 | ImportTableEntry *test_runner_import; | |
| 1698 | ZigPackage *std_package; | |
| 1699 | ZigPackage *panic_package; | |
| 1700 | ZigPackage *test_runner_package; | |
| 1701 | ZigPackage *compile_var_package; | |
| 1702 | ZigType *compile_var_import; | |
| 1703 | ZigType *root_import; | |
| 1704 | ZigType *bootstrap_import; | |
| 1705 | ZigType *test_runner_import; | |
| 1710 | 1706 | |
| 1711 | 1707 | struct { |
| 1712 | 1708 | ZigType *entry_bool; |
| ... | ... | @@ -1731,7 +1727,6 @@ struct CodeGen { |
| 1731 | 1727 | ZigType *entry_unreachable; |
| 1732 | 1728 | ZigType *entry_type; |
| 1733 | 1729 | ZigType *entry_invalid; |
| 1734 | ZigType *entry_namespace; | |
| 1735 | 1730 | ZigType *entry_block; |
| 1736 | 1731 | ZigType *entry_num_lit_int; |
| 1737 | 1732 | ZigType *entry_num_lit_float; |
| ... | ... | @@ -1851,7 +1846,7 @@ struct CodeGen { |
| 1851 | 1846 | Buf *root_out_name; |
| 1852 | 1847 | Buf *test_filter; |
| 1853 | 1848 | Buf *test_name_prefix; |
| 1854 | PackageTableEntry *root_package; | |
| 1849 | ZigPackage *root_package; | |
| 1855 | 1850 | Buf *zig_lib_dir; |
| 1856 | 1851 | Buf *zig_std_dir; |
| 1857 | 1852 | |
| ... | ... | @@ -1945,13 +1940,17 @@ struct ScopeDecls { |
| 1945 | 1940 | Scope base; |
| 1946 | 1941 | |
| 1947 | 1942 | HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> decl_table; |
| 1948 | bool safety_off; | |
| 1943 | ZigList<AstNode *> use_decls; | |
| 1949 | 1944 | AstNode *safety_set_node; |
| 1950 | bool fast_math_on; | |
| 1951 | 1945 | AstNode *fast_math_set_node; |
| 1952 | ImportTableEntry *import; | |
| 1946 | ZigType *import; | |
| 1953 | 1947 | // If this is a scope from a container, this is the type entry, otherwise null |
| 1954 | 1948 | ZigType *container_type; |
| 1949 | Buf *bare_name; | |
| 1950 | ||
| 1951 | bool safety_off; | |
| 1952 | bool fast_math_on; | |
| 1953 | bool any_imports_failed; | |
| 1955 | 1954 | }; |
| 1956 | 1955 | |
| 1957 | 1956 | // This scope comes from a block expression in user code. |
| ... | ... | @@ -3463,7 +3462,8 @@ static const size_t err_union_err_index = 0; |
| 3463 | 3462 | static const size_t err_union_payload_index = 1; |
| 3464 | 3463 | |
| 3465 | 3464 | // TODO call graph analysis to find out what this number needs to be for every function |
| 3466 | static const size_t stack_trace_ptr_count = 30; | |
| 3465 | // MUST BE A POWER OF TWO. | |
| 3466 | static const size_t stack_trace_ptr_count = 32; | |
| 3467 | 3467 | |
| 3468 | 3468 | // these belong to the async function |
| 3469 | 3469 | #define RETURN_ADDRESSES_FIELD_NAME "return_addresses" |
| ... | ... | @@ -3476,6 +3476,8 @@ static const size_t stack_trace_ptr_count = 30; |
| 3476 | 3476 | #define ERR_RET_TRACE_PTR_FIELD_NAME "err_ret_trace_ptr" |
| 3477 | 3477 | #define RESULT_PTR_FIELD_NAME "result_ptr" |
| 3478 | 3478 | |
| 3479 | #define NAMESPACE_SEP_CHAR '.' | |
| 3480 | #define NAMESPACE_SEP_STR "." | |
| 3479 | 3481 | |
| 3480 | 3482 | enum FloatMode { |
| 3481 | 3483 | FloatModeStrict, |
| ... | ... | @@ -3507,7 +3509,7 @@ struct FnWalkTypes { |
| 3507 | 3509 | }; |
| 3508 | 3510 | |
| 3509 | 3511 | struct FnWalkVars { |
| 3510 | ImportTableEntry *import; | |
| 3512 | ZigType *import; | |
| 3511 | 3513 | LLVMValueRef llvm_fn; |
| 3512 | 3514 | ZigFn *fn; |
| 3513 | 3515 | ZigVar *var; |
src/analyze.cpp+295-234| ... | ... | @@ -28,10 +28,14 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum |
| 28 | 28 | static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type); |
| 29 | 29 | static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry); |
| 30 | 30 | |
| 31 | static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token, | |
| 32 | Buf *msg) | |
| 33 | { | |
| 34 | if (owner->c_import_node != nullptr) { | |
| 31 | static bool is_top_level_struct(ZigType *import) { | |
| 32 | return import->id == ZigTypeIdStruct && import->data.structure.root_struct != nullptr; | |
| 33 | } | |
| 34 | ||
| 35 | static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ZigType *owner, Token *token, Buf *msg) { | |
| 36 | assert(is_top_level_struct(owner)); | |
| 37 | RootStruct *root_struct = owner->data.structure.root_struct; | |
| 38 | if (root_struct->c_import_node != nullptr) { | |
| 35 | 39 | // if this happens, then translate_c generated code that |
| 36 | 40 | // failed semantic analysis, which isn't supposed to happen |
| 37 | 41 | |
| ... | ... | @@ -46,18 +50,20 @@ static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTa |
| 46 | 50 | return note; |
| 47 | 51 | } |
| 48 | 52 | |
| 49 | ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, | |
| 50 | owner->source_code, owner->line_offsets, msg); | |
| 53 | ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column, | |
| 54 | root_struct->source_code, root_struct->line_offsets, msg); | |
| 51 | 55 | |
| 52 | 56 | err_msg_add_note(parent_msg, err); |
| 53 | 57 | return err; |
| 54 | 58 | } |
| 55 | 59 | |
| 56 | ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) { | |
| 57 | if (owner->c_import_node != nullptr) { | |
| 60 | ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg) { | |
| 61 | assert(is_top_level_struct(owner)); | |
| 62 | RootStruct *root_struct = owner->data.structure.root_struct; | |
| 63 | if (root_struct->c_import_node != nullptr) { | |
| 58 | 64 | // if this happens, then translate_c generated code that |
| 59 | 65 | // failed semantic analysis, which isn't supposed to happen |
| 60 | ErrorMsg *err = add_node_error(g, owner->c_import_node, | |
| 66 | ErrorMsg *err = add_node_error(g, root_struct->c_import_node, | |
| 61 | 67 | buf_sprintf("compiler bug: @cImport generated invalid zig code")); |
| 62 | 68 | |
| 63 | 69 | add_error_note_token(g, err, owner, token, msg); |
| ... | ... | @@ -65,8 +71,8 @@ ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf |
| 65 | 71 | g->errors.append(err); |
| 66 | 72 | return err; |
| 67 | 73 | } |
| 68 | ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column, | |
| 69 | owner->source_code, owner->line_offsets, msg); | |
| 74 | ErrorMsg *err = err_msg_create_with_line(root_struct->path, token->start_line, token->start_column, | |
| 75 | root_struct->source_code, root_struct->line_offsets, msg); | |
| 70 | 76 | |
| 71 | 77 | g->errors.append(err); |
| 72 | 78 | return err; |
| ... | ... | @@ -114,13 +120,16 @@ void init_scope(CodeGen *g, Scope *dest, ScopeId id, AstNode *source_node, Scope |
| 114 | 120 | dest->parent = parent; |
| 115 | 121 | } |
| 116 | 122 | |
| 117 | ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ImportTableEntry *import) { | |
| 123 | static ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, | |
| 124 | ZigType *import, Buf *bare_name) | |
| 125 | { | |
| 118 | 126 | assert(node == nullptr || node->type == NodeTypeContainerDecl || node->type == NodeTypeFnCallExpr); |
| 119 | 127 | ScopeDecls *scope = allocate<ScopeDecls>(1); |
| 120 | 128 | init_scope(g, &scope->base, ScopeIdDecls, node, parent); |
| 121 | 129 | scope->decl_table.init(4); |
| 122 | 130 | scope->container_type = container_type; |
| 123 | 131 | scope->import = import; |
| 132 | scope->bare_name = bare_name; | |
| 124 | 133 | return scope; |
| 125 | 134 | } |
| 126 | 135 | |
| ... | ... | @@ -207,11 +216,11 @@ Scope *create_coro_prelude_scope(CodeGen *g, AstNode *node, Scope *parent) { |
| 207 | 216 | return &scope->base; |
| 208 | 217 | } |
| 209 | 218 | |
| 210 | ImportTableEntry *get_scope_import(Scope *scope) { | |
| 219 | ZigType *get_scope_import(Scope *scope) { | |
| 211 | 220 | while (scope) { |
| 212 | 221 | if (scope->id == ScopeIdDecls) { |
| 213 | 222 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 214 | assert(decls_scope->import); | |
| 223 | assert(is_top_level_struct(decls_scope->import)); | |
| 215 | 224 | return decls_scope->import; |
| 216 | 225 | } |
| 217 | 226 | scope = scope->parent; |
| ... | ... | @@ -219,9 +228,12 @@ ImportTableEntry *get_scope_import(Scope *scope) { |
| 219 | 228 | zig_unreachable(); |
| 220 | 229 | } |
| 221 | 230 | |
| 222 | static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope) { | |
| 231 | static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope, | |
| 232 | Buf *bare_name) | |
| 233 | { | |
| 223 | 234 | ZigType *entry = new_type_table_entry(id); |
| 224 | *get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry, get_scope_import(parent_scope)); | |
| 235 | *get_container_scope_ptr(entry) = create_decls_scope(g, source_node, parent_scope, entry, | |
| 236 | get_scope_import(parent_scope), bare_name); | |
| 225 | 237 | return entry; |
| 226 | 238 | } |
| 227 | 239 | |
| ... | ... | @@ -261,7 +273,6 @@ AstNode *type_decl_node(ZigType *type_entry) { |
| 261 | 273 | case ZigTypeIdErrorUnion: |
| 262 | 274 | case ZigTypeIdErrorSet: |
| 263 | 275 | case ZigTypeIdFn: |
| 264 | case ZigTypeIdNamespace: | |
| 265 | 276 | case ZigTypeIdBoundFn: |
| 266 | 277 | case ZigTypeIdArgTuple: |
| 267 | 278 | case ZigTypeIdPromise: |
| ... | ... | @@ -323,7 +334,6 @@ bool type_is_resolved(ZigType *type_entry, ResolveStatus status) { |
| 323 | 334 | case ZigTypeIdErrorUnion: |
| 324 | 335 | case ZigTypeIdErrorSet: |
| 325 | 336 | case ZigTypeIdFn: |
| 326 | case ZigTypeIdNamespace: | |
| 327 | 337 | case ZigTypeIdBoundFn: |
| 328 | 338 | case ZigTypeIdArgTuple: |
| 329 | 339 | case ZigTypeIdPromise: |
| ... | ... | @@ -616,7 +626,7 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type) { |
| 616 | 626 | if (child_type->zero_bits) { |
| 617 | 627 | entry->type_ref = LLVMInt1Type(); |
| 618 | 628 | entry->di_type = g->builtin_types.entry_bool->di_type; |
| 619 | } else if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { | |
| 629 | } else if (type_is_non_optional_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { | |
| 620 | 630 | assert(child_type->di_type); |
| 621 | 631 | // this is an optimization but also is necessary for calling C |
| 622 | 632 | // functions where all pointers are maybe pointers |
| ... | ... | @@ -1005,21 +1015,22 @@ ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type) { |
| 1005 | 1015 | return entry; |
| 1006 | 1016 | } |
| 1007 | 1017 | |
| 1008 | ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name) { | |
| 1018 | ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name) { | |
| 1009 | 1019 | ZigType *entry = new_type_table_entry(ZigTypeIdOpaque); |
| 1010 | 1020 | |
| 1011 | buf_init_from_str(&entry->name, name); | |
| 1021 | buf_init_from_str(&entry->name, full_name); | |
| 1012 | 1022 | |
| 1013 | ImportTableEntry *import = scope ? get_scope_import(scope) : nullptr; | |
| 1023 | ZigType *import = scope ? get_scope_import(scope) : nullptr; | |
| 1014 | 1024 | unsigned line = source_node ? (unsigned)(source_node->line + 1) : 0; |
| 1015 | 1025 | |
| 1016 | 1026 | entry->type_ref = LLVMInt8Type(); |
| 1017 | 1027 | entry->di_type = ZigLLVMCreateDebugForwardDeclType(g->dbuilder, |
| 1018 | ZigLLVMTag_DW_structure_type(), buf_ptr(&entry->name), | |
| 1019 | import ? ZigLLVMFileToScope(import->di_file) : nullptr, | |
| 1020 | import ? import->di_file : nullptr, | |
| 1028 | ZigLLVMTag_DW_structure_type(), full_name, | |
| 1029 | import ? ZigLLVMFileToScope(import->data.structure.root_struct->di_file) : nullptr, | |
| 1030 | import ? import->data.structure.root_struct->di_file : nullptr, | |
| 1021 | 1031 | line); |
| 1022 | 1032 | entry->zero_bits = false; |
| 1033 | entry->data.opaque.bare_name = bare_name; | |
| 1023 | 1034 | |
| 1024 | 1035 | return entry; |
| 1025 | 1036 | } |
| ... | ... | @@ -1288,11 +1299,32 @@ static ZigTypeId container_to_type(ContainerKind kind) { |
| 1288 | 1299 | zig_unreachable(); |
| 1289 | 1300 | } |
| 1290 | 1301 | |
| 1302 | // This is like get_partial_container_type except it's for the implicit root struct of files. | |
| 1303 | ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf *bare_name, | |
| 1304 | RootStruct *root_struct) | |
| 1305 | { | |
| 1306 | ZigType *entry = new_type_table_entry(ZigTypeIdStruct); | |
| 1307 | entry->data.structure.decls_scope = create_decls_scope(g, nullptr, nullptr, entry, entry, bare_name); | |
| 1308 | entry->data.structure.root_struct = root_struct; | |
| 1309 | entry->data.structure.layout = ContainerLayoutAuto; | |
| 1310 | entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), full_name); | |
| 1311 | ||
| 1312 | size_t line = 0; // root therefore first line | |
| 1313 | unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); | |
| 1314 | ||
| 1315 | entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, | |
| 1316 | dwarf_kind, full_name, | |
| 1317 | ZigLLVMFileToScope(root_struct->di_file), root_struct->di_file, (unsigned)(line + 1)); | |
| 1318 | ||
| 1319 | buf_init_from_str(&entry->name, full_name); | |
| 1320 | return entry; | |
| 1321 | } | |
| 1322 | ||
| 1291 | 1323 | ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, |
| 1292 | AstNode *decl_node, const char *name, ContainerLayout layout) | |
| 1324 | AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout) | |
| 1293 | 1325 | { |
| 1294 | 1326 | ZigTypeId type_id = container_to_type(kind); |
| 1295 | ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope); | |
| 1327 | ZigType *entry = new_container_type_entry(g, type_id, decl_node, scope, bare_name); | |
| 1296 | 1328 | |
| 1297 | 1329 | switch (kind) { |
| 1298 | 1330 | case ContainerKindStruct: |
| ... | ... | @@ -1312,13 +1344,14 @@ ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind |
| 1312 | 1344 | size_t line = decl_node ? decl_node->line : 0; |
| 1313 | 1345 | unsigned dwarf_kind = ZigLLVMTag_DW_structure_type(); |
| 1314 | 1346 | |
| 1315 | ImportTableEntry *import = get_scope_import(scope); | |
| 1316 | entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), name); | |
| 1347 | ZigType *import = get_scope_import(scope); | |
| 1348 | entry->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), full_name); | |
| 1317 | 1349 | entry->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, |
| 1318 | dwarf_kind, name, | |
| 1319 | ZigLLVMFileToScope(import->di_file), import->di_file, (unsigned)(line + 1)); | |
| 1350 | dwarf_kind, full_name, | |
| 1351 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), | |
| 1352 | import->data.structure.root_struct->di_file, (unsigned)(line + 1)); | |
| 1320 | 1353 | |
| 1321 | buf_init_from_str(&entry->name, name); | |
| 1354 | buf_init_from_str(&entry->name, full_name); | |
| 1322 | 1355 | |
| 1323 | 1356 | return entry; |
| 1324 | 1357 | } |
| ... | ... | @@ -1459,7 +1492,6 @@ static Error emit_error_unless_type_allowed_in_packed_struct(CodeGen *g, ZigType |
| 1459 | 1492 | case ZigTypeIdNull: |
| 1460 | 1493 | case ZigTypeIdErrorUnion: |
| 1461 | 1494 | case ZigTypeIdErrorSet: |
| 1462 | case ZigTypeIdNamespace: | |
| 1463 | 1495 | case ZigTypeIdBoundFn: |
| 1464 | 1496 | case ZigTypeIdArgTuple: |
| 1465 | 1497 | case ZigTypeIdOpaque: |
| ... | ... | @@ -1547,7 +1579,6 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry) { |
| 1547 | 1579 | case ZigTypeIdNull: |
| 1548 | 1580 | case ZigTypeIdErrorUnion: |
| 1549 | 1581 | case ZigTypeIdErrorSet: |
| 1550 | case ZigTypeIdNamespace: | |
| 1551 | 1582 | case ZigTypeIdBoundFn: |
| 1552 | 1583 | case ZigTypeIdArgTuple: |
| 1553 | 1584 | case ZigTypeIdPromise: |
| ... | ... | @@ -1706,7 +1737,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc |
| 1706 | 1737 | return g->builtin_types.entry_invalid; |
| 1707 | 1738 | case ZigTypeIdComptimeFloat: |
| 1708 | 1739 | case ZigTypeIdComptimeInt: |
| 1709 | case ZigTypeIdNamespace: | |
| 1710 | 1740 | case ZigTypeIdBoundFn: |
| 1711 | 1741 | case ZigTypeIdMetaType: |
| 1712 | 1742 | case ZigTypeIdVoid: |
| ... | ... | @@ -1801,7 +1831,6 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc |
| 1801 | 1831 | |
| 1802 | 1832 | case ZigTypeIdComptimeFloat: |
| 1803 | 1833 | case ZigTypeIdComptimeInt: |
| 1804 | case ZigTypeIdNamespace: | |
| 1805 | 1834 | case ZigTypeIdBoundFn: |
| 1806 | 1835 | case ZigTypeIdMetaType: |
| 1807 | 1836 | case ZigTypeIdUnreachable: |
| ... | ... | @@ -1880,7 +1909,9 @@ static Error resolve_enum_type(CodeGen *g, ZigType *enum_type) { |
| 1880 | 1909 | if (!enum_type->data.enumeration.reported_infinite_err) { |
| 1881 | 1910 | enum_type->data.enumeration.is_invalid = true; |
| 1882 | 1911 | enum_type->data.enumeration.reported_infinite_err = true; |
| 1883 | add_node_error(g, decl_node, buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name))); | |
| 1912 | ErrorMsg *msg = add_node_error(g, decl_node, | |
| 1913 | buf_sprintf("enum '%s' contains itself", buf_ptr(&enum_type->name))); | |
| 1914 | emit_error_notes_for_ref_stack(g, msg); | |
| 1884 | 1915 | } |
| 1885 | 1916 | return ErrorSemanticAnalyzeFail; |
| 1886 | 1917 | } |
| ... | ... | @@ -1895,7 +1926,7 @@ static Error resolve_enum_type(CodeGen *g, ZigType *enum_type) { |
| 1895 | 1926 | ZigLLVMDIEnumerator **di_enumerators = allocate<ZigLLVMDIEnumerator*>(field_count); |
| 1896 | 1927 | |
| 1897 | 1928 | Scope *scope = &enum_type->data.enumeration.decls_scope->base; |
| 1898 | ImportTableEntry *import = get_scope_import(scope); | |
| 1929 | ZigType *import = get_scope_import(scope); | |
| 1899 | 1930 | |
| 1900 | 1931 | // set temporary flag |
| 1901 | 1932 | enum_type->data.enumeration.embedded_in_current = true; |
| ... | ... | @@ -1924,9 +1955,9 @@ static Error resolve_enum_type(CodeGen *g, ZigType *enum_type) { |
| 1924 | 1955 | ZigLLVMDIType **di_root_members = nullptr; |
| 1925 | 1956 | size_t debug_member_count = 0; |
| 1926 | 1957 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, |
| 1927 | ZigLLVMFileToScope(import->di_file), | |
| 1958 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), | |
| 1928 | 1959 | buf_ptr(&enum_type->name), |
| 1929 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1960 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 1930 | 1961 | debug_size_in_bits, |
| 1931 | 1962 | debug_align_in_bits, |
| 1932 | 1963 | 0, nullptr, di_root_members, (int)debug_member_count, 0, nullptr, ""); |
| ... | ... | @@ -1942,8 +1973,8 @@ static Error resolve_enum_type(CodeGen *g, ZigType *enum_type) { |
| 1942 | 1973 | uint64_t tag_debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, tag_int_type->type_ref); |
| 1943 | 1974 | uint64_t tag_debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_int_type->type_ref); |
| 1944 | 1975 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, |
| 1945 | ZigLLVMFileToScope(import->di_file), buf_ptr(&enum_type->name), | |
| 1946 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 1976 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&enum_type->name), | |
| 1977 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 1947 | 1978 | tag_debug_size_in_bits, |
| 1948 | 1979 | tag_debug_align_in_bits, |
| 1949 | 1980 | di_enumerators, field_count, |
| ... | ... | @@ -2051,8 +2082,9 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2051 | 2082 | if (struct_type->data.structure.resolve_loop_flag) { |
| 2052 | 2083 | if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { |
| 2053 | 2084 | struct_type->data.structure.resolve_status = ResolveStatusInvalid; |
| 2054 | add_node_error(g, decl_node, | |
| 2085 | ErrorMsg *msg = add_node_error(g, decl_node, | |
| 2055 | 2086 | buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name))); |
| 2087 | emit_error_notes_for_ref_stack(g, msg); | |
| 2056 | 2088 | } |
| 2057 | 2089 | return ErrorSemanticAnalyzeFail; |
| 2058 | 2090 | } |
| ... | ... | @@ -2159,15 +2191,15 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2159 | 2191 | if (struct_type->zero_bits) { |
| 2160 | 2192 | struct_type->type_ref = LLVMVoidType(); |
| 2161 | 2193 | |
| 2162 | ImportTableEntry *import = get_scope_import(scope); | |
| 2194 | ZigType *import = get_scope_import(scope); | |
| 2163 | 2195 | uint64_t debug_size_in_bits = 0; |
| 2164 | 2196 | uint64_t debug_align_in_bits = 0; |
| 2165 | 2197 | ZigLLVMDIType **di_element_types = nullptr; |
| 2166 | 2198 | size_t debug_field_count = 0; |
| 2167 | 2199 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, |
| 2168 | ZigLLVMFileToScope(import->di_file), | |
| 2200 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), | |
| 2169 | 2201 | buf_ptr(&struct_type->name), |
| 2170 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2202 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2171 | 2203 | debug_size_in_bits, |
| 2172 | 2204 | debug_align_in_bits, |
| 2173 | 2205 | 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); |
| ... | ... | @@ -2191,7 +2223,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2191 | 2223 | |
| 2192 | 2224 | ZigLLVMDIType **di_element_types = allocate<ZigLLVMDIType*>(debug_field_count); |
| 2193 | 2225 | |
| 2194 | ImportTableEntry *import = get_scope_import(scope); | |
| 2226 | ZigType *import = get_scope_import(scope); | |
| 2195 | 2227 | size_t debug_field_index = 0; |
| 2196 | 2228 | for (size_t i = 0; i < field_count; i += 1) { |
| 2197 | 2229 | AstNode *field_node = decl_node->data.container_decl.fields.at(i); |
| ... | ... | @@ -2234,7 +2266,7 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2234 | 2266 | } |
| 2235 | 2267 | di_element_types[debug_field_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, |
| 2236 | 2268 | ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), |
| 2237 | import->di_file, (unsigned)(field_node->line + 1), | |
| 2269 | import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1), | |
| 2238 | 2270 | debug_size_in_bits, |
| 2239 | 2271 | debug_align_in_bits, |
| 2240 | 2272 | debug_offset_in_bits, |
| ... | ... | @@ -2247,9 +2279,9 @@ static Error resolve_struct_type(CodeGen *g, ZigType *struct_type) { |
| 2247 | 2279 | uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref); |
| 2248 | 2280 | uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, struct_type->type_ref); |
| 2249 | 2281 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, |
| 2250 | ZigLLVMFileToScope(import->di_file), | |
| 2282 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), | |
| 2251 | 2283 | buf_ptr(&struct_type->name), |
| 2252 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2284 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2253 | 2285 | debug_size_in_bits, |
| 2254 | 2286 | debug_align_in_bits, |
| 2255 | 2287 | 0, nullptr, di_element_types, (int)debug_field_count, 0, nullptr, ""); |
| ... | ... | @@ -2276,7 +2308,9 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2276 | 2308 | if (!union_type->data.unionation.reported_infinite_err) { |
| 2277 | 2309 | union_type->data.unionation.reported_infinite_err = true; |
| 2278 | 2310 | union_type->data.unionation.is_invalid = true; |
| 2279 | add_node_error(g, decl_node, buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name))); | |
| 2311 | ErrorMsg *msg = add_node_error(g, decl_node, | |
| 2312 | buf_sprintf("union '%s' contains itself", buf_ptr(&union_type->name))); | |
| 2313 | emit_error_notes_for_ref_stack(g, msg); | |
| 2280 | 2314 | } |
| 2281 | 2315 | return ErrorSemanticAnalyzeFail; |
| 2282 | 2316 | } |
| ... | ... | @@ -2298,7 +2332,7 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2298 | 2332 | uint64_t biggest_size_in_bits = 0; |
| 2299 | 2333 | |
| 2300 | 2334 | Scope *scope = &union_type->data.unionation.decls_scope->base; |
| 2301 | ImportTableEntry *import = get_scope_import(scope); | |
| 2335 | ZigType *import = get_scope_import(scope); | |
| 2302 | 2336 | |
| 2303 | 2337 | // set temporary flag |
| 2304 | 2338 | union_type->data.unionation.embedded_in_current = true; |
| ... | ... | @@ -2325,7 +2359,7 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2325 | 2359 | |
| 2326 | 2360 | union_inner_di_types[union_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, |
| 2327 | 2361 | ZigLLVMTypeToScope(union_type->di_type), buf_ptr(union_field->enum_field->name), |
| 2328 | import->di_file, (unsigned)(field_node->line + 1), | |
| 2362 | import->data.structure.root_struct->di_file, (unsigned)(field_node->line + 1), | |
| 2329 | 2363 | store_size_in_bits, |
| 2330 | 2364 | abi_align_in_bits, |
| 2331 | 2365 | 0, |
| ... | ... | @@ -2358,9 +2392,9 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2358 | 2392 | ZigLLVMDIType **di_root_members = nullptr; |
| 2359 | 2393 | size_t debug_member_count = 0; |
| 2360 | 2394 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, |
| 2361 | ZigLLVMFileToScope(import->di_file), | |
| 2395 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), | |
| 2362 | 2396 | buf_ptr(&union_type->name), |
| 2363 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2397 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2364 | 2398 | debug_size_in_bits, |
| 2365 | 2399 | debug_align_in_bits, |
| 2366 | 2400 | 0, di_root_members, (int)debug_member_count, 0, ""); |
| ... | ... | @@ -2396,8 +2430,8 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2396 | 2430 | |
| 2397 | 2431 | // create debug type for union |
| 2398 | 2432 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, |
| 2399 | ZigLLVMFileToScope(import->di_file), buf_ptr(&union_type->name), | |
| 2400 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2433 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&union_type->name), | |
| 2434 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2401 | 2435 | biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, |
| 2402 | 2436 | gen_field_count, 0, ""); |
| 2403 | 2437 | |
| ... | ... | @@ -2452,7 +2486,7 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2452 | 2486 | // create debug type for union |
| 2453 | 2487 | ZigLLVMDIType *union_di_type = ZigLLVMCreateDebugUnionType(g->dbuilder, |
| 2454 | 2488 | ZigLLVMTypeToScope(union_type->di_type), "AnonUnion", |
| 2455 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2489 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2456 | 2490 | biggest_size_in_bits, biggest_align_in_bits, 0, union_inner_di_types, |
| 2457 | 2491 | gen_field_count, 0, ""); |
| 2458 | 2492 | |
| ... | ... | @@ -2463,7 +2497,7 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2463 | 2497 | |
| 2464 | 2498 | ZigLLVMDIType *union_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, |
| 2465 | 2499 | ZigLLVMTypeToScope(union_type->di_type), "payload", |
| 2466 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2500 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2467 | 2501 | biggest_size_in_bits, |
| 2468 | 2502 | biggest_align_in_bits, |
| 2469 | 2503 | union_offset_in_bits, |
| ... | ... | @@ -2474,7 +2508,7 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2474 | 2508 | |
| 2475 | 2509 | ZigLLVMDIType *tag_member_di_type = ZigLLVMCreateDebugMemberType(g->dbuilder, |
| 2476 | 2510 | ZigLLVMTypeToScope(union_type->di_type), "tag", |
| 2477 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2511 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2478 | 2512 | tag_debug_size_in_bits, |
| 2479 | 2513 | tag_debug_align_in_bits, |
| 2480 | 2514 | tag_offset_in_bits, |
| ... | ... | @@ -2487,9 +2521,9 @@ static Error resolve_union_type(CodeGen *g, ZigType *union_type) { |
| 2487 | 2521 | uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, union_type->type_ref); |
| 2488 | 2522 | uint64_t debug_align_in_bits = 8*LLVMABISizeOfType(g->target_data_ref, union_type->type_ref); |
| 2489 | 2523 | ZigLLVMDIType *replacement_di_type = ZigLLVMCreateDebugStructType(g->dbuilder, |
| 2490 | ZigLLVMFileToScope(import->di_file), | |
| 2524 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), | |
| 2491 | 2525 | buf_ptr(&union_type->name), |
| 2492 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 2526 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 2493 | 2527 | debug_size_in_bits, |
| 2494 | 2528 | debug_align_in_bits, |
| 2495 | 2529 | 0, nullptr, di_root_members, 2, 0, nullptr, ""); |
| ... | ... | @@ -2507,8 +2541,9 @@ static Error resolve_enum_zero_bits(CodeGen *g, ZigType *enum_type) { |
| 2507 | 2541 | return ErrorNone; |
| 2508 | 2542 | |
| 2509 | 2543 | if (enum_type->data.enumeration.zero_bits_loop_flag) { |
| 2510 | add_node_error(g, enum_type->data.enumeration.decl_node, | |
| 2544 | ErrorMsg *msg = add_node_error(g, enum_type->data.enumeration.decl_node, | |
| 2511 | 2545 | buf_sprintf("'%s' depends on itself", buf_ptr(&enum_type->name))); |
| 2546 | emit_error_notes_for_ref_stack(g, msg); | |
| 2512 | 2547 | enum_type->data.enumeration.is_invalid = true; |
| 2513 | 2548 | return ErrorSemanticAnalyzeFail; |
| 2514 | 2549 | } |
| ... | ... | @@ -2780,8 +2815,9 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { |
| 2780 | 2815 | if (struct_type->data.structure.resolve_loop_flag) { |
| 2781 | 2816 | if (struct_type->data.structure.resolve_status != ResolveStatusInvalid) { |
| 2782 | 2817 | struct_type->data.structure.resolve_status = ResolveStatusInvalid; |
| 2783 | add_node_error(g, decl_node, | |
| 2818 | ErrorMsg *msg = add_node_error(g, decl_node, | |
| 2784 | 2819 | buf_sprintf("struct '%s' contains itself", buf_ptr(&struct_type->name))); |
| 2820 | emit_error_notes_for_ref_stack(g, msg); | |
| 2785 | 2821 | } |
| 2786 | 2822 | return ErrorSemanticAnalyzeFail; |
| 2787 | 2823 | } |
| ... | ... | @@ -3185,15 +3221,15 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { |
| 3185 | 3221 | } |
| 3186 | 3222 | |
| 3187 | 3223 | if (create_enum_type) { |
| 3188 | ImportTableEntry *import = get_scope_import(scope); | |
| 3224 | ZigType *import = get_scope_import(scope); | |
| 3189 | 3225 | uint64_t tag_debug_size_in_bits = tag_type->zero_bits ? 0 : |
| 3190 | 3226 | 8*LLVMStoreSizeOfType(g->target_data_ref, tag_type->type_ref); |
| 3191 | 3227 | uint64_t tag_debug_align_in_bits = tag_type->zero_bits ? 0 : |
| 3192 | 3228 | 8*LLVMABIAlignmentOfType(g->target_data_ref, tag_type->type_ref); |
| 3193 | 3229 | // TODO get a more accurate debug scope |
| 3194 | 3230 | ZigLLVMDIType *tag_di_type = ZigLLVMCreateDebugEnumerationType(g->dbuilder, |
| 3195 | ZigLLVMFileToScope(import->di_file), buf_ptr(&tag_type->name), | |
| 3196 | import->di_file, (unsigned)(decl_node->line + 1), | |
| 3231 | ZigLLVMFileToScope(import->data.structure.root_struct->di_file), buf_ptr(&tag_type->name), | |
| 3232 | import->data.structure.root_struct->di_file, (unsigned)(decl_node->line + 1), | |
| 3197 | 3233 | tag_debug_size_in_bits, tag_debug_align_in_bits, di_enumerators, field_count, |
| 3198 | 3234 | tag_type->di_type, ""); |
| 3199 | 3235 | tag_type->di_type = tag_di_type; |
| ... | ... | @@ -3210,27 +3246,16 @@ static Error resolve_union_zero_bits(CodeGen *g, ZigType *union_type) { |
| 3210 | 3246 | return ErrorNone; |
| 3211 | 3247 | } |
| 3212 | 3248 | |
| 3213 | static void get_fully_qualified_decl_name_internal(Buf *buf, Scope *scope, uint8_t sep) { | |
| 3214 | if (!scope) | |
| 3215 | return; | |
| 3216 | ||
| 3217 | if (scope->id == ScopeIdDecls) { | |
| 3218 | get_fully_qualified_decl_name_internal(buf, scope->parent, sep); | |
| 3249 | static void get_fully_qualified_decl_name(Buf *buf, Tld *tld) { | |
| 3250 | buf_resize(buf, 0); | |
| 3219 | 3251 | |
| 3220 | ScopeDecls *scope_decls = (ScopeDecls *)scope; | |
| 3221 | if (scope_decls->container_type) { | |
| 3222 | buf_append_buf(buf, &scope_decls->container_type->name); | |
| 3223 | buf_append_char(buf, sep); | |
| 3224 | } | |
| 3225 | return; | |
| 3252 | Scope *scope = tld->parent_scope; | |
| 3253 | while (scope->id != ScopeIdDecls) { | |
| 3254 | scope = scope->parent; | |
| 3226 | 3255 | } |
| 3227 | ||
| 3228 | get_fully_qualified_decl_name_internal(buf, scope->parent, sep); | |
| 3229 | } | |
| 3230 | ||
| 3231 | static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) { | |
| 3232 | buf_resize(buf, 0); | |
| 3233 | get_fully_qualified_decl_name_internal(buf, tld->parent_scope, sep); | |
| 3256 | ScopeDecls *decls_scope = reinterpret_cast<ScopeDecls *>(scope); | |
| 3257 | buf_append_buf(buf, &decls_scope->container_type->name); | |
| 3258 | if (buf_len(buf) != 0) buf_append_char(buf, NAMESPACE_SEP_CHAR); | |
| 3234 | 3259 | buf_append_buf(buf, tld->name); |
| 3235 | 3260 | } |
| 3236 | 3261 | |
| ... | ... | @@ -3265,7 +3290,7 @@ static bool scope_is_root_decls(Scope *scope) { |
| 3265 | 3290 | while (scope) { |
| 3266 | 3291 | if (scope->id == ScopeIdDecls) { |
| 3267 | 3292 | ScopeDecls *scope_decls = (ScopeDecls *)scope; |
| 3268 | return (scope_decls->container_type == nullptr); | |
| 3293 | return is_top_level_struct(scope_decls->container_type); | |
| 3269 | 3294 | } |
| 3270 | 3295 | scope = scope->parent; |
| 3271 | 3296 | } |
| ... | ... | @@ -3281,7 +3306,7 @@ void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn) { |
| 3281 | 3306 | AstNode *fake_decl = allocate<AstNode>(1); |
| 3282 | 3307 | *fake_decl = *panic_fn->proto_node; |
| 3283 | 3308 | fake_decl->type = NodeTypeSymbol; |
| 3284 | fake_decl->data.symbol_expr.symbol = &panic_fn->symbol_name; | |
| 3309 | fake_decl->data.symbol_expr.symbol = tld_fn->base.name; | |
| 3285 | 3310 | |
| 3286 | 3311 | // call this for the side effects of casting to panic_fn_type |
| 3287 | 3312 | analyze_const_value(g, tld_fn->base.parent_scope, fake_decl, panic_fn_type, nullptr); |
| ... | ... | @@ -3326,7 +3351,7 @@ void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLi |
| 3326 | 3351 | } |
| 3327 | 3352 | |
| 3328 | 3353 | static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 3329 | ImportTableEntry *import = tld_fn->base.import; | |
| 3354 | ZigType *import = tld_fn->base.import; | |
| 3330 | 3355 | AstNode *source_node = tld_fn->base.source_node; |
| 3331 | 3356 | if (source_node->type == NodeTypeFnProto) { |
| 3332 | 3357 | AstNodeFnProto *fn_proto = &source_node->data.fn_proto; |
| ... | ... | @@ -3334,16 +3359,21 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 3334 | 3359 | AstNode *fn_def_node = fn_proto->fn_def_node; |
| 3335 | 3360 | |
| 3336 | 3361 | ZigFn *fn_table_entry = create_fn(g, source_node); |
| 3337 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_'); | |
| 3362 | tld_fn->fn_entry = fn_table_entry; | |
| 3363 | ||
| 3364 | bool is_extern = (fn_table_entry->body_node == nullptr); | |
| 3365 | if (fn_proto->is_export || is_extern) { | |
| 3366 | buf_init_from_buf(&fn_table_entry->symbol_name, tld_fn->base.name); | |
| 3367 | } else { | |
| 3368 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base); | |
| 3369 | } | |
| 3338 | 3370 | |
| 3339 | 3371 | if (fn_proto->is_export) { |
| 3340 | 3372 | bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC); |
| 3341 | 3373 | add_fn_export(g, fn_table_entry, &fn_table_entry->symbol_name, GlobalLinkageIdStrong, ccc); |
| 3342 | 3374 | } |
| 3343 | 3375 | |
| 3344 | tld_fn->fn_entry = fn_table_entry; | |
| 3345 | ||
| 3346 | if (fn_table_entry->body_node) { | |
| 3376 | if (!is_extern) { | |
| 3347 | 3377 | fn_table_entry->fndef_scope = create_fndef_scope(g, |
| 3348 | 3378 | fn_table_entry->body_node, tld_fn->base.parent_scope, fn_table_entry); |
| 3349 | 3379 | |
| ... | ... | @@ -3382,12 +3412,12 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 3382 | 3412 | } |
| 3383 | 3413 | |
| 3384 | 3414 | if (scope_is_root_decls(tld_fn->base.parent_scope) && |
| 3385 | (import == g->root_import || import->package == g->panic_package)) | |
| 3415 | (import == g->root_import || import->data.structure.root_struct->package == g->panic_package)) | |
| 3386 | 3416 | { |
| 3387 | if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) { | |
| 3417 | if (g->have_pub_main && buf_eql_str(tld_fn->base.name, "main")) { | |
| 3388 | 3418 | g->main_fn = fn_table_entry; |
| 3389 | } else if ((import->package == g->panic_package || g->have_pub_panic) && | |
| 3390 | buf_eql_str(&fn_table_entry->symbol_name, "panic")) | |
| 3419 | } else if ((import->data.structure.root_struct->package == g->panic_package || g->have_pub_panic) && | |
| 3420 | buf_eql_str(tld_fn->base.name, "panic")) | |
| 3391 | 3421 | { |
| 3392 | 3422 | g->panic_fn = fn_table_entry; |
| 3393 | 3423 | g->panic_tld_fn = tld_fn; |
| ... | ... | @@ -3396,7 +3426,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { |
| 3396 | 3426 | } else if (source_node->type == NodeTypeTestDecl) { |
| 3397 | 3427 | ZigFn *fn_table_entry = create_fn_raw(g, FnInlineAuto); |
| 3398 | 3428 | |
| 3399 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_'); | |
| 3429 | get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base); | |
| 3400 | 3430 | |
| 3401 | 3431 | tld_fn->fn_entry = fn_table_entry; |
| 3402 | 3432 | |
| ... | ... | @@ -3473,8 +3503,8 @@ static void preview_test_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_scope |
| 3473 | 3503 | if (!g->is_test_build) |
| 3474 | 3504 | return; |
| 3475 | 3505 | |
| 3476 | ImportTableEntry *import = get_scope_import(&decls_scope->base); | |
| 3477 | if (import->package != g->root_package) | |
| 3506 | ZigType *import = get_scope_import(&decls_scope->base); | |
| 3507 | if (import->data.structure.root_struct->package != g->root_package) | |
| 3478 | 3508 | return; |
| 3479 | 3509 | |
| 3480 | 3510 | Buf *decl_name_buf = node->data.test_decl.name; |
| ... | ... | @@ -3511,8 +3541,8 @@ void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source |
| 3511 | 3541 | } |
| 3512 | 3542 | |
| 3513 | 3543 | void update_compile_var(CodeGen *g, Buf *name, ConstExprValue *value) { |
| 3514 | Tld *tld = g->compile_var_import->decls_scope->decl_table.get(name); | |
| 3515 | resolve_top_level_decl(g, tld, false, tld->source_node); | |
| 3544 | Tld *tld = get_container_scope(g->compile_var_import)->decl_table.get(name); | |
| 3545 | resolve_top_level_decl(g, tld, tld->source_node); | |
| 3516 | 3546 | assert(tld->id == TldIdVar); |
| 3517 | 3547 | TldVar *tld_var = (TldVar *)tld; |
| 3518 | 3548 | tld_var->var->const_value = value; |
| ... | ... | @@ -3561,8 +3591,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { |
| 3561 | 3591 | case NodeTypeUse: |
| 3562 | 3592 | { |
| 3563 | 3593 | g->use_queue.append(node); |
| 3564 | ImportTableEntry *import = get_scope_import(&decls_scope->base); | |
| 3565 | import->use_decls.append(node); | |
| 3594 | decls_scope->use_decls.append(node); | |
| 3566 | 3595 | break; |
| 3567 | 3596 | } |
| 3568 | 3597 | case NodeTypeTestDecl: |
| ... | ... | @@ -3654,7 +3683,6 @@ ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry |
| 3654 | 3683 | return g->builtin_types.entry_invalid; |
| 3655 | 3684 | case ZigTypeIdComptimeFloat: |
| 3656 | 3685 | case ZigTypeIdComptimeInt: |
| 3657 | case ZigTypeIdNamespace: | |
| 3658 | 3686 | case ZigTypeIdMetaType: |
| 3659 | 3687 | case ZigTypeIdVoid: |
| 3660 | 3688 | case ZigTypeIdBool: |
| ... | ... | @@ -3763,8 +3791,16 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { |
| 3763 | 3791 | |
| 3764 | 3792 | ZigType *explicit_type = nullptr; |
| 3765 | 3793 | if (var_decl->type) { |
| 3766 | ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type); | |
| 3767 | explicit_type = validate_var_type(g, var_decl->type, proposed_type); | |
| 3794 | if (tld_var->analyzing_type) { | |
| 3795 | ErrorMsg *msg = add_node_error(g, var_decl->type, | |
| 3796 | buf_sprintf("type of '%s' depends on itself", buf_ptr(tld_var->base.name))); | |
| 3797 | emit_error_notes_for_ref_stack(g, msg); | |
| 3798 | explicit_type = g->builtin_types.entry_invalid; | |
| 3799 | } else { | |
| 3800 | tld_var->analyzing_type = true; | |
| 3801 | ZigType *proposed_type = analyze_type_expr(g, tld_var->base.parent_scope, var_decl->type); | |
| 3802 | explicit_type = validate_var_type(g, var_decl->type, proposed_type); | |
| 3803 | } | |
| 3768 | 3804 | } |
| 3769 | 3805 | |
| 3770 | 3806 | assert(!is_export || !is_extern); |
| ... | ... | @@ -3847,17 +3883,12 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) { |
| 3847 | 3883 | g->global_vars.append(tld_var); |
| 3848 | 3884 | } |
| 3849 | 3885 | |
| 3850 | void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node) { | |
| 3886 | void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node) { | |
| 3851 | 3887 | if (tld->resolution != TldResolutionUnresolved) |
| 3852 | 3888 | return; |
| 3853 | 3889 | |
| 3854 | if (tld->dep_loop_flag) { | |
| 3855 | add_node_error(g, tld->source_node, buf_sprintf("'%s' depends on itself", buf_ptr(tld->name))); | |
| 3856 | tld->resolution = TldResolutionInvalid; | |
| 3857 | return; | |
| 3858 | } | |
| 3859 | ||
| 3860 | tld->dep_loop_flag = true; | |
| 3890 | assert(tld->resolution != TldResolutionResolving); | |
| 3891 | tld->resolution = TldResolutionResolving; | |
| 3861 | 3892 | g->tld_ref_source_node_stack.append(source_node); |
| 3862 | 3893 | |
| 3863 | 3894 | switch (tld->id) { |
| ... | ... | @@ -3888,27 +3919,31 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *so |
| 3888 | 3919 | } |
| 3889 | 3920 | |
| 3890 | 3921 | tld->resolution = TldResolutionOk; |
| 3891 | tld->dep_loop_flag = false; | |
| 3892 | 3922 | g->tld_ref_source_node_stack.pop(); |
| 3893 | 3923 | } |
| 3894 | 3924 | |
| 3895 | Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { | |
| 3896 | // we must resolve all the use decls | |
| 3897 | ImportTableEntry *import = get_scope_import(scope); | |
| 3898 | for (size_t i = 0; i < import->use_decls.length; i += 1) { | |
| 3899 | AstNode *use_decl_node = import->use_decls.at(i); | |
| 3925 | Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name) { | |
| 3926 | // resolve all the use decls | |
| 3927 | for (size_t i = 0; i < decls_scope->use_decls.length; i += 1) { | |
| 3928 | AstNode *use_decl_node = decls_scope->use_decls.at(i); | |
| 3900 | 3929 | if (use_decl_node->data.use.resolution == TldResolutionUnresolved) { |
| 3901 | 3930 | preview_use_decl(g, use_decl_node); |
| 3902 | 3931 | resolve_use_decl(g, use_decl_node); |
| 3903 | 3932 | } |
| 3904 | 3933 | } |
| 3905 | 3934 | |
| 3935 | auto entry = decls_scope->decl_table.maybe_get(name); | |
| 3936 | return (entry == nullptr) ? nullptr : entry->value; | |
| 3937 | } | |
| 3938 | ||
| 3939 | Tld *find_decl(CodeGen *g, Scope *scope, Buf *name) { | |
| 3906 | 3940 | while (scope) { |
| 3907 | 3941 | if (scope->id == ScopeIdDecls) { |
| 3908 | 3942 | ScopeDecls *decls_scope = (ScopeDecls *)scope; |
| 3909 | auto entry = decls_scope->decl_table.maybe_get(name); | |
| 3910 | if (entry) | |
| 3911 | return entry->value; | |
| 3943 | ||
| 3944 | Tld *result = find_container_decl(g, decls_scope, name); | |
| 3945 | if (result != nullptr) | |
| 3946 | return result; | |
| 3912 | 3947 | } |
| 3913 | 3948 | scope = scope->parent; |
| 3914 | 3949 | } |
| ... | ... | @@ -3959,6 +3994,12 @@ ZigFn *scope_fn_entry(Scope *scope) { |
| 3959 | 3994 | return nullptr; |
| 3960 | 3995 | } |
| 3961 | 3996 | |
| 3997 | ZigPackage *scope_package(Scope *scope) { | |
| 3998 | ZigType *import = get_scope_import(scope); | |
| 3999 | assert(is_top_level_struct(import)); | |
| 4000 | return import->data.structure.root_struct->package; | |
| 4001 | } | |
| 4002 | ||
| 3962 | 4003 | TypeEnumField *find_enum_type_field(ZigType *enum_type, Buf *name) { |
| 3963 | 4004 | assert(enum_type->id == ZigTypeIdEnum); |
| 3964 | 4005 | if (enum_type->data.enumeration.src_field_count == 0) |
| ... | ... | @@ -4039,7 +4080,6 @@ static bool is_container(ZigType *type_entry) { |
| 4039 | 4080 | case ZigTypeIdErrorUnion: |
| 4040 | 4081 | case ZigTypeIdErrorSet: |
| 4041 | 4082 | case ZigTypeIdFn: |
| 4042 | case ZigTypeIdNamespace: | |
| 4043 | 4083 | case ZigTypeIdBoundFn: |
| 4044 | 4084 | case ZigTypeIdArgTuple: |
| 4045 | 4085 | case ZigTypeIdOpaque: |
| ... | ... | @@ -4098,7 +4138,6 @@ void resolve_container_type(CodeGen *g, ZigType *type_entry) { |
| 4098 | 4138 | case ZigTypeIdErrorUnion: |
| 4099 | 4139 | case ZigTypeIdErrorSet: |
| 4100 | 4140 | case ZigTypeIdFn: |
| 4101 | case ZigTypeIdNamespace: | |
| 4102 | 4141 | case ZigTypeIdBoundFn: |
| 4103 | 4142 | case ZigTypeIdInvalid: |
| 4104 | 4143 | case ZigTypeIdArgTuple: |
| ... | ... | @@ -4131,10 +4170,10 @@ ZigType *get_codegen_ptr_type(ZigType *type) { |
| 4131 | 4170 | } |
| 4132 | 4171 | |
| 4133 | 4172 | bool type_is_nonnull_ptr(ZigType *type) { |
| 4134 | return type_is_codegen_pointer(type) && !ptr_allows_addr_zero(type); | |
| 4173 | return type_is_non_optional_pointer(type) && !ptr_allows_addr_zero(type); | |
| 4135 | 4174 | } |
| 4136 | 4175 | |
| 4137 | bool type_is_codegen_pointer(ZigType *type) { | |
| 4176 | bool type_is_non_optional_pointer(ZigType *type) { | |
| 4138 | 4177 | return get_codegen_ptr_type(type) == type; |
| 4139 | 4178 | } |
| 4140 | 4179 | |
| ... | ... | @@ -4342,9 +4381,9 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * |
| 4342 | 4381 | preview_use_decl(g, src_use_node); |
| 4343 | 4382 | } |
| 4344 | 4383 | |
| 4345 | ConstExprValue *use_target_value = src_use_node->data.use.value; | |
| 4384 | ConstExprValue *use_target_value = src_use_node->data.use.using_namespace_value; | |
| 4346 | 4385 | if (type_is_invalid(use_target_value->type)) { |
| 4347 | dst_use_node->owner->any_imports_failed = true; | |
| 4386 | get_container_scope(dst_use_node->owner)->any_imports_failed = true; | |
| 4348 | 4387 | return; |
| 4349 | 4388 | } |
| 4350 | 4389 | |
| ... | ... | @@ -4352,14 +4391,14 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * |
| 4352 | 4391 | |
| 4353 | 4392 | assert(use_target_value->special != ConstValSpecialRuntime); |
| 4354 | 4393 | |
| 4355 | ImportTableEntry *target_import = use_target_value->data.x_import; | |
| 4394 | ZigType *target_import = use_target_value->data.x_type; | |
| 4356 | 4395 | assert(target_import); |
| 4357 | 4396 | |
| 4358 | if (target_import->any_imports_failed) { | |
| 4359 | dst_use_node->owner->any_imports_failed = true; | |
| 4397 | if (get_container_scope(target_import)->any_imports_failed) { | |
| 4398 | get_container_scope(dst_use_node->owner)->any_imports_failed = true; | |
| 4360 | 4399 | } |
| 4361 | 4400 | |
| 4362 | auto it = target_import->decls_scope->decl_table.entry_iterator(); | |
| 4401 | auto it = get_container_scope(target_import)->decl_table.entry_iterator(); | |
| 4363 | 4402 | for (;;) { |
| 4364 | 4403 | auto *entry = it.next(); |
| 4365 | 4404 | if (!entry) |
| ... | ... | @@ -4374,7 +4413,7 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * |
| 4374 | 4413 | |
| 4375 | 4414 | Buf *target_tld_name = entry->key; |
| 4376 | 4415 | |
| 4377 | auto existing_entry = dst_use_node->owner->decls_scope->decl_table.put_unique(target_tld_name, target_tld); | |
| 4416 | auto existing_entry = get_container_scope(dst_use_node->owner)->decl_table.put_unique(target_tld_name, target_tld); | |
| 4378 | 4417 | if (existing_entry) { |
| 4379 | 4418 | Tld *existing_decl = existing_entry->value; |
| 4380 | 4419 | if (existing_decl != target_tld) { |
| ... | ... | @@ -4387,8 +4426,8 @@ static void add_symbols_from_import(CodeGen *g, AstNode *src_use_node, AstNode * |
| 4387 | 4426 | } |
| 4388 | 4427 | } |
| 4389 | 4428 | |
| 4390 | for (size_t i = 0; i < target_import->use_decls.length; i += 1) { | |
| 4391 | AstNode *use_decl_node = target_import->use_decls.at(i); | |
| 4429 | for (size_t i = 0; i < get_container_scope(target_import)->use_decls.length; i += 1) { | |
| 4430 | AstNode *use_decl_node = get_container_scope(target_import)->use_decls.at(i); | |
| 4392 | 4431 | if (use_decl_node->data.use.visib_mod != VisibModPrivate) |
| 4393 | 4432 | add_symbols_from_import(g, use_decl_node, dst_use_node); |
| 4394 | 4433 | } |
| ... | ... | @@ -4415,16 +4454,18 @@ void preview_use_decl(CodeGen *g, AstNode *node) { |
| 4415 | 4454 | } |
| 4416 | 4455 | |
| 4417 | 4456 | node->data.use.resolution = TldResolutionResolving; |
| 4418 | ConstExprValue *result = analyze_const_value(g, &node->owner->decls_scope->base, | |
| 4419 | node->data.use.expr, g->builtin_types.entry_namespace, nullptr); | |
| 4457 | ConstExprValue *result = analyze_const_value(g, &get_container_scope(node->owner)->base, | |
| 4458 | node->data.use.expr, g->builtin_types.entry_type, nullptr); | |
| 4420 | 4459 | |
| 4421 | 4460 | if (type_is_invalid(result->type)) |
| 4422 | node->owner->any_imports_failed = true; | |
| 4461 | get_container_scope(node->owner)->any_imports_failed = true; | |
| 4423 | 4462 | |
| 4424 | node->data.use.value = result; | |
| 4463 | node->data.use.using_namespace_value = result; | |
| 4425 | 4464 | } |
| 4426 | 4465 | |
| 4427 | ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *resolved_path, Buf *source_code) { | |
| 4466 | ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *resolved_path, Buf *source_code, | |
| 4467 | SourceKind source_kind) | |
| 4468 | { | |
| 4428 | 4469 | if (g->verbose_tokenize) { |
| 4429 | 4470 | fprintf(stderr, "\nOriginal Source (%s):\n", buf_ptr(resolved_path)); |
| 4430 | 4471 | fprintf(stderr, "----------------\n"); |
| ... | ... | @@ -4452,87 +4493,102 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *r |
| 4452 | 4493 | fprintf(stderr, "------\n"); |
| 4453 | 4494 | } |
| 4454 | 4495 | |
| 4455 | ImportTableEntry *import_entry = allocate<ImportTableEntry>(1); | |
| 4456 | import_entry->package = package; | |
| 4457 | import_entry->source_code = source_code; | |
| 4458 | import_entry->line_offsets = tokenization.line_offsets; | |
| 4459 | import_entry->path = resolved_path; | |
| 4460 | ||
| 4461 | import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color); | |
| 4462 | assert(import_entry->root); | |
| 4463 | if (g->verbose_ast) { | |
| 4464 | ast_print(stderr, import_entry->root, 0); | |
| 4465 | } | |
| 4466 | ||
| 4467 | 4496 | Buf *src_dirname = buf_alloc(); |
| 4468 | 4497 | Buf *src_basename = buf_alloc(); |
| 4469 | 4498 | os_path_split(resolved_path, src_dirname, src_basename); |
| 4470 | 4499 | |
| 4471 | import_entry->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); | |
| 4472 | g->import_table.put(resolved_path, import_entry); | |
| 4473 | g->import_queue.append(import_entry); | |
| 4474 | ||
| 4475 | import_entry->decls_scope = create_decls_scope(g, import_entry->root, nullptr, nullptr, import_entry); | |
| 4500 | Buf noextname = BUF_INIT; | |
| 4501 | os_path_extname(resolved_path, &noextname, nullptr); | |
| 4476 | 4502 | |
| 4503 | Buf *pkg_root_src_dir = &package->root_src_dir; | |
| 4504 | Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1); | |
| 4477 | 4505 | |
| 4478 | assert(import_entry->root->type == NodeTypeContainerDecl); | |
| 4479 | for (size_t decl_i = 0; decl_i < import_entry->root->data.container_decl.decls.length; decl_i += 1) { | |
| 4480 | AstNode *top_level_decl = import_entry->root->data.container_decl.decls.at(decl_i); | |
| 4506 | assert(buf_starts_with_buf(resolved_path, &resolved_root_src_dir)); | |
| 4481 | 4507 | |
| 4482 | if (top_level_decl->type == NodeTypeFnDef) { | |
| 4483 | AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; | |
| 4484 | assert(proto_node->type == NodeTypeFnProto); | |
| 4485 | Buf *proto_name = proto_node->data.fn_proto.name; | |
| 4508 | Buf namespace_name = BUF_INIT; | |
| 4509 | buf_init_from_buf(&namespace_name, &package->pkg_path); | |
| 4510 | if (source_kind == SourceKindNonRoot) { | |
| 4511 | if (buf_len(&namespace_name) != 0) buf_append_char(&namespace_name, NAMESPACE_SEP_CHAR); | |
| 4512 | buf_append_mem(&namespace_name, buf_ptr(&noextname) + buf_len(&resolved_root_src_dir) + 1, | |
| 4513 | buf_len(&noextname) - (buf_len(&resolved_root_src_dir) + 1)); | |
| 4514 | buf_replace(&namespace_name, ZIG_OS_SEP_CHAR, NAMESPACE_SEP_CHAR); | |
| 4515 | } | |
| 4516 | Buf *bare_name = buf_alloc(); | |
| 4517 | os_path_extname(src_basename, bare_name, nullptr); | |
| 4486 | 4518 | |
| 4487 | bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub); | |
| 4488 | bool ok_cc = (proto_node->data.fn_proto.cc == CallingConventionUnspecified || | |
| 4489 | proto_node->data.fn_proto.cc == CallingConventionCold); | |
| 4519 | RootStruct *root_struct = allocate<RootStruct>(1); | |
| 4520 | root_struct->package = package; | |
| 4521 | root_struct->source_code = source_code; | |
| 4522 | root_struct->line_offsets = tokenization.line_offsets; | |
| 4523 | root_struct->path = resolved_path; | |
| 4524 | root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); | |
| 4525 | ZigType *import_entry = get_root_container_type(g, buf_ptr(&namespace_name), bare_name, root_struct); | |
| 4526 | if (source_kind == SourceKindRoot) { | |
| 4527 | assert(g->root_import == nullptr); | |
| 4528 | g->root_import = import_entry; | |
| 4529 | } | |
| 4530 | g->import_table.put(resolved_path, import_entry); | |
| 4490 | 4531 | |
| 4491 | if (is_pub && ok_cc) { | |
| 4492 | if (buf_eql_str(proto_name, "main")) { | |
| 4493 | g->have_pub_main = true; | |
| 4494 | g->subsystem = TargetSubsystemConsole; | |
| 4495 | } else if (buf_eql_str(proto_name, "panic")) { | |
| 4496 | g->have_pub_panic = true; | |
| 4532 | AstNode *root_node = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color); | |
| 4533 | assert(root_node != nullptr); | |
| 4534 | assert(root_node->type == NodeTypeContainerDecl); | |
| 4535 | import_entry->data.structure.decl_node = root_node; | |
| 4536 | import_entry->data.structure.decls_scope->base.source_node = root_node; | |
| 4537 | if (g->verbose_ast) { | |
| 4538 | ast_print(stderr, root_node, 0); | |
| 4539 | } | |
| 4540 | ||
| 4541 | if (source_kind == SourceKindRoot || package == g->panic_package) { | |
| 4542 | // Look for panic and main | |
| 4543 | for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { | |
| 4544 | AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); | |
| 4545 | ||
| 4546 | if (top_level_decl->type == NodeTypeFnDef) { | |
| 4547 | AstNode *proto_node = top_level_decl->data.fn_def.fn_proto; | |
| 4548 | assert(proto_node->type == NodeTypeFnProto); | |
| 4549 | Buf *proto_name = proto_node->data.fn_proto.name; | |
| 4550 | ||
| 4551 | bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub); | |
| 4552 | if (is_pub) { | |
| 4553 | if (buf_eql_str(proto_name, "main")) { | |
| 4554 | g->have_pub_main = true; | |
| 4555 | g->subsystem = TargetSubsystemConsole; | |
| 4556 | } else if (buf_eql_str(proto_name, "panic")) { | |
| 4557 | g->have_pub_panic = true; | |
| 4558 | } | |
| 4497 | 4559 | } |
| 4498 | 4560 | } |
| 4499 | 4561 | } |
| 4500 | 4562 | } |
| 4501 | 4563 | |
| 4502 | return import_entry; | |
| 4503 | } | |
| 4504 | ||
| 4505 | void scan_import(CodeGen *g, ImportTableEntry *import) { | |
| 4506 | if (!import->scanned) { | |
| 4507 | import->scanned = true; | |
| 4508 | scan_decls(g, import->decls_scope, import->root); | |
| 4564 | for (size_t decl_i = 0; decl_i < root_node->data.container_decl.decls.length; decl_i += 1) { | |
| 4565 | AstNode *top_level_decl = root_node->data.container_decl.decls.at(decl_i); | |
| 4566 | scan_decls(g, import_entry->data.structure.decls_scope, top_level_decl); | |
| 4509 | 4567 | } |
| 4510 | } | |
| 4511 | 4568 | |
| 4512 | void semantic_analyze(CodeGen *g) { | |
| 4513 | for (; g->import_queue_index < g->import_queue.length; g->import_queue_index += 1) { | |
| 4514 | ImportTableEntry *import = g->import_queue.at(g->import_queue_index); | |
| 4515 | scan_import(g, import); | |
| 4516 | } | |
| 4569 | TldContainer *tld_container = allocate<TldContainer>(1); | |
| 4570 | init_tld(&tld_container->base, TldIdContainer, &namespace_name, VisibModPub, root_node, nullptr); | |
| 4571 | tld_container->type_entry = import_entry; | |
| 4572 | tld_container->decls_scope = import_entry->data.structure.decls_scope; | |
| 4573 | g->resolve_queue.append(&tld_container->base); | |
| 4517 | 4574 | |
| 4518 | for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) { | |
| 4519 | AstNode *use_decl_node = g->use_queue.at(g->use_queue_index); | |
| 4520 | preview_use_decl(g, use_decl_node); | |
| 4521 | } | |
| 4522 | ||
| 4523 | for (size_t i = 0; i < g->use_queue.length; i += 1) { | |
| 4524 | AstNode *use_decl_node = g->use_queue.at(i); | |
| 4525 | resolve_use_decl(g, use_decl_node); | |
| 4526 | } | |
| 4575 | return import_entry; | |
| 4576 | } | |
| 4527 | 4577 | |
| 4578 | void semantic_analyze(CodeGen *g) { | |
| 4528 | 4579 | while (g->resolve_queue_index < g->resolve_queue.length || |
| 4529 | g->fn_defs_index < g->fn_defs.length) | |
| 4580 | g->fn_defs_index < g->fn_defs.length || | |
| 4581 | g->use_queue_index < g->use_queue.length) | |
| 4530 | 4582 | { |
| 4583 | for (; g->use_queue_index < g->use_queue.length; g->use_queue_index += 1) { | |
| 4584 | AstNode *use_decl_node = g->use_queue.at(g->use_queue_index); | |
| 4585 | preview_use_decl(g, use_decl_node); | |
| 4586 | resolve_use_decl(g, use_decl_node); | |
| 4587 | } | |
| 4531 | 4588 | for (; g->resolve_queue_index < g->resolve_queue.length; g->resolve_queue_index += 1) { |
| 4532 | 4589 | Tld *tld = g->resolve_queue.at(g->resolve_queue_index); |
| 4533 | bool pointer_only = false; | |
| 4534 | 4590 | AstNode *source_node = nullptr; |
| 4535 | resolve_top_level_decl(g, tld, pointer_only, source_node); | |
| 4591 | resolve_top_level_decl(g, tld, source_node); | |
| 4536 | 4592 | } |
| 4537 | 4593 | |
| 4538 | 4594 | for (; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) { |
| ... | ... | @@ -4613,7 +4669,6 @@ bool handle_is_ptr(ZigType *type_entry) { |
| 4613 | 4669 | case ZigTypeIdComptimeInt: |
| 4614 | 4670 | case ZigTypeIdUndefined: |
| 4615 | 4671 | case ZigTypeIdNull: |
| 4616 | case ZigTypeIdNamespace: | |
| 4617 | 4672 | case ZigTypeIdBoundFn: |
| 4618 | 4673 | case ZigTypeIdArgTuple: |
| 4619 | 4674 | case ZigTypeIdOpaque: |
| ... | ... | @@ -4637,7 +4692,7 @@ bool handle_is_ptr(ZigType *type_entry) { |
| 4637 | 4692 | return type_has_bits(type_entry->data.error_union.payload_type); |
| 4638 | 4693 | case ZigTypeIdOptional: |
| 4639 | 4694 | return type_has_bits(type_entry->data.maybe.child_type) && |
| 4640 | !type_is_codegen_pointer(type_entry->data.maybe.child_type) && | |
| 4695 | !type_is_non_optional_pointer(type_entry->data.maybe.child_type) && | |
| 4641 | 4696 | type_entry->data.maybe.child_type->id != ZigTypeIdErrorSet; |
| 4642 | 4697 | case ZigTypeIdUnion: |
| 4643 | 4698 | assert(type_entry->data.unionation.zero_bits_known); |
| ... | ... | @@ -4880,8 +4935,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { |
| 4880 | 4935 | return 3415065496; |
| 4881 | 4936 | case ZigTypeIdErrorSet: |
| 4882 | 4937 | return hash_const_val_error_set(const_val); |
| 4883 | case ZigTypeIdNamespace: | |
| 4884 | return hash_ptr(const_val->data.x_import); | |
| 4885 | 4938 | case ZigTypeIdVector: |
| 4886 | 4939 | // TODO better hashing algorithm |
| 4887 | 4940 | return 3647867726; |
| ... | ... | @@ -4943,7 +4996,6 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) { |
| 4943 | 4996 | case ZigTypeIdComptimeInt: |
| 4944 | 4997 | case ZigTypeIdUndefined: |
| 4945 | 4998 | case ZigTypeIdNull: |
| 4946 | case ZigTypeIdNamespace: | |
| 4947 | 4999 | case ZigTypeIdBoundFn: |
| 4948 | 5000 | case ZigTypeIdFn: |
| 4949 | 5001 | case ZigTypeIdOpaque: |
| ... | ... | @@ -5013,7 +5065,6 @@ static bool return_type_is_cacheable(ZigType *return_type) { |
| 5013 | 5065 | case ZigTypeIdComptimeInt: |
| 5014 | 5066 | case ZigTypeIdUndefined: |
| 5015 | 5067 | case ZigTypeIdNull: |
| 5016 | case ZigTypeIdNamespace: | |
| 5017 | 5068 | case ZigTypeIdBoundFn: |
| 5018 | 5069 | case ZigTypeIdFn: |
| 5019 | 5070 | case ZigTypeIdOpaque: |
| ... | ... | @@ -5143,7 +5194,6 @@ OnePossibleValue type_has_one_possible_value(CodeGen *g, ZigType *type_entry) { |
| 5143 | 5194 | case ZigTypeIdComptimeFloat: |
| 5144 | 5195 | case ZigTypeIdComptimeInt: |
| 5145 | 5196 | case ZigTypeIdMetaType: |
| 5146 | case ZigTypeIdNamespace: | |
| 5147 | 5197 | case ZigTypeIdBoundFn: |
| 5148 | 5198 | case ZigTypeIdArgTuple: |
| 5149 | 5199 | case ZigTypeIdOptional: |
| ... | ... | @@ -5209,7 +5259,6 @@ ReqCompTime type_requires_comptime(CodeGen *g, ZigType *type_entry) { |
| 5209 | 5259 | case ZigTypeIdUndefined: |
| 5210 | 5260 | case ZigTypeIdNull: |
| 5211 | 5261 | case ZigTypeIdMetaType: |
| 5212 | case ZigTypeIdNamespace: | |
| 5213 | 5262 | case ZigTypeIdBoundFn: |
| 5214 | 5263 | case ZigTypeIdArgTuple: |
| 5215 | 5264 | return ReqCompTimeYes; |
| ... | ... | @@ -5797,8 +5846,6 @@ bool const_values_equal(CodeGen *g, ConstExprValue *a, ConstExprValue *b) { |
| 5797 | 5846 | } |
| 5798 | 5847 | case ZigTypeIdErrorUnion: |
| 5799 | 5848 | zig_panic("TODO"); |
| 5800 | case ZigTypeIdNamespace: | |
| 5801 | return a->data.x_import == b->data.x_import; | |
| 5802 | 5849 | case ZigTypeIdArgTuple: |
| 5803 | 5850 | return a->data.x_arg_tuple.start_index == b->data.x_arg_tuple.start_index && |
| 5804 | 5851 | a->data.x_arg_tuple.end_index == b->data.x_arg_tuple.end_index; |
| ... | ... | @@ -6072,16 +6119,6 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { |
| 6072 | 6119 | } |
| 6073 | 6120 | return; |
| 6074 | 6121 | } |
| 6075 | case ZigTypeIdNamespace: | |
| 6076 | { | |
| 6077 | ImportTableEntry *import = const_val->data.x_import; | |
| 6078 | if (import->c_import_node) { | |
| 6079 | buf_appendf(buf, "(namespace from C import)"); | |
| 6080 | } else { | |
| 6081 | buf_appendf(buf, "(namespace: %s)", buf_ptr(import->path)); | |
| 6082 | } | |
| 6083 | return; | |
| 6084 | } | |
| 6085 | 6122 | case ZigTypeIdBoundFn: |
| 6086 | 6123 | { |
| 6087 | 6124 | ZigFn *fn_entry = const_val->data.x_bound_fn.fn; |
| ... | ... | @@ -6203,7 +6240,6 @@ uint32_t type_id_hash(TypeId x) { |
| 6203 | 6240 | case ZigTypeIdEnum: |
| 6204 | 6241 | case ZigTypeIdUnion: |
| 6205 | 6242 | case ZigTypeIdFn: |
| 6206 | case ZigTypeIdNamespace: | |
| 6207 | 6243 | case ZigTypeIdBoundFn: |
| 6208 | 6244 | case ZigTypeIdArgTuple: |
| 6209 | 6245 | case ZigTypeIdPromise: |
| ... | ... | @@ -6252,7 +6288,6 @@ bool type_id_eql(TypeId a, TypeId b) { |
| 6252 | 6288 | case ZigTypeIdEnum: |
| 6253 | 6289 | case ZigTypeIdUnion: |
| 6254 | 6290 | case ZigTypeIdFn: |
| 6255 | case ZigTypeIdNamespace: | |
| 6256 | 6291 | case ZigTypeIdBoundFn: |
| 6257 | 6292 | case ZigTypeIdArgTuple: |
| 6258 | 6293 | case ZigTypeIdOpaque: |
| ... | ... | @@ -6419,7 +6454,6 @@ static const ZigTypeId all_type_ids[] = { |
| 6419 | 6454 | ZigTypeIdEnum, |
| 6420 | 6455 | ZigTypeIdUnion, |
| 6421 | 6456 | ZigTypeIdFn, |
| 6422 | ZigTypeIdNamespace, | |
| 6423 | 6457 | ZigTypeIdBoundFn, |
| 6424 | 6458 | ZigTypeIdArgTuple, |
| 6425 | 6459 | ZigTypeIdOpaque, |
| ... | ... | @@ -6480,18 +6514,16 @@ size_t type_id_index(ZigType *entry) { |
| 6480 | 6514 | return 17; |
| 6481 | 6515 | case ZigTypeIdFn: |
| 6482 | 6516 | return 18; |
| 6483 | case ZigTypeIdNamespace: | |
| 6484 | return 19; | |
| 6485 | 6517 | case ZigTypeIdBoundFn: |
| 6486 | return 20; | |
| 6518 | return 19; | |
| 6487 | 6519 | case ZigTypeIdArgTuple: |
| 6488 | return 21; | |
| 6520 | return 20; | |
| 6489 | 6521 | case ZigTypeIdOpaque: |
| 6490 | return 22; | |
| 6522 | return 21; | |
| 6491 | 6523 | case ZigTypeIdPromise: |
| 6492 | return 23; | |
| 6524 | return 22; | |
| 6493 | 6525 | case ZigTypeIdVector: |
| 6494 | return 24; | |
| 6526 | return 23; | |
| 6495 | 6527 | } |
| 6496 | 6528 | zig_unreachable(); |
| 6497 | 6529 | } |
| ... | ... | @@ -6538,8 +6570,6 @@ const char *type_id_name(ZigTypeId id) { |
| 6538 | 6570 | return "Union"; |
| 6539 | 6571 | case ZigTypeIdFn: |
| 6540 | 6572 | return "Fn"; |
| 6541 | case ZigTypeIdNamespace: | |
| 6542 | return "Namespace"; | |
| 6543 | 6573 | case ZigTypeIdBoundFn: |
| 6544 | 6574 | return "BoundFn"; |
| 6545 | 6575 | case ZigTypeIdArgTuple: |
| ... | ... | @@ -6619,8 +6649,8 @@ bool type_ptr_eql(const ZigType *a, const ZigType *b) { |
| 6619 | 6649 | } |
| 6620 | 6650 | |
| 6621 | 6651 | ConstExprValue *get_builtin_value(CodeGen *codegen, const char *name) { |
| 6622 | Tld *tld = codegen->compile_var_import->decls_scope->decl_table.get(buf_create_from_str(name)); | |
| 6623 | resolve_top_level_decl(codegen, tld, false, nullptr); | |
| 6652 | Tld *tld = get_container_scope(codegen->compile_var_import)->decl_table.get(buf_create_from_str(name)); | |
| 6653 | resolve_top_level_decl(codegen, tld, nullptr); | |
| 6624 | 6654 | assert(tld->id == TldIdVar); |
| 6625 | 6655 | TldVar *tld_var = (TldVar *)tld; |
| 6626 | 6656 | ConstExprValue *var_value = tld_var->var->const_value; |
| ... | ... | @@ -6812,3 +6842,34 @@ bool ptr_allows_addr_zero(ZigType *ptr_type) { |
| 6812 | 6842 | } |
| 6813 | 6843 | return false; |
| 6814 | 6844 | } |
| 6845 | ||
| 6846 | void emit_error_notes_for_ref_stack(CodeGen *g, ErrorMsg *msg) { | |
| 6847 | size_t i = g->tld_ref_source_node_stack.length; | |
| 6848 | for (;;) { | |
| 6849 | if (i == 0) | |
| 6850 | break; | |
| 6851 | i -= 1; | |
| 6852 | AstNode *source_node = g->tld_ref_source_node_stack.at(i); | |
| 6853 | if (source_node) { | |
| 6854 | msg = add_error_note(g, msg, source_node, buf_sprintf("referenced here")); | |
| 6855 | } | |
| 6856 | } | |
| 6857 | } | |
| 6858 | ||
| 6859 | Buf *type_bare_name(ZigType *type_entry) { | |
| 6860 | if (is_slice(type_entry)) { | |
| 6861 | return &type_entry->name; | |
| 6862 | } else if (is_container(type_entry)) { | |
| 6863 | return get_container_scope(type_entry)->bare_name; | |
| 6864 | } else if (type_entry->id == ZigTypeIdOpaque) { | |
| 6865 | return type_entry->data.opaque.bare_name; | |
| 6866 | } else { | |
| 6867 | return &type_entry->name; | |
| 6868 | } | |
| 6869 | } | |
| 6870 | ||
| 6871 | // TODO this will have to be more clever, probably using the full name | |
| 6872 | // and replacing '.' with '_' or something like that | |
| 6873 | Buf *type_h_name(ZigType *t) { | |
| 6874 | return type_bare_name(t); | |
| 6875 | } |
src/analyze.hpp+21-11| ... | ... | @@ -12,8 +12,9 @@ |
| 12 | 12 | |
| 13 | 13 | void semantic_analyze(CodeGen *g); |
| 14 | 14 | ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); |
| 15 | ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg); | |
| 15 | ErrorMsg *add_token_error(CodeGen *g, ZigType *owner, Token *token, Buf *msg); | |
| 16 | 16 | ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg); |
| 17 | void emit_error_notes_for_ref_stack(CodeGen *g, ErrorMsg *msg); | |
| 17 | 18 | ZigType *new_type_table_entry(ZigTypeId id); |
| 18 | 19 | ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const); |
| 19 | 20 | ZigType *get_pointer_to_type_extra(CodeGen *g, ZigType *child_type, bool is_const, |
| ... | ... | @@ -29,11 +30,13 @@ ZigType *get_optional_type(CodeGen *g, ZigType *child_type); |
| 29 | 30 | ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size); |
| 30 | 31 | ZigType *get_slice_type(CodeGen *g, ZigType *ptr_type); |
| 31 | 32 | ZigType *get_partial_container_type(CodeGen *g, Scope *scope, ContainerKind kind, |
| 32 | AstNode *decl_node, const char *name, ContainerLayout layout); | |
| 33 | AstNode *decl_node, const char *full_name, Buf *bare_name, ContainerLayout layout); | |
| 34 | ZigType *get_root_container_type(CodeGen *g, const char *full_name, Buf *bare_name, | |
| 35 | RootStruct *root_struct); | |
| 33 | 36 | ZigType *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x); |
| 34 | 37 | ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payload_type); |
| 35 | 38 | ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry); |
| 36 | ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *name); | |
| 39 | ZigType *get_opaque_type(CodeGen *g, Scope *scope, AstNode *source_node, const char *full_name, Buf *bare_name); | |
| 37 | 40 | ZigType *get_struct_type(CodeGen *g, const char *type_name, const char *field_names[], |
| 38 | 41 | ZigType *field_types[], size_t field_count); |
| 39 | 42 | ZigType *get_promise_type(CodeGen *g, ZigType *result_type); |
| ... | ... | @@ -46,13 +49,19 @@ bool type_allowed_in_extern(CodeGen *g, ZigType *type_entry); |
| 46 | 49 | bool ptr_allows_addr_zero(ZigType *ptr_type); |
| 47 | 50 | bool type_is_nonnull_ptr(ZigType *type); |
| 48 | 51 | |
| 49 | ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *abs_full_path, Buf *source_code); | |
| 50 | ||
| 52 | enum SourceKind { | |
| 53 | SourceKindRoot, | |
| 54 | SourceKindPkgMain, | |
| 55 | SourceKindNonRoot, | |
| 56 | }; | |
| 57 | ZigType *add_source_file(CodeGen *g, ZigPackage *package, Buf *abs_full_path, Buf *source_code, | |
| 58 | SourceKind source_kind); | |
| 51 | 59 | |
| 52 | 60 | ZigVar *find_variable(CodeGen *g, Scope *orig_context, Buf *name, ScopeFnDef **crossed_fndef_scope); |
| 53 | 61 | Tld *find_decl(CodeGen *g, Scope *scope, Buf *name); |
| 54 | void resolve_top_level_decl(CodeGen *g, Tld *tld, bool pointer_only, AstNode *source_node); | |
| 55 | bool type_is_codegen_pointer(ZigType *type); | |
| 62 | Tld *find_container_decl(CodeGen *g, ScopeDecls *decls_scope, Buf *name); | |
| 63 | void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node); | |
| 64 | bool type_is_non_optional_pointer(ZigType *type); | |
| 56 | 65 | |
| 57 | 66 | ZigType *get_src_ptr_type(ZigType *type); |
| 58 | 67 | ZigType *get_codegen_ptr_type(ZigType *type); |
| ... | ... | @@ -77,11 +86,11 @@ bool is_array_ref(ZigType *type_entry); |
| 77 | 86 | bool is_container_ref(ZigType *type_entry); |
| 78 | 87 | bool is_valid_vector_elem_type(ZigType *elem_type); |
| 79 | 88 | void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); |
| 80 | void scan_import(CodeGen *g, ImportTableEntry *import); | |
| 81 | 89 | void preview_use_decl(CodeGen *g, AstNode *node); |
| 82 | 90 | void resolve_use_decl(CodeGen *g, AstNode *node); |
| 83 | 91 | ZigFn *scope_fn_entry(Scope *scope); |
| 84 | ImportTableEntry *get_scope_import(Scope *scope); | |
| 92 | ZigPackage *scope_package(Scope *scope); | |
| 93 | ZigType *get_scope_import(Scope *scope); | |
| 85 | 94 | void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope); |
| 86 | 95 | ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, |
| 87 | 96 | bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); |
| ... | ... | @@ -109,7 +118,6 @@ ScopeCImport *create_cimport_scope(CodeGen *g, AstNode *node, Scope *parent); |
| 109 | 118 | ScopeLoop *create_loop_scope(CodeGen *g, AstNode *node, Scope *parent); |
| 110 | 119 | ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent); |
| 111 | 120 | ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry); |
| 112 | ScopeDecls *create_decls_scope(CodeGen *g, AstNode *node, Scope *parent, ZigType *container_type, ImportTableEntry *import); | |
| 113 | 121 | Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent); |
| 114 | 122 | Scope *create_coro_prelude_scope(CodeGen *g, AstNode *node, Scope *parent); |
| 115 | 123 | Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime); |
| ... | ... | @@ -186,7 +194,7 @@ LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); |
| 186 | 194 | |
| 187 | 195 | uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry); |
| 188 | 196 | ZigType *get_align_amt_type(CodeGen *g); |
| 189 | PackageTableEntry *new_anonymous_package(void); | |
| 197 | ZigPackage *new_anonymous_package(void); | |
| 190 | 198 | |
| 191 | 199 | Buf *const_value_to_buffer(ConstExprValue *const_val); |
| 192 | 200 | void add_fn_export(CodeGen *g, ZigFn *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc); |
| ... | ... | @@ -232,4 +240,6 @@ Error ensure_const_val_repr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_no |
| 232 | 240 | ConstExprValue *const_val, ZigType *wanted_type); |
| 233 | 241 | |
| 234 | 242 | void typecheck_panic_fn(CodeGen *g, TldFn *tld_fn, ZigFn *panic_fn); |
| 243 | Buf *type_bare_name(ZigType *t); | |
| 244 | Buf *type_h_name(ZigType *t); | |
| 235 | 245 | #endif |
src/buffer.hpp+3-3| ... | ... | @@ -59,7 +59,7 @@ static inline void buf_deinit(Buf *buf) { |
| 59 | 59 | static inline void buf_init_from_mem(Buf *buf, const char *ptr, size_t len) { |
| 60 | 60 | assert(len != SIZE_MAX); |
| 61 | 61 | buf->list.resize(len + 1); |
| 62 | safe_memcpy(buf_ptr(buf), ptr, len); | |
| 62 | memcpy(buf_ptr(buf), ptr, len); | |
| 63 | 63 | buf->list.at(buf_len(buf)) = 0; |
| 64 | 64 | } |
| 65 | 65 | |
| ... | ... | @@ -98,7 +98,7 @@ static inline Buf *buf_slice(Buf *in_buf, size_t start, size_t end) { |
| 98 | 98 | assert(end <= buf_len(in_buf)); |
| 99 | 99 | Buf *out_buf = allocate<Buf>(1); |
| 100 | 100 | out_buf->list.resize(end - start + 1); |
| 101 | safe_memcpy(buf_ptr(out_buf), buf_ptr(in_buf) + start, end - start); | |
| 101 | memcpy(buf_ptr(out_buf), buf_ptr(in_buf) + start, end - start); | |
| 102 | 102 | out_buf->list.at(buf_len(out_buf)) = 0; |
| 103 | 103 | return out_buf; |
| 104 | 104 | } |
| ... | ... | @@ -108,7 +108,7 @@ static inline void buf_append_mem(Buf *buf, const char *mem, size_t mem_len) { |
| 108 | 108 | assert(mem_len != SIZE_MAX); |
| 109 | 109 | size_t old_len = buf_len(buf); |
| 110 | 110 | buf_resize(buf, old_len + mem_len); |
| 111 | safe_memcpy(buf_ptr(buf) + old_len, mem, mem_len); | |
| 111 | memcpy(buf_ptr(buf) + old_len, mem, mem_len); | |
| 112 | 112 | buf->list.at(buf_len(buf)) = 0; |
| 113 | 113 | } |
| 114 | 114 |
src/codegen.cpp+118-94| ... | ... | @@ -48,16 +48,17 @@ static void init_darwin_native(CodeGen *g) { |
| 48 | 48 | } |
| 49 | 49 | } |
| 50 | 50 | |
| 51 | static PackageTableEntry *new_package(const char *root_src_dir, const char *root_src_path) { | |
| 52 | PackageTableEntry *entry = allocate<PackageTableEntry>(1); | |
| 51 | static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) { | |
| 52 | ZigPackage *entry = allocate<ZigPackage>(1); | |
| 53 | 53 | entry->package_table.init(4); |
| 54 | 54 | buf_init_from_str(&entry->root_src_dir, root_src_dir); |
| 55 | 55 | buf_init_from_str(&entry->root_src_path, root_src_path); |
| 56 | buf_init_from_str(&entry->pkg_path, pkg_path); | |
| 56 | 57 | return entry; |
| 57 | 58 | } |
| 58 | 59 | |
| 59 | PackageTableEntry *new_anonymous_package(void) { | |
| 60 | return new_package("", ""); | |
| 60 | ZigPackage *new_anonymous_package() { | |
| 61 | return new_package("", "", ""); | |
| 61 | 62 | } |
| 62 | 63 | |
| 63 | 64 | static const char *symbols_that_llvm_depends_on[] = { |
| ... | ... | @@ -87,8 +88,8 @@ static const char *symbols_that_llvm_depends_on[] = { |
| 87 | 88 | // TODO probably all of compiler-rt needs to go here |
| 88 | 89 | }; |
| 89 | 90 | |
| 90 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, | |
| 91 | Buf *zig_lib_dir, Buf *override_std_dir, ZigLibCInstallation *libc) | |
| 91 | CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, | |
| 92 | OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, Buf *override_std_dir, ZigLibCInstallation *libc) | |
| 92 | 93 | { |
| 93 | 94 | CodeGen *g = allocate<CodeGen>(1); |
| 94 | 95 | |
| ... | ... | @@ -132,20 +133,39 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out |
| 132 | 133 | } |
| 133 | 134 | |
| 134 | 135 | if (root_src_path) { |
| 135 | Buf *src_basename = buf_alloc(); | |
| 136 | Buf *src_dir = buf_alloc(); | |
| 137 | os_path_split(root_src_path, src_dir, src_basename); | |
| 136 | Buf *root_pkg_path; | |
| 137 | Buf *rel_root_src_path; | |
| 138 | if (main_pkg_path == nullptr) { | |
| 139 | Buf *src_basename = buf_alloc(); | |
| 140 | Buf *src_dir = buf_alloc(); | |
| 141 | os_path_split(root_src_path, src_dir, src_basename); | |
| 142 | ||
| 143 | if (buf_len(src_basename) == 0) { | |
| 144 | fprintf(stderr, "Invalid root source path: %s\n", buf_ptr(root_src_path)); | |
| 145 | exit(1); | |
| 146 | } | |
| 147 | root_pkg_path = src_dir; | |
| 148 | rel_root_src_path = src_basename; | |
| 149 | } else { | |
| 150 | Buf resolved_root_src_path = os_path_resolve(&root_src_path, 1); | |
| 151 | Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1); | |
| 138 | 152 | |
| 139 | if (buf_len(src_basename) == 0) { | |
| 140 | fprintf(stderr, "Invalid root source path: %s\n", buf_ptr(root_src_path)); | |
| 141 | exit(1); | |
| 153 | if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) { | |
| 154 | fprintf(stderr, "Root source path '%s' outside main package path '%s'", | |
| 155 | buf_ptr(root_src_path), buf_ptr(main_pkg_path)); | |
| 156 | exit(1); | |
| 157 | } | |
| 158 | root_pkg_path = main_pkg_path; | |
| 159 | rel_root_src_path = buf_create_from_mem( | |
| 160 | buf_ptr(&resolved_root_src_path) + buf_len(&resolved_main_pkg_path) + 1, | |
| 161 | buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1); | |
| 142 | 162 | } |
| 143 | 163 | |
| 144 | g->root_package = new_package(buf_ptr(src_dir), buf_ptr(src_basename)); | |
| 145 | g->std_package = new_package(buf_ptr(g->zig_std_dir), "index.zig"); | |
| 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), "std.zig", "std"); | |
| 146 | 166 | g->root_package->package_table.put(buf_create_from_str("std"), g->std_package); |
| 147 | 167 | } else { |
| 148 | g->root_package = new_package(".", ""); | |
| 168 | g->root_package = new_package(".", "", ""); | |
| 149 | 169 | } |
| 150 | 170 | |
| 151 | 171 | g->zig_std_special_dir = buf_alloc(); |
| ... | ... | @@ -621,7 +641,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { |
| 621 | 641 | if (scope->di_scope) |
| 622 | 642 | return scope->di_scope; |
| 623 | 643 | |
| 624 | ImportTableEntry *import = get_scope_import(scope); | |
| 644 | ZigType *import = get_scope_import(scope); | |
| 625 | 645 | switch (scope->id) { |
| 626 | 646 | case ScopeIdCImport: |
| 627 | 647 | zig_unreachable(); |
| ... | ... | @@ -644,7 +664,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { |
| 644 | 664 | assert(fn_di_scope != nullptr); |
| 645 | 665 | ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, |
| 646 | 666 | fn_di_scope, buf_ptr(&fn_table_entry->symbol_name), "", |
| 647 | import->di_file, line_number, | |
| 667 | import->data.structure.root_struct->di_file, line_number, | |
| 648 | 668 | fn_table_entry->type_entry->data.fn.raw_di_type, is_internal_linkage, |
| 649 | 669 | is_definition, scope_line, flags, is_optimized, nullptr); |
| 650 | 670 | |
| ... | ... | @@ -658,7 +678,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { |
| 658 | 678 | assert(decls_scope->container_type); |
| 659 | 679 | scope->di_scope = ZigLLVMTypeToScope(decls_scope->container_type->di_type); |
| 660 | 680 | } else { |
| 661 | scope->di_scope = ZigLLVMFileToScope(import->di_file); | |
| 681 | scope->di_scope = ZigLLVMFileToScope(import->data.structure.root_struct->di_file); | |
| 662 | 682 | } |
| 663 | 683 | return scope->di_scope; |
| 664 | 684 | case ScopeIdBlock: |
| ... | ... | @@ -668,7 +688,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { |
| 668 | 688 | assert(scope->parent); |
| 669 | 689 | ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, |
| 670 | 690 | get_di_scope(g, scope->parent), |
| 671 | import->di_file, | |
| 691 | import->data.structure.root_struct->di_file, | |
| 672 | 692 | (unsigned)scope->source_node->line + 1, |
| 673 | 693 | (unsigned)scope->source_node->column + 1); |
| 674 | 694 | scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); |
| ... | ... | @@ -1236,7 +1256,7 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { |
| 1236 | 1256 | |
| 1237 | 1257 | LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->type_ref; |
| 1238 | 1258 | |
| 1239 | // stack_trace.instruction_addresses[stack_trace.index % stack_trace.instruction_addresses.len] = return_address; | |
| 1259 | // stack_trace.instruction_addresses[stack_trace.index & (stack_trace.instruction_addresses.len - 1)] = return_address; | |
| 1240 | 1260 | |
| 1241 | 1261 | LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); |
| 1242 | 1262 | LLVMValueRef address_value = LLVMGetParam(fn_val, 1); |
| ... | ... | @@ -1254,9 +1274,10 @@ static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { |
| 1254 | 1274 | |
| 1255 | 1275 | LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); |
| 1256 | 1276 | LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); |
| 1257 | LLVMValueRef modded_val = LLVMBuildURem(g->builder, index_val, len_value, ""); | |
| 1277 | LLVMValueRef len_val_minus_one = LLVMBuildSub(g->builder, len_value, LLVMConstInt(usize_type_ref, 1, false), ""); | |
| 1278 | LLVMValueRef masked_val = LLVMBuildAnd(g->builder, index_val, len_val_minus_one, ""); | |
| 1258 | 1279 | LLVMValueRef address_indices[] = { |
| 1259 | modded_val, | |
| 1280 | masked_val, | |
| 1260 | 1281 | }; |
| 1261 | 1282 | |
| 1262 | 1283 | LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); |
| ... | ... | @@ -2196,7 +2217,7 @@ var_ok: |
| 2196 | 2217 | if (dest_ty != nullptr && var->decl_node) { |
| 2197 | 2218 | // arg index + 1 because the 0 index is return value |
| 2198 | 2219 | var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), |
| 2199 | buf_ptr(&var->name), fn_walk->data.vars.import->di_file, | |
| 2220 | buf_ptr(&var->name), fn_walk->data.vars.import->data.structure.root_struct->di_file, | |
| 2200 | 2221 | (unsigned)(var->decl_node->line + 1), |
| 2201 | 2222 | dest_ty->di_type, !g->strip_debug_symbols, 0, di_arg_index + 1); |
| 2202 | 2223 | } |
| ... | ... | @@ -3938,7 +3959,7 @@ static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueR |
| 3938 | 3959 | if (child_type->zero_bits) { |
| 3939 | 3960 | return maybe_handle; |
| 3940 | 3961 | } else { |
| 3941 | bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; | |
| 3962 | bool is_scalar = type_is_non_optional_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; | |
| 3942 | 3963 | if (is_scalar) { |
| 3943 | 3964 | return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(maybe_type->type_ref), ""); |
| 3944 | 3965 | } else { |
| ... | ... | @@ -3978,7 +3999,7 @@ static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutable *exec |
| 3978 | 3999 | if (child_type->zero_bits) { |
| 3979 | 4000 | return nullptr; |
| 3980 | 4001 | } else { |
| 3981 | bool is_scalar = type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; | |
| 4002 | bool is_scalar = type_is_non_optional_pointer(child_type) || child_type->id == ZigTypeIdErrorSet; | |
| 3982 | 4003 | if (is_scalar) { |
| 3983 | 4004 | return maybe_ptr; |
| 3984 | 4005 | } else { |
| ... | ... | @@ -4549,7 +4570,7 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 4549 | 4570 | |
| 4550 | 4571 | return tmp_struct_ptr; |
| 4551 | 4572 | } else if (array_type->id == ZigTypeIdPointer) { |
| 4552 | assert(array_type->data.pointer.ptr_len == PtrLenUnknown); | |
| 4573 | assert(array_type->data.pointer.ptr_len != PtrLenSingle); | |
| 4553 | 4574 | LLVMValueRef start_val = ir_llvm_value(g, instruction->start); |
| 4554 | 4575 | LLVMValueRef end_val = ir_llvm_value(g, instruction->end); |
| 4555 | 4576 | |
| ... | ... | @@ -4640,7 +4661,8 @@ static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutable *executabl |
| 4640 | 4661 | IrInstructionReturnAddress *instruction) |
| 4641 | 4662 | { |
| 4642 | 4663 | LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); |
| 4643 | return LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); | |
| 4664 | LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); | |
| 4665 | return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->type_ref, ""); | |
| 4644 | 4666 | } |
| 4645 | 4667 | |
| 4646 | 4668 | static LLVMValueRef get_frame_address_fn_val(CodeGen *g) { |
| ... | ... | @@ -4661,7 +4683,8 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable |
| 4661 | 4683 | IrInstructionFrameAddress *instruction) |
| 4662 | 4684 | { |
| 4663 | 4685 | LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->type_ref); |
| 4664 | return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); | |
| 4686 | LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); | |
| 4687 | return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->type_ref, ""); | |
| 4665 | 4688 | } |
| 4666 | 4689 | |
| 4667 | 4690 | static LLVMValueRef get_handle_fn_val(CodeGen *g) { |
| ... | ... | @@ -4839,7 +4862,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I |
| 4839 | 4862 | } |
| 4840 | 4863 | |
| 4841 | 4864 | LLVMValueRef payload_val = ir_llvm_value(g, instruction->value); |
| 4842 | if (type_is_codegen_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { | |
| 4865 | if (type_is_non_optional_pointer(child_type) || child_type->id == ZigTypeIdErrorSet) { | |
| 4843 | 4866 | return payload_val; |
| 4844 | 4867 | } |
| 4845 | 4868 | |
| ... | ... | @@ -5800,7 +5823,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con |
| 5800 | 5823 | case ZigTypeIdNull: |
| 5801 | 5824 | case ZigTypeIdErrorUnion: |
| 5802 | 5825 | case ZigTypeIdErrorSet: |
| 5803 | case ZigTypeIdNamespace: | |
| 5804 | 5826 | case ZigTypeIdBoundFn: |
| 5805 | 5827 | case ZigTypeIdArgTuple: |
| 5806 | 5828 | case ZigTypeIdVoid: |
| ... | ... | @@ -6077,9 +6099,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 6077 | 6099 | case ZigTypeIdOptional: |
| 6078 | 6100 | { |
| 6079 | 6101 | ZigType *child_type = type_entry->data.maybe.child_type; |
| 6080 | if (child_type->zero_bits) { | |
| 6102 | if (!type_has_bits(child_type)) { | |
| 6081 | 6103 | return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); |
| 6082 | } else if (type_is_codegen_pointer(child_type)) { | |
| 6104 | } else if (get_codegen_ptr_type(type_entry) != nullptr) { | |
| 6083 | 6105 | return gen_const_val_ptr(g, const_val, name); |
| 6084 | 6106 | } else if (child_type->id == ZigTypeIdErrorSet) { |
| 6085 | 6107 | return gen_const_val_err_set(g, const_val, name); |
| ... | ... | @@ -6400,7 +6422,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c |
| 6400 | 6422 | case ZigTypeIdComptimeInt: |
| 6401 | 6423 | case ZigTypeIdUndefined: |
| 6402 | 6424 | case ZigTypeIdNull: |
| 6403 | case ZigTypeIdNamespace: | |
| 6404 | 6425 | case ZigTypeIdBoundFn: |
| 6405 | 6426 | case ZigTypeIdArgTuple: |
| 6406 | 6427 | case ZigTypeIdOpaque: |
| ... | ... | @@ -6506,12 +6527,12 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, |
| 6506 | 6527 | assert(var->gen_is_const); |
| 6507 | 6528 | assert(type_entry); |
| 6508 | 6529 | |
| 6509 | ImportTableEntry *import = get_scope_import(var->parent_scope); | |
| 6530 | ZigType *import = get_scope_import(var->parent_scope); | |
| 6510 | 6531 | assert(import); |
| 6511 | 6532 | |
| 6512 | 6533 | bool is_local_to_unit = true; |
| 6513 | 6534 | ZigLLVMCreateGlobalVariable(g->dbuilder, get_di_scope(g, var->parent_scope), buf_ptr(&var->name), |
| 6514 | buf_ptr(&var->name), import->di_file, | |
| 6535 | buf_ptr(&var->name), import->data.structure.root_struct->di_file, | |
| 6515 | 6536 | (unsigned)(var->decl_node->line + 1), |
| 6516 | 6537 | type_entry->di_type, is_local_to_unit); |
| 6517 | 6538 | |
| ... | ... | @@ -6767,7 +6788,7 @@ static void do_code_gen(CodeGen *g) { |
| 6767 | 6788 | *slot = build_alloca(g, slot_type, "", alignment_bytes); |
| 6768 | 6789 | } |
| 6769 | 6790 | |
| 6770 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); | |
| 6791 | ZigType *import = get_scope_import(&fn_table_entry->fndef_scope->base); | |
| 6771 | 6792 | |
| 6772 | 6793 | unsigned gen_i_init = want_first_arg_sret(g, fn_type_id) ? 1 : 0; |
| 6773 | 6794 | |
| ... | ... | @@ -6799,7 +6820,7 @@ static void do_code_gen(CodeGen *g) { |
| 6799 | 6820 | var->value_ref = build_alloca(g, var->var_type, buf_ptr(&var->name), var->align_bytes); |
| 6800 | 6821 | |
| 6801 | 6822 | var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), |
| 6802 | buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), | |
| 6823 | buf_ptr(&var->name), import->data.structure.root_struct->di_file, (unsigned)(var->decl_node->line + 1), | |
| 6803 | 6824 | var->var_type->di_type, !g->strip_debug_symbols, 0); |
| 6804 | 6825 | |
| 6805 | 6826 | } else if (is_c_abi) { |
| ... | ... | @@ -6823,7 +6844,7 @@ static void do_code_gen(CodeGen *g) { |
| 6823 | 6844 | } |
| 6824 | 6845 | if (var->decl_node) { |
| 6825 | 6846 | var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), |
| 6826 | buf_ptr(&var->name), import->di_file, | |
| 6847 | buf_ptr(&var->name), import->data.structure.root_struct->di_file, | |
| 6827 | 6848 | (unsigned)(var->decl_node->line + 1), |
| 6828 | 6849 | gen_type->di_type, !g->strip_debug_symbols, 0, (unsigned)(var->gen_arg_index + 1)); |
| 6829 | 6850 | } |
| ... | ... | @@ -6971,12 +6992,6 @@ static void define_builtin_types(CodeGen *g) { |
| 6971 | 6992 | entry->zero_bits = true; |
| 6972 | 6993 | g->builtin_types.entry_invalid = entry; |
| 6973 | 6994 | } |
| 6974 | { | |
| 6975 | ZigType *entry = new_type_table_entry(ZigTypeIdNamespace); | |
| 6976 | buf_init_from_str(&entry->name, "(namespace)"); | |
| 6977 | entry->zero_bits = true; | |
| 6978 | g->builtin_types.entry_namespace = entry; | |
| 6979 | } | |
| 6980 | 6995 | { |
| 6981 | 6996 | ZigType *entry = new_type_table_entry(ZigTypeIdComptimeFloat); |
| 6982 | 6997 | buf_init_from_str(&entry->name, "comptime_float"); |
| ... | ... | @@ -7130,7 +7145,8 @@ static void define_builtin_types(CodeGen *g) { |
| 7130 | 7145 | g->builtin_types.entry_i64 = get_int_type(g, true, 64); |
| 7131 | 7146 | |
| 7132 | 7147 | { |
| 7133 | g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void"); | |
| 7148 | g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void", | |
| 7149 | buf_create_from_str("c_void")); | |
| 7134 | 7150 | g->primitive_type_table.put(&g->builtin_types.entry_c_void->name, g->builtin_types.entry_c_void); |
| 7135 | 7151 | } |
| 7136 | 7152 | |
| ... | ... | @@ -7470,7 +7486,6 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7470 | 7486 | " Enum: Enum,\n" |
| 7471 | 7487 | " Union: Union,\n" |
| 7472 | 7488 | " Fn: Fn,\n" |
| 7473 | " Namespace: void,\n" | |
| 7474 | 7489 | " BoundFn: Fn,\n" |
| 7475 | 7490 | " ArgTuple: void,\n" |
| 7476 | 7491 | " Opaque: void,\n" |
| ... | ... | @@ -7479,18 +7494,18 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7479 | 7494 | "\n\n" |
| 7480 | 7495 | " pub const Int = struct {\n" |
| 7481 | 7496 | " is_signed: bool,\n" |
| 7482 | " bits: u8,\n" | |
| 7497 | " bits: comptime_int,\n" | |
| 7483 | 7498 | " };\n" |
| 7484 | 7499 | "\n" |
| 7485 | 7500 | " pub const Float = struct {\n" |
| 7486 | " bits: u8,\n" | |
| 7501 | " bits: comptime_int,\n" | |
| 7487 | 7502 | " };\n" |
| 7488 | 7503 | "\n" |
| 7489 | 7504 | " pub const Pointer = struct {\n" |
| 7490 | 7505 | " size: Size,\n" |
| 7491 | 7506 | " is_const: bool,\n" |
| 7492 | 7507 | " is_volatile: bool,\n" |
| 7493 | " alignment: u32,\n" | |
| 7508 | " alignment: comptime_int,\n" | |
| 7494 | 7509 | " child: type,\n" |
| 7495 | 7510 | "\n" |
| 7496 | 7511 | " pub const Size = enum {\n" |
| ... | ... | @@ -7502,7 +7517,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7502 | 7517 | " };\n" |
| 7503 | 7518 | "\n" |
| 7504 | 7519 | " pub const Array = struct {\n" |
| 7505 | " len: usize,\n" | |
| 7520 | " len: comptime_int,\n" | |
| 7506 | 7521 | " child: type,\n" |
| 7507 | 7522 | " };\n" |
| 7508 | 7523 | "\n" |
| ... | ... | @@ -7514,7 +7529,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7514 | 7529 | "\n" |
| 7515 | 7530 | " pub const StructField = struct {\n" |
| 7516 | 7531 | " name: []const u8,\n" |
| 7517 | " offset: ?usize,\n" | |
| 7532 | " offset: ?comptime_int,\n" | |
| 7518 | 7533 | " field_type: type,\n" |
| 7519 | 7534 | " };\n" |
| 7520 | 7535 | "\n" |
| ... | ... | @@ -7535,7 +7550,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7535 | 7550 | "\n" |
| 7536 | 7551 | " pub const Error = struct {\n" |
| 7537 | 7552 | " name: []const u8,\n" |
| 7538 | " value: usize,\n" | |
| 7553 | " value: comptime_int,\n" | |
| 7539 | 7554 | " };\n" |
| 7540 | 7555 | "\n" |
| 7541 | 7556 | " pub const ErrorSet = struct {\n" |
| ... | ... | @@ -7544,7 +7559,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7544 | 7559 | "\n" |
| 7545 | 7560 | " pub const EnumField = struct {\n" |
| 7546 | 7561 | " name: []const u8,\n" |
| 7547 | " value: usize,\n" | |
| 7562 | " value: comptime_int,\n" | |
| 7548 | 7563 | " };\n" |
| 7549 | 7564 | "\n" |
| 7550 | 7565 | " pub const Enum = struct {\n" |
| ... | ... | @@ -7596,7 +7611,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { |
| 7596 | 7611 | " };\n" |
| 7597 | 7612 | "\n" |
| 7598 | 7613 | " pub const Vector = struct {\n" |
| 7599 | " len: u32,\n" | |
| 7614 | " len: comptime_int,\n" | |
| 7600 | 7615 | " child: type,\n" |
| 7601 | 7616 | " };\n" |
| 7602 | 7617 | "\n" |
| ... | ... | @@ -7751,12 +7766,12 @@ static Error define_builtin_compile_vars(CodeGen *g) { |
| 7751 | 7766 | |
| 7752 | 7767 | assert(g->root_package); |
| 7753 | 7768 | assert(g->std_package); |
| 7754 | g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename); | |
| 7769 | g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename, "builtin"); | |
| 7755 | 7770 | g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 7756 | 7771 | g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); |
| 7757 | 7772 | g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); |
| 7758 | g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents); | |
| 7759 | scan_import(g, g->compile_var_import); | |
| 7773 | g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents, | |
| 7774 | SourceKindPkgMain); | |
| 7760 | 7775 | |
| 7761 | 7776 | return ErrorNone; |
| 7762 | 7777 | } |
| ... | ... | @@ -7948,17 +7963,20 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) { |
| 7948 | 7963 | Buf *src_dirname = buf_alloc(); |
| 7949 | 7964 | os_path_split(full_path, src_dirname, src_basename); |
| 7950 | 7965 | |
| 7951 | ImportTableEntry *import = allocate<ImportTableEntry>(1); | |
| 7952 | import->source_code = nullptr; | |
| 7953 | import->path = full_path; | |
| 7954 | g->root_import = import; | |
| 7955 | import->decls_scope = create_decls_scope(g, nullptr, nullptr, nullptr, import); | |
| 7966 | Buf noextname = BUF_INIT; | |
| 7967 | os_path_extname(src_basename, &noextname, nullptr); | |
| 7956 | 7968 | |
| 7957 | 7969 | detect_libc(g); |
| 7958 | 7970 | |
| 7959 | 7971 | init(g); |
| 7960 | 7972 | |
| 7961 | import->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); | |
| 7973 | RootStruct *root_struct = allocate<RootStruct>(1); | |
| 7974 | root_struct->source_code = nullptr; | |
| 7975 | root_struct->path = full_path; | |
| 7976 | root_struct->di_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(src_basename), buf_ptr(src_dirname)); | |
| 7977 | ||
| 7978 | ZigType *import = get_root_container_type(g, buf_ptr(&noextname), &noextname, root_struct); | |
| 7979 | g->root_import = import; | |
| 7962 | 7980 | |
| 7963 | 7981 | ZigList<ErrorMsg *> errors = {0}; |
| 7964 | 7982 | Error err = parse_h_file(import, &errors, buf_ptr(full_path), g, nullptr); |
| ... | ... | @@ -7977,7 +7995,7 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) { |
| 7977 | 7995 | } |
| 7978 | 7996 | } |
| 7979 | 7997 | |
| 7980 | static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) { | |
| 7998 | static ZigType *add_special_code(CodeGen *g, ZigPackage *package, const char *basename) { | |
| 7981 | 7999 | Buf *code_basename = buf_create_from_str(basename); |
| 7982 | 8000 | Buf path_to_code_src = BUF_INIT; |
| 7983 | 8001 | os_path_join(g->zig_std_special_dir, code_basename, &path_to_code_src); |
| ... | ... | @@ -7991,21 +8009,21 @@ static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package |
| 7991 | 8009 | zig_panic("unable to open '%s': %s\n", buf_ptr(&path_to_code_src), err_str(err)); |
| 7992 | 8010 | } |
| 7993 | 8011 | |
| 7994 | return add_source_file(g, package, resolved_path, import_code); | |
| 8012 | return add_source_file(g, package, resolved_path, import_code, SourceKindPkgMain); | |
| 7995 | 8013 | } |
| 7996 | 8014 | |
| 7997 | static PackageTableEntry *create_bootstrap_pkg(CodeGen *g, PackageTableEntry *pkg_with_main) { | |
| 7998 | PackageTableEntry *package = codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "bootstrap.zig"); | |
| 8015 | static ZigPackage *create_bootstrap_pkg(CodeGen *g, ZigPackage *pkg_with_main) { | |
| 8016 | ZigPackage *package = codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "bootstrap.zig", "std.special"); | |
| 7999 | 8017 | package->package_table.put(buf_create_from_str("@root"), pkg_with_main); |
| 8000 | 8018 | return package; |
| 8001 | 8019 | } |
| 8002 | 8020 | |
| 8003 | static PackageTableEntry *create_test_runner_pkg(CodeGen *g) { | |
| 8004 | return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig"); | |
| 8021 | static ZigPackage *create_test_runner_pkg(CodeGen *g) { | |
| 8022 | return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); | |
| 8005 | 8023 | } |
| 8006 | 8024 | |
| 8007 | static PackageTableEntry *create_panic_pkg(CodeGen *g) { | |
| 8008 | return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "panic.zig"); | |
| 8025 | static ZigPackage *create_panic_pkg(CodeGen *g) { | |
| 8026 | return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "panic.zig", "std.special"); | |
| 8009 | 8027 | } |
| 8010 | 8028 | |
| 8011 | 8029 | static void create_test_compile_var_and_add_test_runner(CodeGen *g) { |
| ... | ... | @@ -8089,24 +8107,24 @@ static void gen_root_source(CodeGen *g) { |
| 8089 | 8107 | exit(1); |
| 8090 | 8108 | } |
| 8091 | 8109 | |
| 8092 | g->root_import = add_source_file(g, g->root_package, resolved_path, source_code); | |
| 8110 | ZigType *root_import_alias = add_source_file(g, g->root_package, resolved_path, source_code, SourceKindRoot); | |
| 8111 | assert(root_import_alias == g->root_import); | |
| 8093 | 8112 | |
| 8094 | 8113 | assert(g->root_out_name); |
| 8095 | 8114 | assert(g->out_type != OutTypeUnknown); |
| 8096 | 8115 | |
| 8097 | 8116 | { |
| 8098 | 8117 | // Zig has lazy top level definitions. Here we semantically analyze the panic function. |
| 8099 | ImportTableEntry *import_with_panic; | |
| 8118 | ZigType *import_with_panic; | |
| 8100 | 8119 | if (g->have_pub_panic) { |
| 8101 | 8120 | import_with_panic = g->root_import; |
| 8102 | 8121 | } else { |
| 8103 | 8122 | g->panic_package = create_panic_pkg(g); |
| 8104 | 8123 | import_with_panic = add_special_code(g, g->panic_package, "panic.zig"); |
| 8105 | 8124 | } |
| 8106 | scan_import(g, import_with_panic); | |
| 8107 | Tld *panic_tld = find_decl(g, &import_with_panic->decls_scope->base, buf_create_from_str("panic")); | |
| 8125 | Tld *panic_tld = find_decl(g, &get_container_scope(import_with_panic)->base, buf_create_from_str("panic")); | |
| 8108 | 8126 | assert(panic_tld != nullptr); |
| 8109 | resolve_top_level_decl(g, panic_tld, false, nullptr); | |
| 8127 | resolve_top_level_decl(g, panic_tld, nullptr); | |
| 8110 | 8128 | } |
| 8111 | 8129 | |
| 8112 | 8130 | |
| ... | ... | @@ -8358,7 +8376,6 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_e |
| 8358 | 8376 | case ZigTypeIdComptimeInt: |
| 8359 | 8377 | case ZigTypeIdUndefined: |
| 8360 | 8378 | case ZigTypeIdNull: |
| 8361 | case ZigTypeIdNamespace: | |
| 8362 | 8379 | case ZigTypeIdBoundFn: |
| 8363 | 8380 | case ZigTypeIdArgTuple: |
| 8364 | 8381 | case ZigTypeIdErrorUnion: |
| ... | ... | @@ -8496,7 +8513,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu |
| 8496 | 8513 | if (child_type->zero_bits) { |
| 8497 | 8514 | buf_init_from_str(out_buf, "bool"); |
| 8498 | 8515 | return; |
| 8499 | } else if (type_is_codegen_pointer(child_type)) { | |
| 8516 | } else if (type_is_non_optional_pointer(child_type)) { | |
| 8500 | 8517 | return get_c_type(g, gen_h, child_type, out_buf); |
| 8501 | 8518 | } else { |
| 8502 | 8519 | zig_unreachable(); |
| ... | ... | @@ -8506,19 +8523,19 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu |
| 8506 | 8523 | case ZigTypeIdOpaque: |
| 8507 | 8524 | { |
| 8508 | 8525 | buf_init_from_str(out_buf, "struct "); |
| 8509 | buf_append_buf(out_buf, &type_entry->name); | |
| 8526 | buf_append_buf(out_buf, type_h_name(type_entry)); | |
| 8510 | 8527 | return; |
| 8511 | 8528 | } |
| 8512 | 8529 | case ZigTypeIdUnion: |
| 8513 | 8530 | { |
| 8514 | 8531 | buf_init_from_str(out_buf, "union "); |
| 8515 | buf_append_buf(out_buf, &type_entry->name); | |
| 8532 | buf_append_buf(out_buf, type_h_name(type_entry)); | |
| 8516 | 8533 | return; |
| 8517 | 8534 | } |
| 8518 | 8535 | case ZigTypeIdEnum: |
| 8519 | 8536 | { |
| 8520 | 8537 | buf_init_from_str(out_buf, "enum "); |
| 8521 | buf_append_buf(out_buf, &type_entry->name); | |
| 8538 | buf_append_buf(out_buf, type_h_name(type_entry)); | |
| 8522 | 8539 | return; |
| 8523 | 8540 | } |
| 8524 | 8541 | case ZigTypeIdArray: |
| ... | ... | @@ -8541,7 +8558,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_bu |
| 8541 | 8558 | case ZigTypeIdInvalid: |
| 8542 | 8559 | case ZigTypeIdMetaType: |
| 8543 | 8560 | case ZigTypeIdBoundFn: |
| 8544 | case ZigTypeIdNamespace: | |
| 8545 | 8561 | case ZigTypeIdComptimeFloat: |
| 8546 | 8562 | case ZigTypeIdComptimeInt: |
| 8547 | 8563 | case ZigTypeIdUndefined: |
| ... | ... | @@ -8614,10 +8630,17 @@ static void gen_h_file(CodeGen *g) { |
| 8614 | 8630 | Buf return_type_c = BUF_INIT; |
| 8615 | 8631 | get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); |
| 8616 | 8632 | |
| 8633 | Buf *symbol_name; | |
| 8634 | if (fn_table_entry->export_list.length == 0) { | |
| 8635 | symbol_name = &fn_table_entry->symbol_name; | |
| 8636 | } else { | |
| 8637 | FnExport *fn_export = &fn_table_entry->export_list.items[0]; | |
| 8638 | symbol_name = &fn_export->name; | |
| 8639 | } | |
| 8617 | 8640 | buf_appendf(&h_buf, "%s %s %s(", |
| 8618 | 8641 | buf_ptr(export_macro), |
| 8619 | 8642 | buf_ptr(&return_type_c), |
| 8620 | buf_ptr(&fn_table_entry->symbol_name)); | |
| 8643 | buf_ptr(symbol_name)); | |
| 8621 | 8644 | |
| 8622 | 8645 | Buf param_type_c = BUF_INIT; |
| 8623 | 8646 | if (fn_type_id->param_count > 0) { |
| ... | ... | @@ -8693,7 +8716,6 @@ static void gen_h_file(CodeGen *g) { |
| 8693 | 8716 | case ZigTypeIdNull: |
| 8694 | 8717 | case ZigTypeIdErrorUnion: |
| 8695 | 8718 | case ZigTypeIdErrorSet: |
| 8696 | case ZigTypeIdNamespace: | |
| 8697 | 8719 | case ZigTypeIdBoundFn: |
| 8698 | 8720 | case ZigTypeIdArgTuple: |
| 8699 | 8721 | case ZigTypeIdOptional: |
| ... | ... | @@ -8703,7 +8725,7 @@ static void gen_h_file(CodeGen *g) { |
| 8703 | 8725 | zig_unreachable(); |
| 8704 | 8726 | case ZigTypeIdEnum: |
| 8705 | 8727 | if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { |
| 8706 | fprintf(out_h, "enum %s {\n", buf_ptr(&type_entry->name)); | |
| 8728 | fprintf(out_h, "enum %s {\n", buf_ptr(type_h_name(type_entry))); | |
| 8707 | 8729 | for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { |
| 8708 | 8730 | TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; |
| 8709 | 8731 | Buf *value_buf = buf_alloc(); |
| ... | ... | @@ -8716,12 +8738,12 @@ static void gen_h_file(CodeGen *g) { |
| 8716 | 8738 | } |
| 8717 | 8739 | fprintf(out_h, "};\n\n"); |
| 8718 | 8740 | } else { |
| 8719 | fprintf(out_h, "enum %s;\n", buf_ptr(&type_entry->name)); | |
| 8741 | fprintf(out_h, "enum %s;\n", buf_ptr(type_h_name(type_entry))); | |
| 8720 | 8742 | } |
| 8721 | 8743 | break; |
| 8722 | 8744 | case ZigTypeIdStruct: |
| 8723 | 8745 | if (type_entry->data.structure.layout == ContainerLayoutExtern) { |
| 8724 | fprintf(out_h, "struct %s {\n", buf_ptr(&type_entry->name)); | |
| 8746 | fprintf(out_h, "struct %s {\n", buf_ptr(type_h_name(type_entry))); | |
| 8725 | 8747 | for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { |
| 8726 | 8748 | TypeStructField *struct_field = &type_entry->data.structure.fields[field_i]; |
| 8727 | 8749 | |
| ... | ... | @@ -8739,12 +8761,12 @@ static void gen_h_file(CodeGen *g) { |
| 8739 | 8761 | } |
| 8740 | 8762 | fprintf(out_h, "};\n\n"); |
| 8741 | 8763 | } else { |
| 8742 | fprintf(out_h, "struct %s;\n", buf_ptr(&type_entry->name)); | |
| 8764 | fprintf(out_h, "struct %s;\n", buf_ptr(type_h_name(type_entry))); | |
| 8743 | 8765 | } |
| 8744 | 8766 | break; |
| 8745 | 8767 | case ZigTypeIdUnion: |
| 8746 | 8768 | if (type_entry->data.unionation.layout == ContainerLayoutExtern) { |
| 8747 | fprintf(out_h, "union %s {\n", buf_ptr(&type_entry->name)); | |
| 8769 | fprintf(out_h, "union %s {\n", buf_ptr(type_h_name(type_entry))); | |
| 8748 | 8770 | for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { |
| 8749 | 8771 | TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; |
| 8750 | 8772 | |
| ... | ... | @@ -8754,11 +8776,11 @@ static void gen_h_file(CodeGen *g) { |
| 8754 | 8776 | } |
| 8755 | 8777 | fprintf(out_h, "};\n\n"); |
| 8756 | 8778 | } else { |
| 8757 | fprintf(out_h, "union %s;\n", buf_ptr(&type_entry->name)); | |
| 8779 | fprintf(out_h, "union %s;\n", buf_ptr(type_h_name(type_entry))); | |
| 8758 | 8780 | } |
| 8759 | 8781 | break; |
| 8760 | 8782 | case ZigTypeIdOpaque: |
| 8761 | fprintf(out_h, "struct %s;\n\n", buf_ptr(&type_entry->name)); | |
| 8783 | fprintf(out_h, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); | |
| 8762 | 8784 | break; |
| 8763 | 8785 | } |
| 8764 | 8786 | } |
| ... | ... | @@ -8792,7 +8814,7 @@ void codegen_add_time_event(CodeGen *g, const char *name) { |
| 8792 | 8814 | g->timing_events.append({os_get_time(), name}); |
| 8793 | 8815 | } |
| 8794 | 8816 | |
| 8795 | static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) { | |
| 8817 | static void add_cache_pkg(CodeGen *g, CacheHash *ch, ZigPackage *pkg) { | |
| 8796 | 8818 | if (buf_len(&pkg->root_src_path) == 0) |
| 8797 | 8819 | return; |
| 8798 | 8820 | |
| ... | ... | @@ -9046,9 +9068,11 @@ void codegen_build_and_link(CodeGen *g) { |
| 9046 | 9068 | codegen_add_time_event(g, "Done"); |
| 9047 | 9069 | } |
| 9048 | 9070 | |
| 9049 | PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path) { | |
| 9071 | ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, | |
| 9072 | const char *pkg_path) | |
| 9073 | { | |
| 9050 | 9074 | init(g); |
| 9051 | PackageTableEntry *pkg = new_package(root_src_dir, root_src_path); | |
| 9075 | ZigPackage *pkg = new_package(root_src_dir, root_src_path, pkg_path); | |
| 9052 | 9076 | if (g->std_package != nullptr) { |
| 9053 | 9077 | assert(g->compile_var_package != nullptr); |
| 9054 | 9078 | pkg->package_table.put(buf_create_from_str("std"), g->std_package); |
src/codegen.hpp+4-3| ... | ... | @@ -15,8 +15,8 @@ |
| 15 | 15 | |
| 16 | 16 | #include <stdio.h> |
| 17 | 17 | |
| 18 | CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, | |
| 19 | Buf *zig_lib_dir, Buf *override_std_dir, ZigLibCInstallation *libc); | |
| 18 | CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, | |
| 19 | OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, Buf *override_std_dir, ZigLibCInstallation *libc); | |
| 20 | 20 | |
| 21 | 21 | void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); |
| 22 | 22 | void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); |
| ... | ... | @@ -48,7 +48,8 @@ void codegen_print_timing_report(CodeGen *g, FILE *f); |
| 48 | 48 | void codegen_link(CodeGen *g); |
| 49 | 49 | void codegen_build_and_link(CodeGen *g); |
| 50 | 50 | |
| 51 | PackageTableEntry *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path); | |
| 51 | ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, | |
| 52 | const char *pkg_path); | |
| 52 | 53 | void codegen_add_assembly(CodeGen *g, Buf *path); |
| 53 | 54 | void codegen_add_object(CodeGen *g, Buf *object_path); |
| 54 | 55 |
src/compiler.cpp+3-3| ... | ... | @@ -78,8 +78,8 @@ static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { |
| 78 | 78 | Buf std_buf = BUF_INIT; |
| 79 | 79 | buf_init_from_str(&std_buf, "std"); |
| 80 | 80 | |
| 81 | Buf index_zig_buf = BUF_INIT; | |
| 82 | buf_init_from_str(&index_zig_buf, "index.zig"); | |
| 81 | Buf std_zig_buf = BUF_INIT; | |
| 82 | buf_init_from_str(&std_zig_buf, "std.zig"); | |
| 83 | 83 | |
| 84 | 84 | Buf test_lib_dir = BUF_INIT; |
| 85 | 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 | 89 | os_path_join(test_path, &lib_buf, &test_lib_dir); |
| 90 | 90 | os_path_join(&test_lib_dir, &zig_buf, &test_zig_dir); |
| 91 | 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 | 94 | int err; |
| 95 | 95 | bool exists; |
src/ir.cpp+149-143| ... | ... | @@ -258,7 +258,6 @@ static bool types_have_same_zig_comptime_repr(ZigType *a, ZigType *b) { |
| 258 | 258 | case ZigTypeIdPointer: |
| 259 | 259 | case ZigTypeIdUndefined: |
| 260 | 260 | case ZigTypeIdNull: |
| 261 | case ZigTypeIdNamespace: | |
| 262 | 261 | case ZigTypeIdBoundFn: |
| 263 | 262 | case ZigTypeIdErrorSet: |
| 264 | 263 | case ZigTypeIdOpaque: |
| ... | ... | @@ -1123,11 +1122,11 @@ static IrInstruction *ir_create_const_fn(IrBuilder *irb, Scope *scope, AstNode * |
| 1123 | 1122 | return &const_instruction->base; |
| 1124 | 1123 | } |
| 1125 | 1124 | |
| 1126 | static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ImportTableEntry *import) { | |
| 1125 | static IrInstruction *ir_build_const_import(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *import) { | |
| 1127 | 1126 | IrInstructionConst *const_instruction = ir_build_instruction<IrInstructionConst>(irb, scope, source_node); |
| 1128 | const_instruction->base.value.type = irb->codegen->builtin_types.entry_namespace; | |
| 1127 | const_instruction->base.value.type = irb->codegen->builtin_types.entry_type; | |
| 1129 | 1128 | const_instruction->base.value.special = ConstValSpecialStatic; |
| 1130 | const_instruction->base.value.data.x_import = import; | |
| 1129 | const_instruction->base.value.data.x_type = import; | |
| 1131 | 1130 | return &const_instruction->base; |
| 1132 | 1131 | } |
| 1133 | 1132 | |
| ... | ... | @@ -3824,7 +3823,7 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, |
| 3824 | 3823 | if (tld) |
| 3825 | 3824 | return ir_build_decl_ref(irb, scope, node, tld, lval); |
| 3826 | 3825 | |
| 3827 | if (node->owner->any_imports_failed) { | |
| 3826 | if (get_container_scope(node->owner)->any_imports_failed) { | |
| 3828 | 3827 | // skip the error message since we had a failing import in this file |
| 3829 | 3828 | // if an import breaks we don't need redundant undeclared identifier errors |
| 3830 | 3829 | return irb->codegen->invalid_instruction; |
| ... | ... | @@ -6609,20 +6608,32 @@ static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *o |
| 6609 | 6608 | return true; |
| 6610 | 6609 | } |
| 6611 | 6610 | |
| 6612 | static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, AstNode *source_node) { | |
| 6611 | static Buf *get_anon_type_name(CodeGen *codegen, IrExecutable *exec, const char *kind_name, | |
| 6612 | Scope *scope, AstNode *source_node, Buf *out_bare_name) | |
| 6613 | { | |
| 6613 | 6614 | if (exec->name) { |
| 6614 | return exec->name; | |
| 6615 | ZigType *import = get_scope_import(scope); | |
| 6616 | Buf *namespace_name = buf_create_from_buf(&import->name); | |
| 6617 | if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR); | |
| 6618 | buf_append_buf(namespace_name, exec->name); | |
| 6619 | buf_init_from_buf(out_bare_name, exec->name); | |
| 6620 | return namespace_name; | |
| 6615 | 6621 | } else if (exec->name_fn != nullptr) { |
| 6616 | 6622 | Buf *name = buf_alloc(); |
| 6617 | 6623 | buf_append_buf(name, &exec->name_fn->symbol_name); |
| 6618 | 6624 | buf_appendf(name, "("); |
| 6619 | 6625 | render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope); |
| 6620 | 6626 | buf_appendf(name, ")"); |
| 6627 | buf_init_from_buf(out_bare_name, name); | |
| 6621 | 6628 | return name; |
| 6622 | 6629 | } else { |
| 6623 | //Note: C-imports do not have valid location information | |
| 6624 | return buf_sprintf("(anonymous %s at %s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ")", kind_name, | |
| 6625 | (source_node->owner->path != nullptr) ? buf_ptr(source_node->owner->path) : "(null)", source_node->line + 1, source_node->column + 1); | |
| 6630 | ZigType *import = get_scope_import(scope); | |
| 6631 | Buf *namespace_name = buf_create_from_buf(&import->name); | |
| 6632 | if (buf_len(namespace_name) != 0) buf_append_char(namespace_name, NAMESPACE_SEP_CHAR); | |
| 6633 | buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name, | |
| 6634 | source_node->line + 1, source_node->column + 1); | |
| 6635 | buf_init_from_buf(out_bare_name, namespace_name); | |
| 6636 | return namespace_name; | |
| 6626 | 6637 | } |
| 6627 | 6638 | } |
| 6628 | 6639 | |
| ... | ... | @@ -6630,24 +6641,23 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, |
| 6630 | 6641 | assert(node->type == NodeTypeContainerDecl); |
| 6631 | 6642 | |
| 6632 | 6643 | ContainerKind kind = node->data.container_decl.kind; |
| 6633 | Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), node); | |
| 6634 | ||
| 6635 | VisibMod visib_mod = VisibModPub; | |
| 6636 | TldContainer *tld_container = allocate<TldContainer>(1); | |
| 6637 | init_tld(&tld_container->base, TldIdContainer, name, visib_mod, node, parent_scope); | |
| 6644 | Buf *bare_name = buf_alloc(); | |
| 6645 | Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name); | |
| 6638 | 6646 | |
| 6639 | 6647 | ContainerLayout layout = node->data.container_decl.layout; |
| 6640 | 6648 | ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope, |
| 6641 | kind, node, buf_ptr(name), layout); | |
| 6649 | kind, node, buf_ptr(name), bare_name, layout); | |
| 6642 | 6650 | ScopeDecls *child_scope = get_container_scope(container_type); |
| 6643 | 6651 | |
| 6644 | tld_container->type_entry = container_type; | |
| 6645 | tld_container->decls_scope = child_scope; | |
| 6646 | ||
| 6647 | 6652 | for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) { |
| 6648 | 6653 | AstNode *child_node = node->data.container_decl.decls.at(i); |
| 6649 | 6654 | scan_decls(irb->codegen, child_scope, child_node); |
| 6650 | 6655 | } |
| 6656 | ||
| 6657 | TldContainer *tld_container = allocate<TldContainer>(1); | |
| 6658 | init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope); | |
| 6659 | tld_container->type_entry = container_type; | |
| 6660 | tld_container->decls_scope = child_scope; | |
| 6651 | 6661 | irb->codegen->resolve_queue.append(&tld_container->base); |
| 6652 | 6662 | |
| 6653 | 6663 | // Add this to the list to mark as invalid if analyzing this exec fails. |
| ... | ... | @@ -6732,7 +6742,8 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A |
| 6732 | 6742 | |
| 6733 | 6743 | uint32_t err_count = node->data.err_set_decl.decls.length; |
| 6734 | 6744 | |
| 6735 | Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error set", node); | |
| 6745 | Buf bare_name = BUF_INIT; | |
| 6746 | Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name); | |
| 6736 | 6747 | ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); |
| 6737 | 6748 | buf_init_from_buf(&err_set_type->name, type_name); |
| 6738 | 6749 | err_set_type->data.error_set.err_count = err_count; |
| ... | ... | @@ -12065,7 +12076,6 @@ static IrInstruction *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp * |
| 12065 | 12076 | case ZigTypeIdErrorSet: |
| 12066 | 12077 | case ZigTypeIdFn: |
| 12067 | 12078 | case ZigTypeIdOpaque: |
| 12068 | case ZigTypeIdNamespace: | |
| 12069 | 12079 | case ZigTypeIdBoundFn: |
| 12070 | 12080 | case ZigTypeIdArgTuple: |
| 12071 | 12081 | case ZigTypeIdPromise: |
| ... | ... | @@ -13407,7 +13417,6 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio |
| 13407 | 13417 | case ZigTypeIdOptional: |
| 13408 | 13418 | case ZigTypeIdErrorUnion: |
| 13409 | 13419 | case ZigTypeIdErrorSet: |
| 13410 | case ZigTypeIdNamespace: | |
| 13411 | 13420 | case ZigTypeIdBoundFn: |
| 13412 | 13421 | case ZigTypeIdArgTuple: |
| 13413 | 13422 | case ZigTypeIdOpaque: |
| ... | ... | @@ -13432,7 +13441,6 @@ static IrInstruction *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructio |
| 13432 | 13441 | case ZigTypeIdErrorSet: |
| 13433 | 13442 | case ZigTypeIdVector: |
| 13434 | 13443 | zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name)); |
| 13435 | case ZigTypeIdNamespace: | |
| 13436 | 13444 | case ZigTypeIdBoundFn: |
| 13437 | 13445 | case ZigTypeIdArgTuple: |
| 13438 | 13446 | case ZigTypeIdOpaque: |
| ... | ... | @@ -14616,7 +14624,6 @@ static IrInstruction *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op_ |
| 14616 | 14624 | case ZigTypeIdEnum: |
| 14617 | 14625 | case ZigTypeIdUnion: |
| 14618 | 14626 | case ZigTypeIdFn: |
| 14619 | case ZigTypeIdNamespace: | |
| 14620 | 14627 | case ZigTypeIdBoundFn: |
| 14621 | 14628 | case ZigTypeIdArgTuple: |
| 14622 | 14629 | case ZigTypeIdPromise: |
| ... | ... | @@ -15368,7 +15375,7 @@ static IrInstruction *ir_analyze_container_member_access_inner(IrAnalyze *ira, |
| 15368 | 15375 | auto entry = container_scope->decl_table.maybe_get(field_name); |
| 15369 | 15376 | Tld *tld = entry ? entry->value : nullptr; |
| 15370 | 15377 | if (tld && tld->id == TldIdFn) { |
| 15371 | resolve_top_level_decl(ira->codegen, tld, false, source_instr->source_node); | |
| 15378 | resolve_top_level_decl(ira->codegen, tld, source_instr->source_node); | |
| 15372 | 15379 | if (tld->resolution == TldResolutionInvalid) |
| 15373 | 15380 | return ira->codegen->invalid_instruction; |
| 15374 | 15381 | TldFn *tld_fn = (TldFn *)tld; |
| ... | ... | @@ -15557,8 +15564,7 @@ static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, |
| 15557 | 15564 | |
| 15558 | 15565 | |
| 15559 | 15566 | static IrInstruction *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source_instruction, Tld *tld) { |
| 15560 | bool pointer_only = false; | |
| 15561 | resolve_top_level_decl(ira->codegen, tld, pointer_only, source_instruction->source_node); | |
| 15567 | resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node); | |
| 15562 | 15568 | if (tld->resolution == TldResolutionInvalid) |
| 15563 | 15569 | return ira->codegen->invalid_instruction; |
| 15564 | 15570 | |
| ... | ... | @@ -15735,12 +15741,17 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc |
| 15735 | 15741 | } |
| 15736 | 15742 | } |
| 15737 | 15743 | ScopeDecls *container_scope = get_container_scope(child_type); |
| 15738 | if (container_scope != nullptr) { | |
| 15739 | auto entry = container_scope->decl_table.maybe_get(field_name); | |
| 15740 | Tld *tld = entry ? entry->value : nullptr; | |
| 15741 | if (tld) { | |
| 15742 | return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); | |
| 15744 | Tld *tld = find_container_decl(ira->codegen, container_scope, field_name); | |
| 15745 | if (tld) { | |
| 15746 | if (tld->visib_mod == VisibModPrivate && | |
| 15747 | tld->import != get_scope_import(field_ptr_instruction->base.scope)) | |
| 15748 | { | |
| 15749 | ErrorMsg *msg = ir_add_error(ira, &field_ptr_instruction->base, | |
| 15750 | buf_sprintf("'%s' is private", buf_ptr(field_name))); | |
| 15751 | add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); | |
| 15752 | return ira->codegen->invalid_instruction; | |
| 15743 | 15753 | } |
| 15754 | return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); | |
| 15744 | 15755 | } |
| 15745 | 15756 | if (child_type->id == ZigTypeIdUnion && |
| 15746 | 15757 | (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || |
| ... | ... | @@ -15758,9 +15769,11 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc |
| 15758 | 15769 | ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); |
| 15759 | 15770 | } |
| 15760 | 15771 | } |
| 15772 | const char *container_name = (child_type == ira->codegen->root_import) ? | |
| 15773 | "root source file" : buf_ptr(buf_sprintf("container '%s'", buf_ptr(&child_type->name))); | |
| 15761 | 15774 | ir_add_error(ira, &field_ptr_instruction->base, |
| 15762 | buf_sprintf("container '%s' has no member called '%s'", | |
| 15763 | buf_ptr(&child_type->name), buf_ptr(field_name))); | |
| 15775 | buf_sprintf("%s has no member called '%s'", | |
| 15776 | container_name, buf_ptr(field_name))); | |
| 15764 | 15777 | return ira->codegen->invalid_instruction; |
| 15765 | 15778 | } else if (child_type->id == ZigTypeIdErrorSet) { |
| 15766 | 15779 | ErrorTableEntry *err_entry; |
| ... | ... | @@ -15971,37 +15984,6 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc |
| 15971 | 15984 | buf_sprintf("type '%s' does not support field access", buf_ptr(&child_type->name))); |
| 15972 | 15985 | return ira->codegen->invalid_instruction; |
| 15973 | 15986 | } |
| 15974 | } else if (container_type->id == ZigTypeIdNamespace) { | |
| 15975 | assert(container_ptr->value.type->id == ZigTypeIdPointer); | |
| 15976 | ConstExprValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); | |
| 15977 | if (!container_ptr_val) | |
| 15978 | return ira->codegen->invalid_instruction; | |
| 15979 | ||
| 15980 | ConstExprValue *namespace_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, | |
| 15981 | field_ptr_instruction->base.source_node); | |
| 15982 | if (namespace_val == nullptr) | |
| 15983 | return ira->codegen->invalid_instruction; | |
| 15984 | assert(namespace_val->special == ConstValSpecialStatic); | |
| 15985 | ||
| 15986 | ImportTableEntry *namespace_import = namespace_val->data.x_import; | |
| 15987 | ||
| 15988 | Tld *tld = find_decl(ira->codegen, &namespace_import->decls_scope->base, field_name); | |
| 15989 | if (tld) { | |
| 15990 | if (tld->visib_mod == VisibModPrivate && | |
| 15991 | tld->import != source_node->owner) | |
| 15992 | { | |
| 15993 | ErrorMsg *msg = ir_add_error_node(ira, source_node, | |
| 15994 | buf_sprintf("'%s' is private", buf_ptr(field_name))); | |
| 15995 | add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); | |
| 15996 | return ira->codegen->invalid_instruction; | |
| 15997 | } | |
| 15998 | return ir_analyze_decl_ref(ira, &field_ptr_instruction->base, tld); | |
| 15999 | } else { | |
| 16000 | const char *import_name = namespace_import->path ? buf_ptr(namespace_import->path) : "(C import)"; | |
| 16001 | ir_add_error_node(ira, source_node, | |
| 16002 | buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), import_name)); | |
| 16003 | return ira->codegen->invalid_instruction; | |
| 16004 | } | |
| 16005 | 15987 | } else { |
| 16006 | 15988 | ir_add_error_node(ira, field_ptr_instruction->base.source_node, |
| 16007 | 15989 | buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); |
| ... | ... | @@ -16281,7 +16263,6 @@ static IrInstruction *ir_analyze_instruction_slice_type(IrAnalyze *ira, |
| 16281 | 16263 | case ZigTypeIdEnum: |
| 16282 | 16264 | case ZigTypeIdUnion: |
| 16283 | 16265 | case ZigTypeIdFn: |
| 16284 | case ZigTypeIdNamespace: | |
| 16285 | 16266 | case ZigTypeIdBoundFn: |
| 16286 | 16267 | case ZigTypeIdPromise: |
| 16287 | 16268 | case ZigTypeIdVector: |
| ... | ... | @@ -16402,7 +16383,6 @@ static IrInstruction *ir_analyze_instruction_array_type(IrAnalyze *ira, |
| 16402 | 16383 | case ZigTypeIdEnum: |
| 16403 | 16384 | case ZigTypeIdUnion: |
| 16404 | 16385 | case ZigTypeIdFn: |
| 16405 | case ZigTypeIdNamespace: | |
| 16406 | 16386 | case ZigTypeIdBoundFn: |
| 16407 | 16387 | case ZigTypeIdPromise: |
| 16408 | 16388 | case ZigTypeIdVector: |
| ... | ... | @@ -16452,7 +16432,6 @@ static IrInstruction *ir_analyze_instruction_size_of(IrAnalyze *ira, |
| 16452 | 16432 | case ZigTypeIdComptimeInt: |
| 16453 | 16433 | case ZigTypeIdBoundFn: |
| 16454 | 16434 | case ZigTypeIdMetaType: |
| 16455 | case ZigTypeIdNamespace: | |
| 16456 | 16435 | case ZigTypeIdArgTuple: |
| 16457 | 16436 | case ZigTypeIdOpaque: |
| 16458 | 16437 | ir_add_error_node(ira, size_of_instruction->base.source_node, |
| ... | ... | @@ -16860,7 +16839,6 @@ static IrInstruction *ir_analyze_instruction_switch_target(IrAnalyze *ira, |
| 16860 | 16839 | case ZigTypeIdPointer: |
| 16861 | 16840 | case ZigTypeIdPromise: |
| 16862 | 16841 | case ZigTypeIdFn: |
| 16863 | case ZigTypeIdNamespace: | |
| 16864 | 16842 | case ZigTypeIdErrorSet: { |
| 16865 | 16843 | if (pointee_val) { |
| 16866 | 16844 | IrInstruction *result = ir_const(ira, &switch_target_instruction->base, nullptr); |
| ... | ... | @@ -17009,25 +16987,29 @@ static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructio |
| 17009 | 16987 | return ira->codegen->invalid_instruction; |
| 17010 | 16988 | |
| 17011 | 16989 | AstNode *source_node = import_instruction->base.source_node; |
| 17012 | ImportTableEntry *import = source_node->owner; | |
| 16990 | ZigType *import = source_node->owner; | |
| 17013 | 16991 | |
| 17014 | 16992 | Buf *import_target_path; |
| 17015 | 16993 | Buf *search_dir; |
| 17016 | assert(import->package); | |
| 17017 | PackageTableEntry *target_package; | |
| 17018 | auto package_entry = import->package->package_table.maybe_get(import_target_str); | |
| 16994 | assert(import->data.structure.root_struct->package); | |
| 16995 | ZigPackage *target_package; | |
| 16996 | auto package_entry = import->data.structure.root_struct->package->package_table.maybe_get(import_target_str); | |
| 16997 | SourceKind source_kind; | |
| 17019 | 16998 | if (package_entry) { |
| 17020 | 16999 | target_package = package_entry->value; |
| 17021 | 17000 | import_target_path = &target_package->root_src_path; |
| 17022 | 17001 | search_dir = &target_package->root_src_dir; |
| 17002 | source_kind = SourceKindPkgMain; | |
| 17023 | 17003 | } else { |
| 17024 | 17004 | // try it as a filename |
| 17025 | target_package = import->package; | |
| 17005 | target_package = import->data.structure.root_struct->package; | |
| 17026 | 17006 | import_target_path = import_target_str; |
| 17027 | 17007 | |
| 17028 | 17008 | // search relative to importing file |
| 17029 | 17009 | search_dir = buf_alloc(); |
| 17030 | os_path_dirname(import->path, search_dir); | |
| 17010 | os_path_dirname(import->data.structure.root_struct->path, search_dir); | |
| 17011 | ||
| 17012 | source_kind = SourceKindNonRoot; | |
| 17031 | 17013 | } |
| 17032 | 17014 | |
| 17033 | 17015 | Buf full_path = BUF_INIT; |
| ... | ... | @@ -17041,10 +17023,19 @@ static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructio |
| 17041 | 17023 | |
| 17042 | 17024 | auto import_entry = ira->codegen->import_table.maybe_get(resolved_path); |
| 17043 | 17025 | if (import_entry) { |
| 17044 | IrInstruction *result = ir_const(ira, &import_instruction->base, | |
| 17045 | ira->codegen->builtin_types.entry_namespace); | |
| 17046 | result->value.data.x_import = import_entry->value; | |
| 17047 | return result; | |
| 17026 | return ir_const_type(ira, &import_instruction->base, import_entry->value); | |
| 17027 | } | |
| 17028 | ||
| 17029 | if (source_kind == SourceKindNonRoot) { | |
| 17030 | ZigPackage *cur_scope_pkg = scope_package(import_instruction->base.scope); | |
| 17031 | Buf *pkg_root_src_dir = &cur_scope_pkg->root_src_dir; | |
| 17032 | Buf resolved_root_src_dir = os_path_resolve(&pkg_root_src_dir, 1); | |
| 17033 | if (!buf_starts_with_buf(resolved_path, &resolved_root_src_dir)) { | |
| 17034 | ir_add_error_node(ira, source_node, | |
| 17035 | buf_sprintf("import of file outside package path: '%s'", | |
| 17036 | buf_ptr(import_target_path))); | |
| 17037 | return ira->codegen->invalid_instruction; | |
| 17038 | } | |
| 17048 | 17039 | } |
| 17049 | 17040 | |
| 17050 | 17041 | if ((err = file_fetch(ira->codegen, resolved_path, import_code))) { |
| ... | ... | @@ -17059,13 +17050,9 @@ static IrInstruction *ir_analyze_instruction_import(IrAnalyze *ira, IrInstructio |
| 17059 | 17050 | } |
| 17060 | 17051 | } |
| 17061 | 17052 | |
| 17062 | ImportTableEntry *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code); | |
| 17063 | ||
| 17064 | scan_import(ira->codegen, target_import); | |
| 17053 | ZigType *target_import = add_source_file(ira->codegen, target_package, resolved_path, import_code, source_kind); | |
| 17065 | 17054 | |
| 17066 | IrInstruction *result = ir_const(ira, &import_instruction->base, ira->codegen->builtin_types.entry_namespace); | |
| 17067 | result->value.data.x_import = target_import; | |
| 17068 | return result; | |
| 17055 | return ir_const_type(ira, &import_instruction->base, target_import); | |
| 17069 | 17056 | } |
| 17070 | 17057 | |
| 17071 | 17058 | static IrInstruction *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstructionRef *ref_instruction) { |
| ... | ... | @@ -17398,17 +17385,7 @@ static IrInstruction *ir_analyze_instruction_compile_err(IrAnalyze *ira, |
| 17398 | 17385 | return ira->codegen->invalid_instruction; |
| 17399 | 17386 | |
| 17400 | 17387 | ErrorMsg *msg = ir_add_error(ira, &instruction->base, msg_buf); |
| 17401 | size_t i = ira->codegen->tld_ref_source_node_stack.length; | |
| 17402 | for (;;) { | |
| 17403 | if (i == 0) | |
| 17404 | break; | |
| 17405 | i -= 1; | |
| 17406 | AstNode *source_node = ira->codegen->tld_ref_source_node_stack.at(i); | |
| 17407 | if (source_node) { | |
| 17408 | add_error_note(ira->codegen, msg, source_node, | |
| 17409 | buf_sprintf("referenced here")); | |
| 17410 | } | |
| 17411 | } | |
| 17388 | emit_error_notes_for_ref_stack(ira->codegen, msg); | |
| 17412 | 17389 | |
| 17413 | 17390 | return ira->codegen->invalid_instruction; |
| 17414 | 17391 | } |
| ... | ... | @@ -17665,6 +17642,12 @@ static IrInstruction *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, |
| 17665 | 17642 | return ir_const_unsigned(ira, &instruction->base, bit_offset); |
| 17666 | 17643 | } |
| 17667 | 17644 | |
| 17645 | static IrInstruction *ir_error_dependency_loop(IrAnalyze *ira, IrInstruction *source_instr) { | |
| 17646 | ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected")); | |
| 17647 | emit_error_notes_for_ref_stack(ira->codegen, msg); | |
| 17648 | return ira->codegen->invalid_instruction; | |
| 17649 | } | |
| 17650 | ||
| 17668 | 17651 | static void ensure_field_index(ZigType *type, const char *field_name, size_t index) { |
| 17669 | 17652 | Buf *field_name_buf; |
| 17670 | 17653 | |
| ... | ... | @@ -17711,7 +17694,9 @@ static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, Zig |
| 17711 | 17694 | return var->const_value->data.x_type; |
| 17712 | 17695 | } |
| 17713 | 17696 | |
| 17714 | static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, ScopeDecls *decls_scope) { | |
| 17697 | static Error ir_make_type_info_defs(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *out_val, | |
| 17698 | ScopeDecls *decls_scope) | |
| 17699 | { | |
| 17715 | 17700 | Error err; |
| 17716 | 17701 | ZigType *type_info_definition_type = ir_type_info_get_type(ira, "Definition", nullptr); |
| 17717 | 17702 | if ((err = ensure_complete_type(ira->codegen, type_info_definition_type))) |
| ... | ... | @@ -17741,7 +17726,7 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco |
| 17741 | 17726 | while ((curr_entry = decl_it.next()) != nullptr) { |
| 17742 | 17727 | // If the definition is unresolved, force it to be resolved again. |
| 17743 | 17728 | if (curr_entry->value->resolution == TldResolutionUnresolved) { |
| 17744 | resolve_top_level_decl(ira->codegen, curr_entry->value, false, curr_entry->value->source_node); | |
| 17729 | resolve_top_level_decl(ira->codegen, curr_entry->value, curr_entry->value->source_node); | |
| 17745 | 17730 | if (curr_entry->value->resolution != TldResolutionOk) { |
| 17746 | 17731 | return ErrorSemanticAnalyzeFail; |
| 17747 | 17732 | } |
| ... | ... | @@ -17831,6 +17816,11 @@ static Error ir_make_type_info_defs(IrAnalyze *ira, ConstExprValue *out_val, Sco |
| 17831 | 17816 | ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; |
| 17832 | 17817 | assert(!fn_entry->is_test); |
| 17833 | 17818 | |
| 17819 | if (fn_entry->type_entry == nullptr) { | |
| 17820 | ir_error_dependency_loop(ira, source_instr); | |
| 17821 | return ErrorSemanticAnalyzeFail; | |
| 17822 | } | |
| 17823 | ||
| 17834 | 17824 | AstNodeFnProto *fn_node = (AstNodeFnProto *)(fn_entry->proto_node); |
| 17835 | 17825 | |
| 17836 | 17826 | ConstExprValue *fn_def_val = create_const_vals(1); |
| ... | ... | @@ -18007,7 +17997,7 @@ static ConstExprValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_ty |
| 18007 | 17997 | // alignment: u32 |
| 18008 | 17998 | ensure_field_index(result->type, "alignment", 3); |
| 18009 | 17999 | fields[3].special = ConstValSpecialStatic; |
| 18010 | fields[3].type = get_int_type(ira->codegen, false, 29); | |
| 18000 | fields[3].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18011 | 18001 | bigint_init_unsigned(&fields[3].data.x_bigint, get_ptr_align(ira->codegen, attrs_type)); |
| 18012 | 18002 | // child: type |
| 18013 | 18003 | ensure_field_index(result->type, "child", 4); |
| ... | ... | @@ -18026,7 +18016,7 @@ static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, |
| 18026 | 18016 | |
| 18027 | 18017 | ConstExprValue *inner_fields = create_const_vals(2); |
| 18028 | 18018 | inner_fields[1].special = ConstValSpecialStatic; |
| 18029 | inner_fields[1].type = ira->codegen->builtin_types.entry_usize; | |
| 18019 | inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18030 | 18020 | |
| 18031 | 18021 | ConstExprValue *name = create_const_str_lit(ira->codegen, enum_field->name); |
| 18032 | 18022 | init_const_slice(ira->codegen, &inner_fields[0], name, 0, buf_len(enum_field->name), true); |
| ... | ... | @@ -18036,7 +18026,9 @@ static void make_enum_field_val(IrAnalyze *ira, ConstExprValue *enum_field_val, |
| 18036 | 18026 | enum_field_val->data.x_struct.fields = inner_fields; |
| 18037 | 18027 | } |
| 18038 | 18028 | |
| 18039 | static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigType *type_entry, ConstExprValue **out) { | |
| 18029 | static Error ir_make_type_info_value(IrAnalyze *ira, IrInstruction *source_instr, ZigType *type_entry, | |
| 18030 | ConstExprValue **out) | |
| 18031 | { | |
| 18040 | 18032 | Error err; |
| 18041 | 18033 | assert(type_entry != nullptr); |
| 18042 | 18034 | assert(!type_is_invalid(type_entry)); |
| ... | ... | @@ -18056,7 +18048,6 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18056 | 18048 | case ZigTypeIdComptimeInt: |
| 18057 | 18049 | case ZigTypeIdUndefined: |
| 18058 | 18050 | case ZigTypeIdNull: |
| 18059 | case ZigTypeIdNamespace: | |
| 18060 | 18051 | case ZigTypeIdArgTuple: |
| 18061 | 18052 | case ZigTypeIdOpaque: |
| 18062 | 18053 | *out = nullptr; |
| ... | ... | @@ -18089,7 +18080,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18089 | 18080 | // bits: u8 |
| 18090 | 18081 | ensure_field_index(result->type, "bits", 1); |
| 18091 | 18082 | fields[1].special = ConstValSpecialStatic; |
| 18092 | fields[1].type = ira->codegen->builtin_types.entry_u8; | |
| 18083 | fields[1].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18093 | 18084 | bigint_init_unsigned(&fields[1].data.x_bigint, type_entry->data.integral.bit_count); |
| 18094 | 18085 | |
| 18095 | 18086 | break; |
| ... | ... | @@ -18106,7 +18097,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18106 | 18097 | // bits: u8 |
| 18107 | 18098 | ensure_field_index(result->type, "bits", 0); |
| 18108 | 18099 | fields[0].special = ConstValSpecialStatic; |
| 18109 | fields[0].type = ira->codegen->builtin_types.entry_u8; | |
| 18100 | fields[0].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18110 | 18101 | bigint_init_unsigned(&fields->data.x_bigint, type_entry->data.floating.bit_count); |
| 18111 | 18102 | |
| 18112 | 18103 | break; |
| ... | ... | @@ -18130,7 +18121,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18130 | 18121 | // len: usize |
| 18131 | 18122 | ensure_field_index(result->type, "len", 0); |
| 18132 | 18123 | fields[0].special = ConstValSpecialStatic; |
| 18133 | fields[0].type = ira->codegen->builtin_types.entry_usize; | |
| 18124 | fields[0].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18134 | 18125 | bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.array.len); |
| 18135 | 18126 | // child: type |
| 18136 | 18127 | ensure_field_index(result->type, "child", 1); |
| ... | ... | @@ -18151,7 +18142,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18151 | 18142 | // len: usize |
| 18152 | 18143 | ensure_field_index(result->type, "len", 0); |
| 18153 | 18144 | fields[0].special = ConstValSpecialStatic; |
| 18154 | fields[0].type = ira->codegen->builtin_types.entry_u32; | |
| 18145 | fields[0].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18155 | 18146 | bigint_init_unsigned(&fields[0].data.x_bigint, type_entry->data.vector.len); |
| 18156 | 18147 | // child: type |
| 18157 | 18148 | ensure_field_index(result->type, "child", 1); |
| ... | ... | @@ -18248,8 +18239,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18248 | 18239 | } |
| 18249 | 18240 | // defs: []TypeInfo.Definition |
| 18250 | 18241 | ensure_field_index(result->type, "defs", 3); |
| 18251 | if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.enumeration.decls_scope))) | |
| 18242 | if ((err = ir_make_type_info_defs(ira, source_instr, &fields[3], | |
| 18243 | type_entry->data.enumeration.decls_scope))) | |
| 18244 | { | |
| 18252 | 18245 | return err; |
| 18246 | } | |
| 18253 | 18247 | |
| 18254 | 18248 | break; |
| 18255 | 18249 | } |
| ... | ... | @@ -18266,11 +18260,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18266 | 18260 | ensure_field_index(result->type, "errors", 0); |
| 18267 | 18261 | |
| 18268 | 18262 | ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr); |
| 18269 | if (!resolve_inferred_error_set(ira->codegen, type_entry, source_node)) { | |
| 18263 | if (!resolve_inferred_error_set(ira->codegen, type_entry, source_instr->source_node)) { | |
| 18270 | 18264 | return ErrorSemanticAnalyzeFail; |
| 18271 | 18265 | } |
| 18272 | 18266 | if (type_is_global_error_set(type_entry)) { |
| 18273 | ir_add_error_node(ira, source_node, | |
| 18267 | ir_add_error(ira, source_instr, | |
| 18274 | 18268 | buf_sprintf("TODO: compiler bug: implement @typeInfo support for anyerror. https://github.com/ziglang/zig/issues/1936")); |
| 18275 | 18269 | return ErrorSemanticAnalyzeFail; |
| 18276 | 18270 | } |
| ... | ... | @@ -18292,7 +18286,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18292 | 18286 | |
| 18293 | 18287 | ConstExprValue *inner_fields = create_const_vals(2); |
| 18294 | 18288 | inner_fields[1].special = ConstValSpecialStatic; |
| 18295 | inner_fields[1].type = ira->codegen->builtin_types.entry_usize; | |
| 18289 | inner_fields[1].type = ira->codegen->builtin_types.entry_num_lit_int; | |
| 18296 | 18290 | |
| 18297 | 18291 | ConstExprValue *name = nullptr; |
| 18298 | 18292 | if (error->cached_error_name_val != nullptr) |
| ... | ... | @@ -18412,8 +18406,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18412 | 18406 | } |
| 18413 | 18407 | // defs: []TypeInfo.Definition |
| 18414 | 18408 | ensure_field_index(result->type, "defs", 3); |
| 18415 | if ((err = ir_make_type_info_defs(ira, &fields[3], type_entry->data.unionation.decls_scope))) | |
| 18409 | if ((err = ir_make_type_info_defs(ira, source_instr, &fields[3], | |
| 18410 | type_entry->data.unionation.decls_scope))) | |
| 18411 | { | |
| 18416 | 18412 | return err; |
| 18413 | } | |
| 18417 | 18414 | |
| 18418 | 18415 | break; |
| 18419 | 18416 | } |
| ... | ... | @@ -18461,7 +18458,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18461 | 18458 | |
| 18462 | 18459 | ConstExprValue *inner_fields = create_const_vals(3); |
| 18463 | 18460 | inner_fields[1].special = ConstValSpecialStatic; |
| 18464 | inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_usize); | |
| 18461 | inner_fields[1].type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_num_lit_int); | |
| 18465 | 18462 | |
| 18466 | 18463 | if (!type_has_bits(struct_field->type_entry)) { |
| 18467 | 18464 | inner_fields[1].data.x_optional = nullptr; |
| ... | ... | @@ -18487,8 +18484,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18487 | 18484 | } |
| 18488 | 18485 | // defs: []TypeInfo.Definition |
| 18489 | 18486 | ensure_field_index(result->type, "defs", 2); |
| 18490 | if ((err = ir_make_type_info_defs(ira, &fields[2], type_entry->data.structure.decls_scope))) | |
| 18487 | if ((err = ir_make_type_info_defs(ira, source_instr, &fields[2], | |
| 18488 | type_entry->data.structure.decls_scope))) | |
| 18489 | { | |
| 18491 | 18490 | return err; |
| 18491 | } | |
| 18492 | 18492 | |
| 18493 | 18493 | break; |
| 18494 | 18494 | } |
| ... | ... | @@ -18600,7 +18600,7 @@ static Error ir_make_type_info_value(IrAnalyze *ira, AstNode *source_node, ZigTy |
| 18600 | 18600 | { |
| 18601 | 18601 | ZigType *fn_type = type_entry->data.bound_fn.fn_type; |
| 18602 | 18602 | assert(fn_type->id == ZigTypeIdFn); |
| 18603 | if ((err = ir_make_type_info_value(ira, source_node, fn_type, &result))) | |
| 18603 | if ((err = ir_make_type_info_value(ira, source_instr, fn_type, &result))) | |
| 18604 | 18604 | return err; |
| 18605 | 18605 | |
| 18606 | 18606 | break; |
| ... | ... | @@ -18625,7 +18625,7 @@ static IrInstruction *ir_analyze_instruction_type_info(IrAnalyze *ira, |
| 18625 | 18625 | ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); |
| 18626 | 18626 | |
| 18627 | 18627 | ConstExprValue *payload; |
| 18628 | if ((err = ir_make_type_info_value(ira, instruction->base.source_node, type_entry, &payload))) | |
| 18628 | if ((err = ir_make_type_info_value(ira, &instruction->base, type_entry, &payload))) | |
| 18629 | 18629 | return ira->codegen->invalid_instruction; |
| 18630 | 18630 | |
| 18631 | 18631 | IrInstruction *result = ir_const(ira, &instruction->base, result_type); |
| ... | ... | @@ -18680,7 +18680,7 @@ static IrInstruction *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstruc |
| 18680 | 18680 | return ira->codegen->invalid_instruction; |
| 18681 | 18681 | |
| 18682 | 18682 | if (!type_entry->cached_const_name_val) { |
| 18683 | type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, &type_entry->name); | |
| 18683 | type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry)); | |
| 18684 | 18684 | } |
| 18685 | 18685 | IrInstruction *result = ir_const(ira, &instruction->base, nullptr); |
| 18686 | 18686 | copy_const_val(&result->value, type_entry->cached_const_name_val, true); |
| ... | ... | @@ -18702,14 +18702,21 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct |
| 18702 | 18702 | if (type_is_invalid(cimport_result->type)) |
| 18703 | 18703 | return ira->codegen->invalid_instruction; |
| 18704 | 18704 | |
| 18705 | ImportTableEntry *child_import = allocate<ImportTableEntry>(1); | |
| 18706 | child_import->decls_scope = create_decls_scope(ira->codegen, node, nullptr, nullptr, child_import); | |
| 18707 | child_import->c_import_node = node; | |
| 18708 | child_import->package = new_anonymous_package(); | |
| 18709 | child_import->package->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package); | |
| 18710 | child_import->package->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); | |
| 18711 | child_import->di_file = ZigLLVMCreateFile(ira->codegen->dbuilder, | |
| 18705 | ZigPackage *cur_scope_pkg = scope_package(instruction->base.scope); | |
| 18706 | Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, | |
| 18707 | buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1); | |
| 18708 | ||
| 18709 | RootStruct *root_struct = allocate<RootStruct>(1); | |
| 18710 | root_struct->package = new_anonymous_package(); | |
| 18711 | root_struct->package->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package); | |
| 18712 | root_struct->package->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); | |
| 18713 | root_struct->c_import_node = node; | |
| 18714 | // TODO create namespace_name file in zig-cache instead of /tmp and use it | |
| 18715 | // for this DIFile | |
| 18716 | root_struct->di_file = ZigLLVMCreateFile(ira->codegen->dbuilder, | |
| 18712 | 18717 | buf_ptr(buf_create_from_str("cimport.h")), buf_ptr(buf_create_from_str("."))); |
| 18718 | ZigType *child_import = get_root_container_type(ira->codegen, buf_ptr(namespace_name), | |
| 18719 | namespace_name, root_struct); | |
| 18713 | 18720 | |
| 18714 | 18721 | ZigList<ErrorMsg *> errors = {0}; |
| 18715 | 18722 | |
| ... | ... | @@ -18738,14 +18745,12 @@ static IrInstruction *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruct |
| 18738 | 18745 | if (ira->codegen->verbose_cimport) { |
| 18739 | 18746 | fprintf(stderr, "\nC imports:\n"); |
| 18740 | 18747 | fprintf(stderr, "-----------\n"); |
| 18741 | ast_render(ira->codegen, stderr, child_import->root, 4); | |
| 18748 | ast_render(ira->codegen, stderr, child_import->data.structure.decl_node, 4); | |
| 18742 | 18749 | } |
| 18743 | 18750 | |
| 18744 | scan_decls(ira->codegen, child_import->decls_scope, child_import->root); | |
| 18751 | scan_decls(ira->codegen, get_container_scope(child_import), child_import->data.structure.decl_node); | |
| 18745 | 18752 | |
| 18746 | IrInstruction *result = ir_const(ira, &instruction->base, ira->codegen->builtin_types.entry_namespace); | |
| 18747 | result->value.data.x_import = child_import; | |
| 18748 | return result; | |
| 18753 | return ir_const_type(ira, &instruction->base, child_import); | |
| 18749 | 18754 | } |
| 18750 | 18755 | |
| 18751 | 18756 | static IrInstruction *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstructionCInclude *instruction) { |
| ... | ... | @@ -18819,10 +18824,10 @@ static IrInstruction *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstru |
| 18819 | 18824 | if (!rel_file_path) |
| 18820 | 18825 | return ira->codegen->invalid_instruction; |
| 18821 | 18826 | |
| 18822 | ImportTableEntry *import = get_scope_import(instruction->base.scope); | |
| 18827 | ZigType *import = get_scope_import(instruction->base.scope); | |
| 18823 | 18828 | // figure out absolute path to resource |
| 18824 | 18829 | Buf source_dir_path = BUF_INIT; |
| 18825 | os_path_dirname(import->path, &source_dir_path); | |
| 18830 | os_path_dirname(import->data.structure.root_struct->path, &source_dir_path); | |
| 18826 | 18831 | |
| 18827 | 18832 | Buf *resolve_paths[] = { |
| 18828 | 18833 | &source_dir_path, |
| ... | ... | @@ -19752,6 +19757,9 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction |
| 19752 | 19757 | return ira->codegen->invalid_instruction; |
| 19753 | 19758 | } |
| 19754 | 19759 | } else { |
| 19760 | if (array_type->data.pointer.ptr_len == PtrLenC) { | |
| 19761 | array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); | |
| 19762 | } | |
| 19755 | 19763 | return_type = get_slice_type(ira->codegen, array_type); |
| 19756 | 19764 | if (!end) { |
| 19757 | 19765 | ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); |
| ... | ... | @@ -20137,18 +20145,14 @@ static IrInstruction *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstru |
| 20137 | 20145 | static IrInstruction *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstructionReturnAddress *instruction) { |
| 20138 | 20146 | IrInstruction *result = ir_build_return_address(&ira->new_irb, |
| 20139 | 20147 | instruction->base.scope, instruction->base.source_node); |
| 20140 | ZigType *u8 = ira->codegen->builtin_types.entry_u8; | |
| 20141 | ZigType *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); | |
| 20142 | result->value.type = u8_ptr_const; | |
| 20148 | result->value.type = ira->codegen->builtin_types.entry_usize; | |
| 20143 | 20149 | return result; |
| 20144 | 20150 | } |
| 20145 | 20151 | |
| 20146 | 20152 | static IrInstruction *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstructionFrameAddress *instruction) { |
| 20147 | 20153 | IrInstruction *result = ir_build_frame_address(&ira->new_irb, |
| 20148 | 20154 | instruction->base.scope, instruction->base.source_node); |
| 20149 | ZigType *u8 = ira->codegen->builtin_types.entry_u8; | |
| 20150 | ZigType *u8_ptr_const = get_pointer_to_type(ira->codegen, u8, true); | |
| 20151 | result->value.type = u8_ptr_const; | |
| 20155 | result->value.type = ira->codegen->builtin_types.entry_usize; | |
| 20152 | 20156 | return result; |
| 20153 | 20157 | } |
| 20154 | 20158 | |
| ... | ... | @@ -20179,7 +20183,6 @@ static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruct |
| 20179 | 20183 | case ZigTypeIdComptimeInt: |
| 20180 | 20184 | case ZigTypeIdUndefined: |
| 20181 | 20185 | case ZigTypeIdNull: |
| 20182 | case ZigTypeIdNamespace: | |
| 20183 | 20186 | case ZigTypeIdBoundFn: |
| 20184 | 20187 | case ZigTypeIdArgTuple: |
| 20185 | 20188 | case ZigTypeIdVoid: |
| ... | ... | @@ -21025,7 +21028,6 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue |
| 21025 | 21028 | case ZigTypeIdOpaque: |
| 21026 | 21029 | case ZigTypeIdBoundFn: |
| 21027 | 21030 | case ZigTypeIdArgTuple: |
| 21028 | case ZigTypeIdNamespace: | |
| 21029 | 21031 | case ZigTypeIdUnreachable: |
| 21030 | 21032 | case ZigTypeIdComptimeFloat: |
| 21031 | 21033 | case ZigTypeIdComptimeInt: |
| ... | ... | @@ -21185,7 +21187,6 @@ static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *sou |
| 21185 | 21187 | case ZigTypeIdOpaque: |
| 21186 | 21188 | case ZigTypeIdBoundFn: |
| 21187 | 21189 | case ZigTypeIdArgTuple: |
| 21188 | case ZigTypeIdNamespace: | |
| 21189 | 21190 | case ZigTypeIdUnreachable: |
| 21190 | 21191 | case ZigTypeIdComptimeFloat: |
| 21191 | 21192 | case ZigTypeIdComptimeInt: |
| ... | ... | @@ -21343,7 +21344,6 @@ static bool type_can_bit_cast(ZigType *t) { |
| 21343 | 21344 | case ZigTypeIdOpaque: |
| 21344 | 21345 | case ZigTypeIdBoundFn: |
| 21345 | 21346 | case ZigTypeIdArgTuple: |
| 21346 | case ZigTypeIdNamespace: | |
| 21347 | 21347 | case ZigTypeIdUnreachable: |
| 21348 | 21348 | case ZigTypeIdComptimeFloat: |
| 21349 | 21349 | case ZigTypeIdComptimeInt: |
| ... | ... | @@ -21516,7 +21516,7 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, |
| 21516 | 21516 | Tld *tld = instruction->tld; |
| 21517 | 21517 | LVal lval = instruction->lval; |
| 21518 | 21518 | |
| 21519 | resolve_top_level_decl(ira->codegen, tld, lval == LValPtr, instruction->base.source_node); | |
| 21519 | resolve_top_level_decl(ira->codegen, tld, instruction->base.source_node); | |
| 21520 | 21520 | if (tld->resolution == TldResolutionInvalid) |
| 21521 | 21521 | return ira->codegen->invalid_instruction; |
| 21522 | 21522 | |
| ... | ... | @@ -21528,6 +21528,10 @@ static IrInstruction *ir_analyze_instruction_decl_ref(IrAnalyze *ira, |
| 21528 | 21528 | TldVar *tld_var = (TldVar *)tld; |
| 21529 | 21529 | ZigVar *var = tld_var->var; |
| 21530 | 21530 | |
| 21531 | if (var == nullptr) { | |
| 21532 | return ir_error_dependency_loop(ira, &instruction->base); | |
| 21533 | } | |
| 21534 | ||
| 21531 | 21535 | IrInstruction *var_ptr = ir_get_var_ptr(ira, &instruction->base, var); |
| 21532 | 21536 | if (type_is_invalid(var_ptr->value.type)) |
| 21533 | 21537 | return ira->codegen->invalid_instruction; |
| ... | ... | @@ -21664,9 +21668,11 @@ static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstru |
| 21664 | 21668 | } |
| 21665 | 21669 | |
| 21666 | 21670 | static IrInstruction *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInstructionOpaqueType *instruction) { |
| 21667 | Buf *name = get_anon_type_name(ira->codegen, ira->new_irb.exec, "opaque", instruction->base.source_node); | |
| 21671 | Buf *bare_name = buf_alloc(); | |
| 21672 | Buf *full_name = get_anon_type_name(ira->codegen, ira->new_irb.exec, "opaque", | |
| 21673 | instruction->base.scope, instruction->base.source_node, bare_name); | |
| 21668 | 21674 | ZigType *result_type = get_opaque_type(ira->codegen, instruction->base.scope, instruction->base.source_node, |
| 21669 | buf_ptr(name)); | |
| 21675 | buf_ptr(full_name), bare_name); | |
| 21670 | 21676 | return ir_const_type(ira, &instruction->base, result_type); |
| 21671 | 21677 | } |
| 21672 | 21678 |
src/link.cpp+2-4| ... | ... | @@ -41,7 +41,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) |
| 41 | 41 | child_out_type = OutTypeObj; |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | CodeGen *child_gen = codegen_create(full_path, parent_gen->zig_target, child_out_type, | |
| 44 | CodeGen *child_gen = codegen_create(nullptr, full_path, parent_gen->zig_target, child_out_type, | |
| 45 | 45 | parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir, |
| 46 | 46 | parent_gen->libc); |
| 47 | 47 | |
| ... | ... | @@ -87,10 +87,8 @@ static Buf *build_a(CodeGen *parent_gen, const char *aname) { |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | 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 | 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 | 93 | return build_a_raw(parent_gen, "compiler_rt", full_path); |
| 96 | 94 | } |
src/main.cpp+19-15| ... | ... | @@ -63,6 +63,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { |
| 63 | 63 | " --output-lib [file] override import library path\n" |
| 64 | 64 | " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" |
| 65 | 65 | " --pkg-end pop current pkg\n" |
| 66 | " --main-pkg-path set the directory of the root package\n" | |
| 66 | 67 | " --release-fast build with optimizations on and safety off\n" |
| 67 | 68 | " --release-safe build with optimizations on and safety on\n" |
| 68 | 69 | " --release-small build with size optimizations on and safety off\n" |
| ... | ... | @@ -215,7 +216,7 @@ struct CliPkg { |
| 215 | 216 | CliPkg *parent; |
| 216 | 217 | }; |
| 217 | 218 | |
| 218 | static void add_package(CodeGen *g, CliPkg *cli_pkg, PackageTableEntry *pkg) { | |
| 219 | static void add_package(CodeGen *g, CliPkg *cli_pkg, ZigPackage *pkg) { | |
| 219 | 220 | for (size_t i = 0; i < cli_pkg->children.length; i += 1) { |
| 220 | 221 | CliPkg *child_cli_pkg = cli_pkg->children.at(i); |
| 221 | 222 | |
| ... | ... | @@ -223,10 +224,11 @@ static void add_package(CodeGen *g, CliPkg *cli_pkg, PackageTableEntry *pkg) { |
| 223 | 224 | Buf *basename = buf_alloc(); |
| 224 | 225 | os_path_split(buf_create_from_str(child_cli_pkg->path), dirname, basename); |
| 225 | 226 | |
| 226 | PackageTableEntry *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename)); | |
| 227 | ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename), | |
| 228 | buf_ptr(buf_sprintf("%s.%s", buf_ptr(&pkg->pkg_path), child_cli_pkg->name))); | |
| 227 | 229 | auto entry = pkg->package_table.put_unique(buf_create_from_str(child_cli_pkg->name), child_pkg); |
| 228 | 230 | if (entry) { |
| 229 | PackageTableEntry *existing_pkg = entry->value; | |
| 231 | ZigPackage *existing_pkg = entry->value; | |
| 230 | 232 | Buf *full_path = buf_alloc(); |
| 231 | 233 | os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path); |
| 232 | 234 | fprintf(stderr, "Unable to add package '%s'->'%s': already exists as '%s'\n", |
| ... | ... | @@ -437,6 +439,7 @@ int main(int argc, char **argv) { |
| 437 | 439 | TargetSubsystem subsystem = TargetSubsystemAuto; |
| 438 | 440 | bool is_single_threaded = false; |
| 439 | 441 | Buf *override_std_dir = nullptr; |
| 442 | Buf *main_pkg_path = nullptr; | |
| 440 | 443 | ValgrindSupport valgrind_support = ValgrindSupportAuto; |
| 441 | 444 | |
| 442 | 445 | if (argc >= 2 && strcmp(argv[1], "build") == 0) { |
| ... | ... | @@ -475,8 +478,8 @@ int main(int argc, char **argv) { |
| 475 | 478 | |
| 476 | 479 | ZigTarget target; |
| 477 | 480 | get_native_target(&target); |
| 478 | CodeGen *g = codegen_create(build_runner_path, &target, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), | |
| 479 | override_std_dir, nullptr); | |
| 481 | CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe, | |
| 482 | BuildModeDebug, get_zig_lib_dir(), override_std_dir, nullptr); | |
| 480 | 483 | g->valgrind_support = valgrind_support; |
| 481 | 484 | g->enable_time_report = timing_info; |
| 482 | 485 | buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); |
| ... | ... | @@ -543,8 +546,8 @@ int main(int argc, char **argv) { |
| 543 | 546 | return EXIT_FAILURE; |
| 544 | 547 | } |
| 545 | 548 | |
| 546 | PackageTableEntry *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname), | |
| 547 | buf_ptr(&build_file_basename)); | |
| 549 | ZigPackage *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname), | |
| 550 | buf_ptr(&build_file_basename), "std.special"); | |
| 548 | 551 | g->root_package->package_table.put(buf_create_from_str("@build"), build_pkg); |
| 549 | 552 | g->enable_cache = get_cache_opt(enable_cache, true); |
| 550 | 553 | codegen_build_and_link(g); |
| ... | ... | @@ -566,8 +569,8 @@ int main(int argc, char **argv) { |
| 566 | 569 | get_native_target(&target); |
| 567 | 570 | Buf *fmt_runner_path = buf_alloc(); |
| 568 | 571 | os_path_join(get_zig_special_dir(), buf_create_from_str("fmt_runner.zig"), fmt_runner_path); |
| 569 | CodeGen *g = codegen_create(fmt_runner_path, &target, OutTypeExe, BuildModeDebug, get_zig_lib_dir(), | |
| 570 | nullptr, nullptr); | |
| 572 | CodeGen *g = codegen_create(main_pkg_path, fmt_runner_path, &target, OutTypeExe, | |
| 573 | BuildModeDebug, get_zig_lib_dir(), nullptr, nullptr); | |
| 571 | 574 | buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name); |
| 572 | 575 | g->valgrind_support = valgrind_support; |
| 573 | 576 | g->is_single_threaded = true; |
| ... | ... | @@ -728,6 +731,8 @@ int main(int argc, char **argv) { |
| 728 | 731 | llvm_argv.append(argv[i]); |
| 729 | 732 | } else if (strcmp(arg, "--override-std-dir") == 0) { |
| 730 | 733 | override_std_dir = buf_create_from_str(argv[i]); |
| 734 | } else if (strcmp(arg, "--main-pkg-path") == 0) { | |
| 735 | main_pkg_path = buf_create_from_str(argv[i]); | |
| 731 | 736 | } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { |
| 732 | 737 | lib_dirs.append(argv[i]); |
| 733 | 738 | } else if (strcmp(arg, "--library") == 0) { |
| ... | ... | @@ -907,8 +912,8 @@ int main(int argc, char **argv) { |
| 907 | 912 | return EXIT_SUCCESS; |
| 908 | 913 | } |
| 909 | 914 | case CmdBuiltin: { |
| 910 | CodeGen *g = codegen_create(nullptr, &target, out_type, build_mode, get_zig_lib_dir(), override_std_dir, | |
| 911 | nullptr); | |
| 915 | CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, | |
| 916 | out_type, build_mode, get_zig_lib_dir(), override_std_dir, nullptr); | |
| 912 | 917 | g->valgrind_support = valgrind_support; |
| 913 | 918 | g->is_single_threaded = is_single_threaded; |
| 914 | 919 | Buf *builtin_source = codegen_generate_builtin_source(g); |
| ... | ... | @@ -1011,8 +1016,8 @@ int main(int argc, char **argv) { |
| 1011 | 1016 | return EXIT_FAILURE; |
| 1012 | 1017 | } |
| 1013 | 1018 | } |
| 1014 | CodeGen *g = codegen_create(zig_root_source_file, &target, out_type, build_mode, get_zig_lib_dir(), | |
| 1015 | override_std_dir, libc); | |
| 1019 | CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode, | |
| 1020 | get_zig_lib_dir(), override_std_dir, libc); | |
| 1016 | 1021 | g->valgrind_support = valgrind_support; |
| 1017 | 1022 | g->subsystem = subsystem; |
| 1018 | 1023 | |
| ... | ... | @@ -1095,7 +1100,6 @@ int main(int argc, char **argv) { |
| 1095 | 1100 | if (out_file_lib != nullptr && out_type == OutTypeLib && !is_static) |
| 1096 | 1101 | codegen_set_output_lib_path(g, buf_create_from_str(out_file_lib)); |
| 1097 | 1102 | |
| 1098 | ||
| 1099 | 1103 | add_package(g, cur_pkg, g->root_package); |
| 1100 | 1104 | |
| 1101 | 1105 | if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { |
| ... | ... | @@ -1145,7 +1149,7 @@ int main(int argc, char **argv) { |
| 1145 | 1149 | } |
| 1146 | 1150 | } else if (cmd == CmdTranslateC) { |
| 1147 | 1151 | codegen_translate_c(g, in_file_buf); |
| 1148 | ast_render(g, stdout, g->root_import->root, 4); | |
| 1152 | ast_render(g, stdout, g->root_import->data.structure.decl_node, 4); | |
| 1149 | 1153 | if (timing_info) |
| 1150 | 1154 | codegen_print_timing_report(g, stdout); |
| 1151 | 1155 | return EXIT_SUCCESS; |
src/os.cpp+1-1| ... | ... | @@ -264,7 +264,7 @@ void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { |
| 264 | 264 | buf_append_buf(out_full_path, basename); |
| 265 | 265 | } |
| 266 | 266 | |
| 267 | int os_path_real(Buf *rel_path, Buf *out_abs_path) { | |
| 267 | Error os_path_real(Buf *rel_path, Buf *out_abs_path) { | |
| 268 | 268 | #if defined(ZIG_OS_WINDOWS) |
| 269 | 269 | buf_resize(out_abs_path, 4096); |
| 270 | 270 | if (_fullpath(buf_ptr(out_abs_path), buf_ptr(rel_path), buf_len(out_abs_path)) == nullptr) { |
src/os.hpp+1-1| ... | ... | @@ -96,7 +96,7 @@ void os_path_dirname(Buf *full_path, Buf *out_dirname); |
| 96 | 96 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); |
| 97 | 97 | void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname); |
| 98 | 98 | void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); |
| 99 | int os_path_real(Buf *rel_path, Buf *out_abs_path); | |
| 99 | Error os_path_real(Buf *rel_path, Buf *out_abs_path); | |
| 100 | 100 | Buf os_path_resolve(Buf **paths_ptr, size_t paths_len); |
| 101 | 101 | bool os_path_is_absolute(Buf *path); |
| 102 | 102 |
src/parser.cpp+10-8| ... | ... | @@ -18,7 +18,7 @@ struct ParseContext { |
| 18 | 18 | Buf *buf; |
| 19 | 19 | size_t current_token; |
| 20 | 20 | ZigList<Token> *tokens; |
| 21 | ImportTableEntry *owner; | |
| 21 | ZigType *owner; | |
| 22 | 22 | ErrColor err_color; |
| 23 | 23 | }; |
| 24 | 24 | |
| ... | ... | @@ -130,8 +130,10 @@ static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { |
| 130 | 130 | va_end(ap); |
| 131 | 131 | |
| 132 | 132 | |
| 133 | ErrorMsg *err = err_msg_create_with_line(pc->owner->path, token->start_line, token->start_column, | |
| 134 | pc->owner->source_code, pc->owner->line_offsets, msg); | |
| 133 | ErrorMsg *err = err_msg_create_with_line(pc->owner->data.structure.root_struct->path, | |
| 134 | token->start_line, token->start_column, | |
| 135 | pc->owner->data.structure.root_struct->source_code, | |
| 136 | pc->owner->data.structure.root_struct->line_offsets, msg); | |
| 135 | 137 | err->line_start = token->start_line; |
| 136 | 138 | err->column_start = token->start_column; |
| 137 | 139 | |
| ... | ... | @@ -148,8 +150,10 @@ static void ast_asm_error(ParseContext *pc, AstNode *node, size_t offset, const |
| 148 | 150 | Buf *msg = buf_vprintf(format, ap); |
| 149 | 151 | va_end(ap); |
| 150 | 152 | |
| 151 | ErrorMsg *err = err_msg_create_with_line(pc->owner->path, node->line, node->column, | |
| 152 | pc->owner->source_code, pc->owner->line_offsets, msg); | |
| 153 | ErrorMsg *err = err_msg_create_with_line(pc->owner->data.structure.root_struct->path, | |
| 154 | node->line, node->column, | |
| 155 | pc->owner->data.structure.root_struct->source_code, | |
| 156 | pc->owner->data.structure.root_struct->line_offsets, msg); | |
| 153 | 157 | |
| 154 | 158 | print_err_msg(err, pc->err_color); |
| 155 | 159 | exit(EXIT_FAILURE); |
| ... | ... | @@ -570,9 +574,7 @@ static void ast_parse_asm_template(ParseContext *pc, AstNode *node) { |
| 570 | 574 | } |
| 571 | 575 | } |
| 572 | 576 | |
| 573 | AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, | |
| 574 | ErrColor err_color) | |
| 575 | { | |
| 577 | AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *owner, ErrColor err_color) { | |
| 576 | 578 | ParseContext pc = {}; |
| 577 | 579 | pc.err_color = err_color; |
| 578 | 580 | pc.owner = owner; |
src/parser.hpp+1-1| ... | ... | @@ -16,7 +16,7 @@ ATTRIBUTE_PRINTF(2, 3) |
| 16 | 16 | void ast_token_error(Token *token, const char *format, ...); |
| 17 | 17 | |
| 18 | 18 | |
| 19 | AstNode * ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, ErrColor err_color); | |
| 19 | AstNode * ast_parse(Buf *buf, ZigList<Token> *tokens, ZigType *owner, ErrColor err_color); | |
| 20 | 20 | |
| 21 | 21 | void ast_print(AstNode *node, int indent); |
| 22 | 22 |
src/target.cpp+3-3| ... | ... | @@ -676,15 +676,15 @@ Error target_parse_triple(ZigTarget *target, const char *triple) { |
| 676 | 676 | if (!opt_archsub.is_some) |
| 677 | 677 | return ErrorMissingArchitecture; |
| 678 | 678 | |
| 679 | if (!opt_os.is_some) | |
| 680 | return ErrorMissingOperatingSystem; | |
| 681 | ||
| 682 | 679 | if ((err = target_parse_archsub(&target->arch, &target->sub_arch, |
| 683 | 680 | (char*)opt_archsub.value.ptr, opt_archsub.value.len))) |
| 684 | 681 | { |
| 685 | 682 | return err; |
| 686 | 683 | } |
| 687 | 684 | |
| 685 | if (!opt_os.is_some) | |
| 686 | return ErrorMissingOperatingSystem; | |
| 687 | ||
| 688 | 688 | if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) { |
| 689 | 689 | return err; |
| 690 | 690 | } |
src/translate_c.cpp+5-4| ... | ... | @@ -76,7 +76,7 @@ struct TransScopeWhile { |
| 76 | 76 | }; |
| 77 | 77 | |
| 78 | 78 | struct Context { |
| 79 | ImportTableEntry *import; | |
| 79 | ZigType *import; | |
| 80 | 80 | ZigList<ErrorMsg *> *errors; |
| 81 | 81 | VisibMod visib_mod; |
| 82 | 82 | bool want_export; |
| ... | ... | @@ -4779,7 +4779,7 @@ static void process_preprocessor_entities(Context *c, ZigClangASTUnit *zunit) { |
| 4779 | 4779 | } |
| 4780 | 4780 | } |
| 4781 | 4781 | |
| 4782 | Error parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *source, | |
| 4782 | Error parse_h_buf(ZigType *import, ZigList<ErrorMsg *> *errors, Buf *source, | |
| 4783 | 4783 | CodeGen *codegen, AstNode *source_node) |
| 4784 | 4784 | { |
| 4785 | 4785 | Error err; |
| ... | ... | @@ -4795,7 +4795,7 @@ Error parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *so |
| 4795 | 4795 | return err; |
| 4796 | 4796 | } |
| 4797 | 4797 | |
| 4798 | Error parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const char *target_file, | |
| 4798 | Error parse_h_file(ZigType *import, ZigList<ErrorMsg *> *errors, const char *target_file, | |
| 4799 | 4799 | CodeGen *codegen, AstNode *source_node) |
| 4800 | 4800 | { |
| 4801 | 4801 | Context context = {0}; |
| ... | ... | @@ -5005,7 +5005,8 @@ Error parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const |
| 5005 | 5005 | render_macros(c); |
| 5006 | 5006 | render_aliases(c); |
| 5007 | 5007 | |
| 5008 | import->root = c->root; | |
| 5008 | import->data.structure.decl_node = c->root; | |
| 5009 | import->data.structure.decls_scope->base.source_node = c->root; | |
| 5009 | 5010 | |
| 5010 | 5011 | return ErrorNone; |
| 5011 | 5012 | } |
src/translate_c.hpp+2-2| ... | ... | @@ -11,10 +11,10 @@ |
| 11 | 11 | |
| 12 | 12 | #include "all_types.hpp" |
| 13 | 13 | |
| 14 | Error parse_h_file(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, const char *target_file, | |
| 14 | Error parse_h_file(ZigType *import, ZigList<ErrorMsg *> *errors, const char *target_file, | |
| 15 | 15 | CodeGen *codegen, AstNode *source_node); |
| 16 | 16 | |
| 17 | Error parse_h_buf(ImportTableEntry *import, ZigList<ErrorMsg *> *errors, Buf *source, | |
| 17 | Error parse_h_buf(ZigType *import, ZigList<ErrorMsg *> *errors, Buf *source, | |
| 18 | 18 | CodeGen *codegen, AstNode *source_node); |
| 19 | 19 | |
| 20 | 20 | #endif |
src/util.hpp-12| ... | ... | @@ -93,18 +93,6 @@ ATTRIBUTE_RETURNS_NOALIAS static inline T *allocate(size_t count) { |
| 93 | 93 | return ptr; |
| 94 | 94 | } |
| 95 | 95 | |
| 96 | template<typename T> | |
| 97 | static inline void safe_memcpy(T *dest, const T *src, size_t count) { | |
| 98 | #ifdef NDEBUG | |
| 99 | memcpy(dest, src, count * sizeof(T)); | |
| 100 | #else | |
| 101 | // manually assign every elment to trigger compile error for non-copyable structs | |
| 102 | for (size_t i = 0; i < count; i += 1) { | |
| 103 | dest[i] = src[i]; | |
| 104 | } | |
| 105 | #endif | |
| 106 | } | |
| 107 | ||
| 108 | 96 | template<typename T> |
| 109 | 97 | static inline T *reallocate(T *old, size_t old_count, size_t new_count) { |
| 110 | 98 | T *ptr = reallocate_nonzero(old, old_count, new_count); |
std/array_list.zig+2-2| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 4 | const testing = std.testing; |
| ... | ... | @@ -406,6 +406,6 @@ const Item = struct { |
| 406 | 406 | |
| 407 | 407 | test "std.ArrayList: ArrayList(T) of struct T" { |
| 408 | 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 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
std/atomic/stack.zig+1-1| ... | ... | @@ -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 | 72 | const Context = struct { |
| 73 | 73 | allocator: *std.mem.Allocator, |
| 74 | 74 | stack: *Stack(i32), |
std/base64.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const mem = std.mem; |
std/buf_map.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const HashMap = std.HashMap; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const Allocator = mem.Allocator; |
std/buf_set.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const HashMap = @import("hash_map.zig").HashMap; |
| 3 | 3 | const mem = @import("mem.zig"); |
| 4 | 4 | const Allocator = mem.Allocator; |
std/buffer.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const Allocator = mem.Allocator; |
std/build.zig+13-2| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const mem = std.mem; |
| ... | ... | @@ -859,6 +859,7 @@ pub const LibExeObjStep = struct { |
| 859 | 859 | verbose_cc: bool, |
| 860 | 860 | c_std: Builder.CStd, |
| 861 | 861 | override_std_dir: ?[]const u8, |
| 862 | main_pkg_path: ?[]const u8, | |
| 862 | 863 | exec_cmd_args: ?[]const ?[]const u8, |
| 863 | 864 | name_prefix: []const u8, |
| 864 | 865 | filter: ?[]const u8, |
| ... | ... | @@ -950,6 +951,7 @@ pub const LibExeObjStep = struct { |
| 950 | 951 | .c_std = Builder.CStd.C99, |
| 951 | 952 | .system_linker_hack = false, |
| 952 | 953 | .override_std_dir = null, |
| 954 | .main_pkg_path = null, | |
| 953 | 955 | .exec_cmd_args = null, |
| 954 | 956 | .name_prefix = "", |
| 955 | 957 | .filter = null, |
| ... | ... | @@ -1098,6 +1100,10 @@ pub const LibExeObjStep = struct { |
| 1098 | 1100 | self.override_std_dir = dir_path; |
| 1099 | 1101 | } |
| 1100 | 1102 | |
| 1103 | pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { | |
| 1104 | self.main_pkg_path = dir_path; | |
| 1105 | } | |
| 1106 | ||
| 1101 | 1107 | pub fn setOutputPath(self: *LibExeObjStep, file_path: []const u8) void { |
| 1102 | 1108 | self.output_path = file_path; |
| 1103 | 1109 | |
| ... | ... | @@ -1320,7 +1326,7 @@ pub const LibExeObjStep = struct { |
| 1320 | 1326 | zig_args.append("--ver-patch") catch unreachable; |
| 1321 | 1327 | zig_args.append(builder.fmt("{}", self.version.patch)) catch unreachable; |
| 1322 | 1328 | } |
| 1323 | if ((self.kind == Kind.Exe or self.kind == Kind.Test) and self.static) { | |
| 1329 | if (self.static) { | |
| 1324 | 1330 | zig_args.append("--static") catch unreachable; |
| 1325 | 1331 | } |
| 1326 | 1332 | |
| ... | ... | @@ -1424,6 +1430,11 @@ pub const LibExeObjStep = struct { |
| 1424 | 1430 | try zig_args.append(builder.pathFromRoot(dir)); |
| 1425 | 1431 | } |
| 1426 | 1432 | |
| 1433 | if (self.main_pkg_path) |dir| { | |
| 1434 | try zig_args.append("--main-pkg-path"); | |
| 1435 | try zig_args.append(builder.pathFromRoot(dir)); | |
| 1436 | } | |
| 1437 | ||
| 1427 | 1438 | try builder.spawnChild(zig_args.toSliceConst()); |
| 1428 | 1439 | |
| 1429 | 1440 | if (self.kind == Kind.Lib and !self.static and self.target.wantSharedLibSymLinks()) { |
std/build/fmt.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const build = @import("../build.zig"); |
| 3 | 3 | const Step = build.Step; |
| 4 | 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 | 163 | // sys/types.h on macos uses #pragma pack(4) so these checks are |
| 164 | 164 | // to make sure the struct is laid out the same. These values were |
| 165 | 165 | // produced from C code using the offsetof macro. |
| 166 | const std = @import("../index.zig"); | |
| 166 | const std = @import("../std.zig"); | |
| 167 | 167 | const assert = std.debug.assert; |
| 168 | 168 | |
| 169 | 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 | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | |
| 2 | const std = @import("std.zig"); | |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const mem = std.mem; |
| 5 | 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+4-4| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | |
| 2 | const math = @import("../math.zig"); | |
| 3 | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | |
| 4 | const debug = @import("../debug.zig"); | |
| 5 | 5 | const builtin = @import("builtin"); |
| 6 | 6 | const htest = @import("test.zig"); |
| 7 | 7 | |
| ... | ... | @@ -34,8 +34,8 @@ pub const Blake2s256 = Blake2s(256); |
| 34 | 34 | fn Blake2s(comptime out_len: usize) type { |
| 35 | 35 | return struct { |
| 36 | 36 | const Self = @This(); |
| 37 | const block_length = 64; | |
| 38 | const digest_length = out_len / 8; | |
| 37 | pub const block_length = 64; | |
| 38 | pub const digest_length = out_len / 8; | |
| 39 | 39 | |
| 40 | 40 | const iv = [8]u32{ |
| 41 | 41 | 0x6A09E667, |
std/crypto/chacha20.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 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 | 4 | const mem = std.mem; |
| 5 | 5 | const endian = std.endian; |
| 6 | 6 | const assert = std.debug.assert; |
std/crypto/hmac.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const crypto = std.crypto; |
| 3 | 3 | const debug = std.debug; |
| 4 | 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+5-5| ... | ... | @@ -1,9 +1,9 @@ |
| 1 | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | |
| 2 | const math = @import("../math.zig"); | |
| 3 | 3 | const endian = @import("../endian.zig"); |
| 4 | 4 | const builtin = @import("builtin"); |
| 5 | const debug = @import("../debug/index.zig"); | |
| 6 | const fmt = @import("../fmt/index.zig"); | |
| 5 | const debug = @import("../debug.zig"); | |
| 6 | const fmt = @import("../fmt.zig"); | |
| 7 | 7 | |
| 8 | 8 | const RoundParam = struct { |
| 9 | 9 | a: usize, |
| ... | ... | @@ -29,8 +29,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, k: usize, s: u32, t: u32) RoundPar |
| 29 | 29 | |
| 30 | 30 | pub const Md5 = struct { |
| 31 | 31 | const Self = @This(); |
| 32 | const block_length = 64; | |
| 33 | const digest_length = 16; | |
| 32 | pub const block_length = 64; | |
| 33 | pub const digest_length = 16; | |
| 34 | 34 | |
| 35 | 35 | s: [4]u32, |
| 36 | 36 | // Streaming Cache |
std/crypto/poly1305.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | // |
| 3 | 3 | // https://monocypher.org/ |
| 4 | 4 | |
| 5 | const std = @import("../index.zig"); | |
| 5 | const std = @import("../std.zig"); | |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | |
| 8 | 8 | const Endian = builtin.Endian; |
std/crypto/sha1.zig+4-4| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | |
| 2 | const math = @import("../math.zig"); | |
| 3 | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | |
| 4 | const debug = @import("../debug.zig"); | |
| 5 | 5 | const builtin = @import("builtin"); |
| 6 | 6 | |
| 7 | 7 | const RoundParam = struct { |
| ... | ... | @@ -26,8 +26,8 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, e: usize, i: u32) RoundParam { |
| 26 | 26 | |
| 27 | 27 | pub const Sha1 = struct { |
| 28 | 28 | const Self = @This(); |
| 29 | const block_length = 64; | |
| 30 | const digest_length = 20; | |
| 29 | pub const block_length = 64; | |
| 30 | pub const digest_length = 20; | |
| 31 | 31 | |
| 32 | 32 | s: [5]u32, |
| 33 | 33 | // Streaming Cache |
std/crypto/sha2.zig+4-4| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | |
| 2 | const math = @import("../math.zig"); | |
| 3 | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | |
| 4 | const debug = @import("../debug.zig"); | |
| 5 | 5 | const builtin = @import("builtin"); |
| 6 | 6 | const htest = @import("test.zig"); |
| 7 | 7 | |
| ... | ... | @@ -78,8 +78,8 @@ pub const Sha256 = Sha2_32(Sha256Params); |
| 78 | 78 | fn Sha2_32(comptime params: Sha2Params32) type { |
| 79 | 79 | return struct { |
| 80 | 80 | const Self = @This(); |
| 81 | const block_length = 64; | |
| 82 | const digest_length = params.out_len / 8; | |
| 81 | pub const block_length = 64; | |
| 82 | pub const digest_length = params.out_len / 8; | |
| 83 | 83 | |
| 84 | 84 | s: [8]u32, |
| 85 | 85 | // Streaming Cache |
std/crypto/sha3.zig+4-4| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const mem = @import("../mem.zig"); |
| 2 | const math = @import("../math/index.zig"); | |
| 2 | const math = @import("../math.zig"); | |
| 3 | 3 | const endian = @import("../endian.zig"); |
| 4 | const debug = @import("../debug/index.zig"); | |
| 4 | const debug = @import("../debug.zig"); | |
| 5 | 5 | const builtin = @import("builtin"); |
| 6 | 6 | const htest = @import("test.zig"); |
| 7 | 7 | |
| ... | ... | @@ -13,8 +13,8 @@ pub const Sha3_512 = Keccak(512, 0x06); |
| 13 | 13 | fn Keccak(comptime bits: usize, comptime delim: u8) type { |
| 14 | 14 | return struct { |
| 15 | 15 | const Self = @This(); |
| 16 | const block_length = 200; | |
| 17 | const digest_length = bits / 8; | |
| 16 | pub const block_length = 200; | |
| 17 | pub const digest_length = bits / 8; | |
| 18 | 18 | |
| 19 | 19 | s: [200]u8, |
| 20 | 20 | offset: usize, |
std/crypto/test.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const fmt = std.fmt; |
std/crypto/throughput_test.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ const builtin = @import("builtin"); |
| 2 | 2 | const std = @import("std"); |
| 3 | 3 | const time = std.os.time; |
| 4 | 4 | const Timer = time.Timer; |
| 5 | const crypto = @import("index.zig"); | |
| 5 | const crypto = @import("../crypto.zig"); | |
| 6 | 6 | |
| 7 | 7 | const KiB = 1024; |
| 8 | 8 | const MiB = 1024 * KiB; |
std/crypto/x25519.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | // |
| 3 | 3 | // https://monocypher.org/ |
| 4 | 4 | |
| 5 | const std = @import("../index.zig"); | |
| 5 | const std = @import("../std.zig"); | |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | 7 | const fmt = std.fmt; |
| 8 | 8 |
std/cstr.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const debug = std.debug; |
| 4 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | |
| 4 | 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 = @ptrToInt(@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 = @ptrToInt(@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 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const Os = builtin.Os; |
| 3 | 3 | |
| 4 | const std = @import("index.zig"); | |
| 4 | const std = @import("std.zig"); | |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | const cstr = std.cstr; |
| 7 | 7 | const os = std.os; |
std/elf.zig+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | |
| 2 | const std = @import("std.zig"); | |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const os = std.os; |
| 5 | 5 | const math = std.math; |
std/event/channel.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const testing = std.testing; |
std/event/fs.zig+3-10| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("../index.zig"); | |
| 2 | const std = @import("../std.zig"); | |
| 3 | 3 | const event = std.event; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | 5 | const testing = std.testing; |
| ... | ... | @@ -404,11 +404,7 @@ pub async fn openPosix( |
| 404 | 404 | |
| 405 | 405 | pub async fn openRead(loop: *Loop, path: []const u8) os.File.OpenError!os.FileHandle { |
| 406 | 406 | switch (builtin.os) { |
| 407 | builtin.Os.macosx, | |
| 408 | builtin.Os.linux, | |
| 409 | builtin.Os.freebsd, | |
| 410 | builtin.Os.netbsd | |
| 411 | => { | |
| 407 | builtin.Os.macosx, builtin.Os.linux, builtin.Os.freebsd, builtin.Os.netbsd => { | |
| 412 | 408 | const flags = posix.O_LARGEFILE | posix.O_RDONLY | posix.O_CLOEXEC; |
| 413 | 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 | 872 | |
| 877 | 873 | pub async fn addFile(self: *Self, file_path: []const u8, value: V) !?V { |
| 878 | 874 | switch (builtin.os) { |
| 879 | builtin.Os.macosx, | |
| 880 | builtin.Os.freebsd, | |
| 881 | builtin.Os.netbsd | |
| 882 | => return await (async addFileKEvent(self, file_path, value) catch unreachable), | |
| 875 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => return await (async addFileKEvent(self, file_path, value) catch unreachable), | |
| 883 | 876 | builtin.Os.linux => return await (async addFileLinux(self, file_path, value) catch unreachable), |
| 884 | 877 | builtin.Os.windows => return await (async addFileWindows(self, file_path, value) catch unreachable), |
| 885 | 878 | else => @compileError("Unsupported OS"), |
std/event/future.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const builtin = @import("builtin"); |
std/event/group.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Lock = std.event.Lock; |
| 4 | 4 | const Loop = std.event.Loop; |
std/event/io.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Allocator = std.mem.Allocator; |
| 4 | 4 | const assert = std.debug.assert; |
std/event/lock.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const testing = std.testing; |
std/event/locked.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const Lock = std.event.Lock; |
| 3 | 3 | const Loop = std.event.Loop; |
| 4 | 4 |
std/event/loop.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const testing = std.testing; |
std/event/net.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const event = std.event; |
std/event/rwlock.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const testing = std.testing; |
std/event/rwlocked.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const RwLock = std.event.RwLock; |
| 3 | 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 | 29 | // - Only supports round-to-zero |
| 30 | 30 | // - Does not handle denormals |
| 31 | 31 | |
| 32 | const std = @import("../index.zig"); | |
| 32 | const std = @import("../std.zig"); | |
| 33 | 33 | |
| 34 | 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 | 3 | // https://tools.ietf.org/html/rfc1950#section-9 |
| 4 | 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 | 7 | const testing = std.testing; |
| 8 | 8 | |
| 9 | 9 | pub const Adler32 = struct { |
std/hash/crc.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is |
| 6 | 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 | 9 | const debug = std.debug; |
| 10 | 10 | const testing = std.testing; |
| 11 | 11 |
std/hash/fnv.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // |
| 5 | 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 | 8 | const testing = std.testing; |
| 9 | 9 | |
| 10 | 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 | 5 | // |
| 6 | 6 | // https://131002.net/siphash/ |
| 7 | 7 | |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const assert = std.debug.assert; |
| 10 | 10 | const testing = std.testing; |
| 11 | 11 | const math = std.math; |
std/hash_map.zig+1-3| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 4 | const testing = std.testing; |
| ... | ... | @@ -486,7 +486,6 @@ pub fn autoHash(key: var, comptime rng: *std.rand.Random, comptime HashInt: type |
| 486 | 486 | builtin.TypeId.ErrorSet => return autoHash(@errorToInt(key), rng), |
| 487 | 487 | builtin.TypeId.Promise, builtin.TypeId.Fn => return autoHash(@ptrToInt(key), rng), |
| 488 | 488 | |
| 489 | builtin.TypeId.Namespace, | |
| 490 | 489 | builtin.TypeId.BoundFn, |
| 491 | 490 | builtin.TypeId.ComptimeFloat, |
| 492 | 491 | builtin.TypeId.ComptimeInt, |
| ... | ... | @@ -532,7 +531,6 @@ pub fn autoEql(a: var, b: @typeOf(a)) bool { |
| 532 | 531 | builtin.TypeId.Float, |
| 533 | 532 | builtin.TypeId.ComptimeFloat, |
| 534 | 533 | builtin.TypeId.ComptimeInt, |
| 535 | builtin.TypeId.Namespace, | |
| 536 | 534 | builtin.TypeId.Promise, |
| 537 | 535 | builtin.TypeId.Enum, |
| 538 | 536 | builtin.TypeId.BoundFn, |
std/heap.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 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 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Os = builtin.Os; |
| 4 | 4 | const c = std.c; |
std/io/seekable_stream.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const InStream = std.io.InStream; |
| 3 | 3 | |
| 4 | 4 | pub fn SeekableStream(comptime SeekErrorType: type, comptime GetSeekPosErrorType: type) type { |
std/io_test.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const io = std.io; |
| 3 | 3 | const meta = std.meta; |
| 4 | 4 | const trait = std.trait; |
std/json.zig+1-1| ... | ... | @@ -2,7 +2,7 @@ |
| 2 | 2 | // |
| 3 | 3 | // https://tools.ietf.org/html/rfc8259 |
| 4 | 4 | |
| 5 | const std = @import("index.zig"); | |
| 5 | const std = @import("std.zig"); | |
| 6 | 6 | const debug = std.debug; |
| 7 | 7 | const testing = std.testing; |
| 8 | 8 | const mem = std.mem; |
std/json_test.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // Tests are taken from https://github.com/nst/JSONTestSuite |
| 4 | 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 | 8 | fn ok(comptime s: []const u8) void { |
| 9 | 9 | std.testing.expect(std.json.validate(s)); |
std/lazy_init.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const testing = std.testing; |
std/linked_list.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 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 | 2 | // |
| 3 | 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 | 6 | const math = std.math; |
| 7 | 7 | const expect = std.testing.expect; |
| 8 | 8 |
std/math/acosh.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - acosh(nan) = nan |
| 5 | 5 | |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/asin.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // - asin(+-0) = +-0 |
| 4 | 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 | 7 | const math = std.math; |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | 9 |
std/math/asinh.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - asinh(+-inf) = +-inf |
| 5 | 5 | // - asinh(nan) = nan |
| 6 | 6 | |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 | const maxInt = std.math.maxInt; |
std/math/atan.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // - atan(+-0) = +-0 |
| 4 | 4 | // - atan(+-inf) = +-pi/2 |
| 5 | 5 | |
| 6 | const std = @import("../index.zig"); | |
| 6 | const std = @import("../std.zig"); | |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | 9 |
std/math/atan2.zig+1-1| ... | ... | @@ -18,7 +18,7 @@ |
| 18 | 18 | // atan2(+inf, x) = +pi/2 |
| 19 | 19 | // atan2(-inf, x) = -pi/2 |
| 20 | 20 | |
| 21 | const std = @import("../index.zig"); | |
| 21 | const std = @import("../std.zig"); | |
| 22 | 22 | const math = std.math; |
| 23 | 23 | const expect = std.testing.expect; |
| 24 | 24 |
std/math/atanh.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - atanh(x) = nan if |x| > 1 with signal |
| 5 | 5 | // - atanh(nan) = nan |
| 6 | 6 | |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 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 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const debug = std.debug; |
| 4 | 4 | const testing = std.testing; |
std/math/cbrt.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - cbrt(+-inf) = +-inf |
| 5 | 5 | // - cbrt(nan) = nan |
| 6 | 6 | |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/ceil.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - ceil(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 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 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/acos.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/acosh.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/arg.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/asin.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/asinh.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/atan.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/atanh.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/conj.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/cos.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/cosh.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/exp.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 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 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/log.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/pow.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/proj.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/sin.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/sinh.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/sqrt.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/tan.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/complex/tanh.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const testing = std.testing; |
| 3 | 3 | const math = std.math; |
| 4 | 4 | const cmath = math.complex; |
std/math/copysign.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const maxInt = std.math.maxInt; |
std/math/cos.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - cos(nan) = nan |
| 5 | 5 | |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/cosh.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - cosh(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expo2 = @import("expo2.zig").expo2; |
| 11 | 11 | const expect = std.testing.expect; |
std/math/exp.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // - exp(+inf) = +inf |
| 4 | 4 | // - exp(nan) = nan |
| 5 | 5 | |
| 6 | const std = @import("../index.zig"); | |
| 6 | const std = @import("../std.zig"); | |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const assert = std.debug.assert; |
| 9 | 9 | const builtin = @import("builtin"); |
std/math/exp2.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // - exp2(+inf) = +inf |
| 4 | 4 | // - exp2(nan) = nan |
| 5 | 5 | |
| 6 | const std = @import("../index.zig"); | |
| 6 | const std = @import("../std.zig"); | |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | 9 |
std/math/expm1.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - expm1(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/expo2.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const math = @import("index.zig"); | |
| 1 | const math = @import("../math.zig"); | |
| 2 | 2 | |
| 3 | 3 | pub fn expo2(x: var) @typeOf(x) { |
| 4 | 4 | const T = @typeOf(x); |
std/math/fabs.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // - fabs(+-inf) = +inf |
| 4 | 4 | // - fabs(nan) = nan |
| 5 | 5 | |
| 6 | const std = @import("../index.zig"); | |
| 6 | const std = @import("../std.zig"); | |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | 9 | const maxInt = std.math.maxInt; |
std/math/floor.zig+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | const std = @import("../index.zig"); | |
| 9 | const std = @import("../std.zig"); | |
| 10 | 10 | const math = std.math; |
| 11 | 11 | |
| 12 | 12 | pub fn floor(x: var) @typeOf(x) { |
std/math/fma.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 |
std/math/frexp.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - frexp(+-inf) = +-inf, 0 |
| 5 | 5 | // - frexp(nan) = nan, undefined |
| 6 | 6 | |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 |
std/math/hypot.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - hypot(nan, y) = nan |
| 6 | 6 | // - hypot(x, nan) = nan |
| 7 | 7 | |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 | const maxInt = std.math.maxInt; |
std/math/ilogb.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - ilogb(0) = maxInt(i32) |
| 5 | 5 | // - ilogb(nan) = maxInt(i32) |
| 6 | 6 | |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 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 = comptime_int(info.Int.bits); // TODO #1683 | |
| 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 = comptime_int(info.Int.bits); // TODO #1683 | |
| 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | |
| 4 | 4 | pub fn inf(comptime T: type) T { |
std/math/isfinite.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const maxInt = std.math.maxInt; |
std/math/isinf.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const maxInt = std.math.maxInt; |
std/math/isnan.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const maxInt = std.math.maxInt; |
std/math/isnormal.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const maxInt = std.math.maxInt; |
std/math/ln.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - ln(x) = nan if x < 0 |
| 6 | 6 | // - ln(nan) = nan |
| 7 | 7 | |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 | const builtin = @import("builtin"); |
std/math/log.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const builtin = @import("builtin"); |
| 4 | 4 | const TypeId = builtin.TypeId; |
std/math/log10.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - log10(x) = nan if x < 0 |
| 6 | 6 | // - log10(nan) = nan |
| 7 | 7 | |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const testing = std.testing; |
| 11 | 11 | const builtin = @import("builtin"); |
std/math/log1p.zig+1-1| ... | ... | @@ -7,7 +7,7 @@ |
| 7 | 7 | // - log1p(nan) = nan |
| 8 | 8 | |
| 9 | 9 | const builtin = @import("builtin"); |
| 10 | const std = @import("../index.zig"); | |
| 10 | const std = @import("../std.zig"); | |
| 11 | 11 | const math = std.math; |
| 12 | 12 | const expect = std.testing.expect; |
| 13 | 13 |
std/math/log2.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - log2(x) = nan if x < 0 |
| 6 | 6 | // - log2(nan) = nan |
| 7 | 7 | |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 | const builtin = @import("builtin"); |
std/math/modf.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ |
| 3 | 3 | // - modf(+-inf) = +-inf, nan |
| 4 | 4 | // - modf(nan) = nan, nan |
| 5 | 5 | |
| 6 | const std = @import("../index.zig"); | |
| 6 | const std = @import("../std.zig"); | |
| 7 | 7 | const math = std.math; |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | 9 | const maxInt = std.math.maxInt; |
std/math/nan.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const math = @import("index.zig"); | |
| 1 | const math = @import("../math.zig"); | |
| 2 | 2 | |
| 3 | 3 | pub fn nan(comptime T: type) T { |
| 4 | 4 | return switch (T) { |
std/math/pow.zig+1-1| ... | ... | @@ -22,7 +22,7 @@ |
| 22 | 22 | // pow(x, y) = nan for finite x < 0 and finite non-integer y |
| 23 | 23 | |
| 24 | 24 | const builtin = @import("builtin"); |
| 25 | const std = @import("../index.zig"); | |
| 25 | const std = @import("../std.zig"); | |
| 26 | 26 | const math = std.math; |
| 27 | 27 | const expect = std.testing.expect; |
| 28 | 28 |
std/math/powi.zig+1-1| ... | ... | @@ -9,7 +9,7 @@ |
| 9 | 9 | // powi(x, y) = Underflow for for y > @sizeOf(x) - 1 y < 0 |
| 10 | 10 | |
| 11 | 11 | const builtin = @import("builtin"); |
| 12 | const std = @import("../index.zig"); | |
| 12 | const std = @import("../std.zig"); | |
| 13 | 13 | const math = std.math; |
| 14 | 14 | const assert = std.debug.assert; |
| 15 | 15 | const testing = std.testing; |
std/math/round.zig+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | 8 | const expect = std.testing.expect; |
| 9 | const std = @import("../index.zig"); | |
| 9 | const std = @import("../std.zig"); | |
| 10 | 10 | const math = std.math; |
| 11 | 11 | |
| 12 | 12 | pub fn round(x: var) @typeOf(x) { |
std/math/scalbn.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 |
std/math/signbit.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const math = std.math; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 |
std/math/sin.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - sin(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/sinh.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - sinh(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 | const expo2 = @import("expo2.zig").expo2; |
std/math/sqrt.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - sqrt(x) = nan if x < 0 |
| 6 | 6 | // - sqrt(nan) = nan |
| 7 | 7 | |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 | const builtin = @import("builtin"); |
std/math/tan.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - tan(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 |
std/math/tanh.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | // - sinh(nan) = nan |
| 6 | 6 | |
| 7 | 7 | const builtin = @import("builtin"); |
| 8 | const std = @import("../index.zig"); | |
| 8 | const std = @import("../std.zig"); | |
| 9 | 9 | const math = std.math; |
| 10 | 10 | const expect = std.testing.expect; |
| 11 | 11 | const expo2 = @import("expo2.zig").expo2; |
std/math/trunc.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | // - trunc(+-inf) = +-inf |
| 5 | 5 | // - trunc(nan) = nan |
| 6 | 6 | |
| 7 | const std = @import("../index.zig"); | |
| 7 | const std = @import("../std.zig"); | |
| 8 | 8 | const math = std.math; |
| 9 | 9 | const expect = std.testing.expect; |
| 10 | 10 | const maxInt = std.math.maxInt; |
std/mem.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const debug = std.debug; |
| 3 | 3 | const assert = debug.assert; |
| 4 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const debug = std.debug; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | const warn = debug.warn; |
| 7 | 7 | |
| 8 | const meta = @import("index.zig"); | |
| 8 | const meta = @import("../meta.zig"); | |
| 9 | 9 | |
| 10 | 10 | //This is necessary if we want to return generic functions directly because of how the |
| 11 | 11 | // the type erasure works. see: #1375 |
std/mutex.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
std/net.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const cstr = std.cstr; |
| 3 | 3 | const unicode = std.unicode; |
| 4 | 4 | const io = std.io; |
std/os/darwin.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const c = std.c; |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const maxInt = std.math.maxInt; |
std/os/file.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const os = std.os; |
| 4 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const unicode = std.unicode; |
| 4 | 4 | const mem = std.mem; |
std/os/get_user_id.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const Os = builtin.Os; |
| 3 | const os = @import("index.zig"); | |
| 3 | const os = @import("../os.zig"); | |
| 4 | 4 | const io = @import("../io.zig"); |
| 5 | 5 | |
| 6 | 6 | pub const UserInfo = struct { |
std/os/index.zig deleted-3408| ... | ... | @@ -1,3408 +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 | } |
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 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const linux = std.os.linux; |
| 3 | 3 | const socklen_t = linux.socklen_t; |
| 4 | 4 | const iovec = linux.iovec; |
std/os/linux/index.zig deleted-1537| ... | ... | @@ -1,1537 +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 munmap(address: usize, length: usize) usize { | |
| 810 | return syscall2(SYS_munmap, address, length); | |
| 811 | } | |
| 812 | ||
| 813 | pub fn read(fd: i32, buf: [*]u8, count: usize) usize { | |
| 814 | return syscall3(SYS_read, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | |
| 815 | } | |
| 816 | ||
| 817 | pub fn preadv(fd: i32, iov: [*]const iovec, count: usize, offset: u64) usize { | |
| 818 | return syscall4(SYS_preadv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); | |
| 819 | } | |
| 820 | ||
| 821 | pub fn readv(fd: i32, iov: [*]const iovec, count: usize) usize { | |
| 822 | return syscall3(SYS_readv, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); | |
| 823 | } | |
| 824 | ||
| 825 | pub fn writev(fd: i32, iov: [*]const iovec_const, count: usize) usize { | |
| 826 | return syscall3(SYS_writev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count); | |
| 827 | } | |
| 828 | ||
| 829 | pub fn pwritev(fd: i32, iov: [*]const iovec_const, count: usize, offset: u64) usize { | |
| 830 | return syscall4(SYS_pwritev, @bitCast(usize, isize(fd)), @ptrToInt(iov), count, offset); | |
| 831 | } | |
| 832 | ||
| 833 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 834 | pub fn rmdir(path: [*]const u8) usize { | |
| 835 | return unlinkat(AT_FDCWD, path, AT_REMOVEDIR); | |
| 836 | } | |
| 837 | ||
| 838 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 839 | pub fn symlink(existing: [*]const u8, new: [*]const u8) usize { | |
| 840 | return symlinkat(existing, AT_FDCWD, new); | |
| 841 | } | |
| 842 | ||
| 843 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 844 | pub fn symlinkat(existing: [*]const u8, newfd: i32, newpath: [*]const u8) usize { | |
| 845 | return syscall3(SYS_symlinkat, @ptrToInt(existing), @bitCast(usize, isize(newfd)), @ptrToInt(newpath)); | |
| 846 | } | |
| 847 | ||
| 848 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 849 | pub fn pread(fd: i32, buf: [*]u8, count: usize, offset: usize) usize { | |
| 850 | return syscall4(SYS_pread, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | |
| 851 | } | |
| 852 | ||
| 853 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 854 | pub fn access(path: [*]const u8, mode: u32) usize { | |
| 855 | return faccessat(AT_FDCWD, path, mode); | |
| 856 | } | |
| 857 | ||
| 858 | pub fn faccessat(dirfd: i32, path: [*]const u8, mode: u32) usize { | |
| 859 | return syscall3(SYS_faccessat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), mode); | |
| 860 | } | |
| 861 | ||
| 862 | pub fn pipe(fd: *[2]i32) usize { | |
| 863 | return pipe2(fd, 0); | |
| 864 | } | |
| 865 | ||
| 866 | pub fn pipe2(fd: *[2]i32, flags: u32) usize { | |
| 867 | return syscall2(SYS_pipe2, @ptrToInt(fd), flags); | |
| 868 | } | |
| 869 | ||
| 870 | pub fn write(fd: i32, buf: [*]const u8, count: usize) usize { | |
| 871 | return syscall3(SYS_write, @bitCast(usize, isize(fd)), @ptrToInt(buf), count); | |
| 872 | } | |
| 873 | ||
| 874 | pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: usize) usize { | |
| 875 | return syscall4(SYS_pwrite, @bitCast(usize, isize(fd)), @ptrToInt(buf), count, offset); | |
| 876 | } | |
| 877 | ||
| 878 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 879 | pub fn rename(old: [*]const u8, new: [*]const u8) usize { | |
| 880 | return renameat2(AT_FDCWD, old, AT_FDCWD, new, 0); | |
| 881 | } | |
| 882 | ||
| 883 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 884 | pub fn renameat2(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const u8, flags: u32) usize { | |
| 885 | return syscall5(SYS_renameat2, @bitCast(usize, isize(oldfd)), @ptrToInt(oldpath), @bitCast(usize, isize(newfd)), @ptrToInt(newpath), flags); | |
| 886 | } | |
| 887 | ||
| 888 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 889 | pub fn open(path: [*]const u8, flags: u32, perm: usize) usize { | |
| 890 | return openat(AT_FDCWD, path, flags, perm); | |
| 891 | } | |
| 892 | ||
| 893 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 894 | pub fn create(path: [*]const u8, perm: usize) usize { | |
| 895 | return syscall2(SYS_creat, @ptrToInt(path), perm); | |
| 896 | } | |
| 897 | ||
| 898 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 899 | pub fn openat(dirfd: i32, path: [*]const u8, flags: u32, mode: usize) usize { | |
| 900 | // dirfd could be negative, for example AT_FDCWD is -100 | |
| 901 | return syscall4(SYS_openat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags, mode); | |
| 902 | } | |
| 903 | ||
| 904 | /// See also `clone` (from the arch-specific include) | |
| 905 | pub fn clone5(flags: usize, child_stack_ptr: usize, parent_tid: *i32, child_tid: *i32, newtls: usize) usize { | |
| 906 | return syscall5(SYS_clone, flags, child_stack_ptr, @ptrToInt(parent_tid), @ptrToInt(child_tid), newtls); | |
| 907 | } | |
| 908 | ||
| 909 | /// See also `clone` (from the arch-specific include) | |
| 910 | pub fn clone2(flags: u32, child_stack_ptr: usize) usize { | |
| 911 | return syscall2(SYS_clone, flags, child_stack_ptr); | |
| 912 | } | |
| 913 | ||
| 914 | pub fn close(fd: i32) usize { | |
| 915 | return syscall1(SYS_close, @bitCast(usize, isize(fd))); | |
| 916 | } | |
| 917 | ||
| 918 | pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize { | |
| 919 | return syscall3(SYS_lseek, @bitCast(usize, isize(fd)), @bitCast(usize, offset), ref_pos); | |
| 920 | } | |
| 921 | ||
| 922 | pub fn exit(status: i32) noreturn { | |
| 923 | _ = syscall1(SYS_exit, @bitCast(usize, isize(status))); | |
| 924 | unreachable; | |
| 925 | } | |
| 926 | ||
| 927 | pub fn exit_group(status: i32) noreturn { | |
| 928 | _ = syscall1(SYS_exit_group, @bitCast(usize, isize(status))); | |
| 929 | unreachable; | |
| 930 | } | |
| 931 | ||
| 932 | pub fn getrandom(buf: [*]u8, count: usize, flags: u32) usize { | |
| 933 | return syscall3(SYS_getrandom, @ptrToInt(buf), count, flags); | |
| 934 | } | |
| 935 | ||
| 936 | pub fn kill(pid: i32, sig: i32) usize { | |
| 937 | return syscall2(SYS_kill, @bitCast(usize, isize(pid)), @bitCast(usize, isize(sig))); | |
| 938 | } | |
| 939 | ||
| 940 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 941 | pub fn unlink(path: [*]const u8) usize { | |
| 942 | return unlinkat(AT_FDCWD, path, 0); | |
| 943 | } | |
| 944 | ||
| 945 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 946 | pub fn unlinkat(dirfd: i32, path: [*]const u8, flags: u32) usize { | |
| 947 | return syscall3(SYS_unlinkat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), flags); | |
| 948 | } | |
| 949 | ||
| 950 | pub fn waitpid(pid: i32, status: *i32, options: i32) usize { | |
| 951 | return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0); | |
| 952 | } | |
| 953 | ||
| 954 | pub fn clock_gettime(clk_id: i32, tp: *timespec) usize { | |
| 955 | if (VDSO_CGT_SYM.len != 0) { | |
| 956 | const f = @atomicLoad(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, builtin.AtomicOrder.Unordered); | |
| 957 | if (@ptrToInt(f) != 0) { | |
| 958 | const rc = f(clk_id, tp); | |
| 959 | switch (rc) { | |
| 960 | 0, @bitCast(usize, isize(-EINVAL)) => return rc, | |
| 961 | else => {}, | |
| 962 | } | |
| 963 | } | |
| 964 | } | |
| 965 | return syscall2(SYS_clock_gettime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | |
| 966 | } | |
| 967 | var vdso_clock_gettime = init_vdso_clock_gettime; | |
| 968 | extern fn init_vdso_clock_gettime(clk: i32, ts: *timespec) usize { | |
| 969 | const addr = vdso.lookup(VDSO_CGT_VER, VDSO_CGT_SYM); | |
| 970 | var f = @intToPtr(@typeOf(init_vdso_clock_gettime), addr); | |
| 971 | _ = @cmpxchgStrong(@typeOf(init_vdso_clock_gettime), &vdso_clock_gettime, init_vdso_clock_gettime, f, builtin.AtomicOrder.Monotonic, builtin.AtomicOrder.Monotonic); | |
| 972 | if (@ptrToInt(f) == 0) return @bitCast(usize, isize(-ENOSYS)); | |
| 973 | return f(clk, ts); | |
| 974 | } | |
| 975 | ||
| 976 | pub fn clock_getres(clk_id: i32, tp: *timespec) usize { | |
| 977 | return syscall2(SYS_clock_getres, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | |
| 978 | } | |
| 979 | ||
| 980 | pub fn clock_settime(clk_id: i32, tp: *const timespec) usize { | |
| 981 | return syscall2(SYS_clock_settime, @bitCast(usize, isize(clk_id)), @ptrToInt(tp)); | |
| 982 | } | |
| 983 | ||
| 984 | pub fn gettimeofday(tv: *timeval, tz: *timezone) usize { | |
| 985 | return syscall2(SYS_gettimeofday, @ptrToInt(tv), @ptrToInt(tz)); | |
| 986 | } | |
| 987 | ||
| 988 | pub fn settimeofday(tv: *const timeval, tz: *const timezone) usize { | |
| 989 | return syscall2(SYS_settimeofday, @ptrToInt(tv), @ptrToInt(tz)); | |
| 990 | } | |
| 991 | ||
| 992 | pub fn nanosleep(req: *const timespec, rem: ?*timespec) usize { | |
| 993 | return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem)); | |
| 994 | } | |
| 995 | ||
| 996 | pub fn setuid(uid: u32) usize { | |
| 997 | return syscall1(SYS_setuid, uid); | |
| 998 | } | |
| 999 | ||
| 1000 | pub fn setgid(gid: u32) usize { | |
| 1001 | return syscall1(SYS_setgid, gid); | |
| 1002 | } | |
| 1003 | ||
| 1004 | pub fn setreuid(ruid: u32, euid: u32) usize { | |
| 1005 | return syscall2(SYS_setreuid, ruid, euid); | |
| 1006 | } | |
| 1007 | ||
| 1008 | pub fn setregid(rgid: u32, egid: u32) usize { | |
| 1009 | return syscall2(SYS_setregid, rgid, egid); | |
| 1010 | } | |
| 1011 | ||
| 1012 | pub fn getuid() u32 { | |
| 1013 | return u32(syscall0(SYS_getuid)); | |
| 1014 | } | |
| 1015 | ||
| 1016 | pub fn getgid() u32 { | |
| 1017 | return u32(syscall0(SYS_getgid)); | |
| 1018 | } | |
| 1019 | ||
| 1020 | pub fn geteuid() u32 { | |
| 1021 | return u32(syscall0(SYS_geteuid)); | |
| 1022 | } | |
| 1023 | ||
| 1024 | pub fn getegid() u32 { | |
| 1025 | return u32(syscall0(SYS_getegid)); | |
| 1026 | } | |
| 1027 | ||
| 1028 | pub fn seteuid(euid: u32) usize { | |
| 1029 | return syscall1(SYS_seteuid, euid); | |
| 1030 | } | |
| 1031 | ||
| 1032 | pub fn setegid(egid: u32) usize { | |
| 1033 | return syscall1(SYS_setegid, egid); | |
| 1034 | } | |
| 1035 | ||
| 1036 | pub fn getresuid(ruid: *u32, euid: *u32, suid: *u32) usize { | |
| 1037 | return syscall3(SYS_getresuid, @ptrToInt(ruid), @ptrToInt(euid), @ptrToInt(suid)); | |
| 1038 | } | |
| 1039 | ||
| 1040 | pub fn getresgid(rgid: *u32, egid: *u32, sgid: *u32) usize { | |
| 1041 | return syscall3(SYS_getresgid, @ptrToInt(rgid), @ptrToInt(egid), @ptrToInt(sgid)); | |
| 1042 | } | |
| 1043 | ||
| 1044 | pub fn setresuid(ruid: u32, euid: u32, suid: u32) usize { | |
| 1045 | return syscall3(SYS_setresuid, ruid, euid, suid); | |
| 1046 | } | |
| 1047 | ||
| 1048 | pub fn setresgid(rgid: u32, egid: u32, sgid: u32) usize { | |
| 1049 | return syscall3(SYS_setresgid, rgid, egid, sgid); | |
| 1050 | } | |
| 1051 | ||
| 1052 | pub fn getgroups(size: usize, list: *u32) usize { | |
| 1053 | return syscall2(SYS_getgroups, size, @ptrToInt(list)); | |
| 1054 | } | |
| 1055 | ||
| 1056 | pub fn setgroups(size: usize, list: *const u32) usize { | |
| 1057 | return syscall2(SYS_setgroups, size, @ptrToInt(list)); | |
| 1058 | } | |
| 1059 | ||
| 1060 | pub fn getpid() i32 { | |
| 1061 | return @bitCast(i32, @truncate(u32, syscall0(SYS_getpid))); | |
| 1062 | } | |
| 1063 | ||
| 1064 | pub fn gettid() i32 { | |
| 1065 | return @bitCast(i32, @truncate(u32, syscall0(SYS_gettid))); | |
| 1066 | } | |
| 1067 | ||
| 1068 | pub fn sigprocmask(flags: u32, noalias set: *const sigset_t, noalias oldset: ?*sigset_t) usize { | |
| 1069 | return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG / 8); | |
| 1070 | } | |
| 1071 | ||
| 1072 | pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigaction) usize { | |
| 1073 | assert(sig >= 1); | |
| 1074 | assert(sig != SIGKILL); | |
| 1075 | assert(sig != SIGSTOP); | |
| 1076 | var ksa = k_sigaction{ | |
| 1077 | .handler = act.handler, | |
| 1078 | .flags = act.flags | SA_RESTORER, | |
| 1079 | .mask = undefined, | |
| 1080 | .restorer = @ptrCast(extern fn () void, restore_rt), | |
| 1081 | }; | |
| 1082 | var ksa_old: k_sigaction = undefined; | |
| 1083 | @memcpy(@ptrCast([*]u8, &ksa.mask), @ptrCast([*]const u8, &act.mask), 8); | |
| 1084 | const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask))); | |
| 1085 | const err = getErrno(result); | |
| 1086 | if (err != 0) { | |
| 1087 | return result; | |
| 1088 | } | |
| 1089 | if (oact) |old| { | |
| 1090 | old.handler = ksa_old.handler; | |
| 1091 | old.flags = @truncate(u32, ksa_old.flags); | |
| 1092 | @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask))); | |
| 1093 | } | |
| 1094 | return 0; | |
| 1095 | } | |
| 1096 | ||
| 1097 | const NSIG = 65; | |
| 1098 | const sigset_t = [128 / @sizeOf(usize)]usize; | |
| 1099 | const all_mask = []usize{maxInt(usize)}; | |
| 1100 | const app_mask = []usize{0xfffffffc7fffffff}; | |
| 1101 | ||
| 1102 | const k_sigaction = extern struct { | |
| 1103 | handler: extern fn (i32) void, | |
| 1104 | flags: usize, | |
| 1105 | restorer: extern fn () void, | |
| 1106 | mask: [2]u32, | |
| 1107 | }; | |
| 1108 | ||
| 1109 | /// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. | |
| 1110 | pub const Sigaction = struct { | |
| 1111 | handler: extern fn (i32) void, | |
| 1112 | mask: sigset_t, | |
| 1113 | flags: u32, | |
| 1114 | }; | |
| 1115 | ||
| 1116 | pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize)); | |
| 1117 | pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0); | |
| 1118 | pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1); | |
| 1119 | pub const empty_sigset = []usize{0} ** sigset_t.len; | |
| 1120 | ||
| 1121 | pub fn raise(sig: i32) usize { | |
| 1122 | var set: sigset_t = undefined; | |
| 1123 | blockAppSignals(&set); | |
| 1124 | const tid = syscall0(SYS_gettid); | |
| 1125 | const ret = syscall2(SYS_tkill, tid, @bitCast(usize, isize(sig))); | |
| 1126 | restoreSignals(&set); | |
| 1127 | return ret; | |
| 1128 | } | |
| 1129 | ||
| 1130 | fn blockAllSignals(set: *sigset_t) void { | |
| 1131 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG / 8); | |
| 1132 | } | |
| 1133 | ||
| 1134 | fn blockAppSignals(set: *sigset_t) void { | |
| 1135 | _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG / 8); | |
| 1136 | } | |
| 1137 | ||
| 1138 | fn restoreSignals(set: *sigset_t) void { | |
| 1139 | _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG / 8); | |
| 1140 | } | |
| 1141 | ||
| 1142 | pub fn sigaddset(set: *sigset_t, sig: u6) void { | |
| 1143 | const s = sig - 1; | |
| 1144 | (set.*)[@intCast(usize, s) / usize.bit_count] |= @intCast(usize, 1) << (s & (usize.bit_count - 1)); | |
| 1145 | } | |
| 1146 | ||
| 1147 | pub fn sigismember(set: *const sigset_t, sig: u6) bool { | |
| 1148 | const s = sig - 1; | |
| 1149 | return ((set.*)[@intCast(usize, s) / usize.bit_count] & (@intCast(usize, 1) << (s & (usize.bit_count - 1)))) != 0; | |
| 1150 | } | |
| 1151 | ||
| 1152 | pub const in_port_t = u16; | |
| 1153 | pub const sa_family_t = u16; | |
| 1154 | pub const socklen_t = u32; | |
| 1155 | ||
| 1156 | /// This intentionally only has ip4 and ip6 | |
| 1157 | pub const sockaddr = extern union { | |
| 1158 | in: sockaddr_in, | |
| 1159 | in6: sockaddr_in6, | |
| 1160 | }; | |
| 1161 | ||
| 1162 | pub const sockaddr_in = extern struct { | |
| 1163 | family: sa_family_t, | |
| 1164 | port: in_port_t, | |
| 1165 | addr: u32, | |
| 1166 | zero: [8]u8, | |
| 1167 | }; | |
| 1168 | ||
| 1169 | pub const sockaddr_in6 = extern struct { | |
| 1170 | family: sa_family_t, | |
| 1171 | port: in_port_t, | |
| 1172 | flowinfo: u32, | |
| 1173 | addr: [16]u8, | |
| 1174 | scope_id: u32, | |
| 1175 | }; | |
| 1176 | ||
| 1177 | pub const sockaddr_un = extern struct { | |
| 1178 | family: sa_family_t, | |
| 1179 | path: [108]u8, | |
| 1180 | }; | |
| 1181 | ||
| 1182 | pub const iovec = extern struct { | |
| 1183 | iov_base: [*]u8, | |
| 1184 | iov_len: usize, | |
| 1185 | }; | |
| 1186 | ||
| 1187 | pub const iovec_const = extern struct { | |
| 1188 | iov_base: [*]const u8, | |
| 1189 | iov_len: usize, | |
| 1190 | }; | |
| 1191 | ||
| 1192 | pub fn getsockname(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | |
| 1193 | return syscall3(SYS_getsockname, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); | |
| 1194 | } | |
| 1195 | ||
| 1196 | pub fn getpeername(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | |
| 1197 | return syscall3(SYS_getpeername, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len)); | |
| 1198 | } | |
| 1199 | ||
| 1200 | pub fn socket(domain: u32, socket_type: u32, protocol: u32) usize { | |
| 1201 | return syscall3(SYS_socket, domain, socket_type, protocol); | |
| 1202 | } | |
| 1203 | ||
| 1204 | pub fn setsockopt(fd: i32, level: u32, optname: u32, optval: [*]const u8, optlen: socklen_t) usize { | |
| 1205 | return syscall5(SYS_setsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @intCast(usize, optlen)); | |
| 1206 | } | |
| 1207 | ||
| 1208 | pub fn getsockopt(fd: i32, level: u32, optname: u32, noalias optval: [*]u8, noalias optlen: *socklen_t) usize { | |
| 1209 | return syscall5(SYS_getsockopt, @bitCast(usize, isize(fd)), level, optname, @ptrToInt(optval), @ptrToInt(optlen)); | |
| 1210 | } | |
| 1211 | ||
| 1212 | pub fn sendmsg(fd: i32, msg: *const msghdr, flags: u32) usize { | |
| 1213 | return syscall3(SYS_sendmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); | |
| 1214 | } | |
| 1215 | ||
| 1216 | pub fn connect(fd: i32, addr: *const c_void, len: socklen_t) usize { | |
| 1217 | return syscall3(SYS_connect, @bitCast(usize, isize(fd)), @ptrToInt(addr), len); | |
| 1218 | } | |
| 1219 | ||
| 1220 | pub fn recvmsg(fd: i32, msg: *msghdr, flags: u32) usize { | |
| 1221 | return syscall3(SYS_recvmsg, @bitCast(usize, isize(fd)), @ptrToInt(msg), flags); | |
| 1222 | } | |
| 1223 | ||
| 1224 | pub fn recvfrom(fd: i32, noalias buf: [*]u8, len: usize, flags: u32, noalias addr: ?*sockaddr, noalias alen: ?*socklen_t) usize { | |
| 1225 | return syscall6(SYS_recvfrom, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen)); | |
| 1226 | } | |
| 1227 | ||
| 1228 | pub fn shutdown(fd: i32, how: i32) usize { | |
| 1229 | return syscall2(SYS_shutdown, @bitCast(usize, isize(fd)), @bitCast(usize, isize(how))); | |
| 1230 | } | |
| 1231 | ||
| 1232 | pub fn bind(fd: i32, addr: *const sockaddr, len: socklen_t) usize { | |
| 1233 | return syscall3(SYS_bind, @bitCast(usize, isize(fd)), @ptrToInt(addr), @intCast(usize, len)); | |
| 1234 | } | |
| 1235 | ||
| 1236 | pub fn listen(fd: i32, backlog: u32) usize { | |
| 1237 | return syscall2(SYS_listen, @bitCast(usize, isize(fd)), backlog); | |
| 1238 | } | |
| 1239 | ||
| 1240 | pub fn sendto(fd: i32, buf: [*]const u8, len: usize, flags: u32, addr: ?*const sockaddr, alen: socklen_t) usize { | |
| 1241 | return syscall6(SYS_sendto, @bitCast(usize, isize(fd)), @ptrToInt(buf), len, flags, @ptrToInt(addr), @intCast(usize, alen)); | |
| 1242 | } | |
| 1243 | ||
| 1244 | pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) usize { | |
| 1245 | return syscall4(SYS_socketpair, @intCast(usize, domain), @intCast(usize, socket_type), @intCast(usize, protocol), @ptrToInt(&fd[0])); | |
| 1246 | } | |
| 1247 | ||
| 1248 | pub fn accept(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t) usize { | |
| 1249 | return accept4(fd, addr, len, 0); | |
| 1250 | } | |
| 1251 | ||
| 1252 | pub fn accept4(fd: i32, noalias addr: *sockaddr, noalias len: *socklen_t, flags: u32) usize { | |
| 1253 | return syscall4(SYS_accept4, @bitCast(usize, isize(fd)), @ptrToInt(addr), @ptrToInt(len), flags); | |
| 1254 | } | |
| 1255 | ||
| 1256 | pub fn fstat(fd: i32, stat_buf: *Stat) usize { | |
| 1257 | return syscall2(SYS_fstat, @bitCast(usize, isize(fd)), @ptrToInt(stat_buf)); | |
| 1258 | } | |
| 1259 | ||
| 1260 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1261 | pub fn stat(pathname: [*]const u8, statbuf: *Stat) usize { | |
| 1262 | return fstatat(AT_FDCWD, pathname, statbuf, AT_NO_AUTOMOUNT); | |
| 1263 | } | |
| 1264 | ||
| 1265 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1266 | pub fn lstat(pathname: [*]const u8, statbuf: *Stat) usize { | |
| 1267 | return fstatat(AF_FDCWD, pathname, statbuf, AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT); | |
| 1268 | } | |
| 1269 | ||
| 1270 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1271 | pub fn fstatat(dirfd: i32, path: [*]const u8, stat_buf: *Stat, flags: u32) usize { | |
| 1272 | return syscall4(SYS_fstatat, @bitCast(usize, isize(dirfd)), @ptrToInt(path), @ptrToInt(stat_buf), flags); | |
| 1273 | } | |
| 1274 | ||
| 1275 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1276 | pub fn listxattr(path: [*]const u8, list: [*]u8, size: usize) usize { | |
| 1277 | return syscall3(SYS_listxattr, @ptrToInt(path), @ptrToInt(list), size); | |
| 1278 | } | |
| 1279 | ||
| 1280 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1281 | pub fn llistxattr(path: [*]const u8, list: [*]u8, size: usize) usize { | |
| 1282 | return syscall3(SYS_llistxattr, @ptrToInt(path), @ptrToInt(list), size); | |
| 1283 | } | |
| 1284 | ||
| 1285 | pub fn flistxattr(fd: usize, list: [*]u8, size: usize) usize { | |
| 1286 | return syscall3(SYS_flistxattr, fd, @ptrToInt(list), size); | |
| 1287 | } | |
| 1288 | ||
| 1289 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1290 | pub fn getxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { | |
| 1291 | return syscall4(SYS_getxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); | |
| 1292 | } | |
| 1293 | ||
| 1294 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1295 | pub fn lgetxattr(path: [*]const u8, name: [*]const u8, value: [*]u8, size: usize) usize { | |
| 1296 | return syscall4(SYS_lgetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size); | |
| 1297 | } | |
| 1298 | ||
| 1299 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1300 | pub fn fgetxattr(fd: usize, name: [*]const u8, value: [*]u8, size: usize) usize { | |
| 1301 | return syscall4(SYS_lgetxattr, fd, @ptrToInt(name), @ptrToInt(value), size); | |
| 1302 | } | |
| 1303 | ||
| 1304 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1305 | pub fn setxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | |
| 1306 | return syscall5(SYS_setxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); | |
| 1307 | } | |
| 1308 | ||
| 1309 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1310 | pub fn lsetxattr(path: [*]const u8, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | |
| 1311 | return syscall5(SYS_lsetxattr, @ptrToInt(path), @ptrToInt(name), @ptrToInt(value), size, flags); | |
| 1312 | } | |
| 1313 | ||
| 1314 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1315 | pub fn fsetxattr(fd: usize, name: [*]const u8, value: *const void, size: usize, flags: usize) usize { | |
| 1316 | return syscall5(SYS_fsetxattr, fd, @ptrToInt(name), @ptrToInt(value), size, flags); | |
| 1317 | } | |
| 1318 | ||
| 1319 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1320 | pub fn removexattr(path: [*]const u8, name: [*]const u8) usize { | |
| 1321 | return syscall2(SYS_removexattr, @ptrToInt(path), @ptrToInt(name)); | |
| 1322 | } | |
| 1323 | ||
| 1324 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1325 | pub fn lremovexattr(path: [*]const u8, name: [*]const u8) usize { | |
| 1326 | return syscall2(SYS_lremovexattr, @ptrToInt(path), @ptrToInt(name)); | |
| 1327 | } | |
| 1328 | ||
| 1329 | // TODO https://github.com/ziglang/zig/issues/265 | |
| 1330 | pub fn fremovexattr(fd: usize, name: [*]const u8) usize { | |
| 1331 | return syscall2(SYS_fremovexattr, fd, @ptrToInt(name)); | |
| 1332 | } | |
| 1333 | ||
| 1334 | pub fn sched_getaffinity(pid: i32, set: []usize) usize { | |
| 1335 | return syscall3(SYS_sched_getaffinity, @bitCast(usize, isize(pid)), set.len * @sizeOf(usize), @ptrToInt(set.ptr)); | |
| 1336 | } | |
| 1337 | ||
| 1338 | pub const epoll_data = packed union { | |
| 1339 | ptr: usize, | |
| 1340 | fd: i32, | |
| 1341 | @"u32": u32, | |
| 1342 | @"u64": u64, | |
| 1343 | }; | |
| 1344 | ||
| 1345 | pub const epoll_event = packed struct { | |
| 1346 | events: u32, | |
| 1347 | data: epoll_data, | |
| 1348 | }; | |
| 1349 | ||
| 1350 | pub fn epoll_create() usize { | |
| 1351 | return epoll_create1(0); | |
| 1352 | } | |
| 1353 | ||
| 1354 | pub fn epoll_create1(flags: usize) usize { | |
| 1355 | return syscall1(SYS_epoll_create1, flags); | |
| 1356 | } | |
| 1357 | ||
| 1358 | pub fn epoll_ctl(epoll_fd: i32, op: u32, fd: i32, ev: *epoll_event) usize { | |
| 1359 | return syscall4(SYS_epoll_ctl, @bitCast(usize, isize(epoll_fd)), @intCast(usize, op), @bitCast(usize, isize(fd)), @ptrToInt(ev)); | |
| 1360 | } | |
| 1361 | ||
| 1362 | pub fn epoll_wait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32) usize { | |
| 1363 | return epoll_pwait(epoll_fd, events, maxevents, timeout, null); | |
| 1364 | } | |
| 1365 | ||
| 1366 | pub fn epoll_pwait(epoll_fd: i32, events: [*]epoll_event, maxevents: u32, timeout: i32, sigmask: ?*sigset_t) usize { | |
| 1367 | return syscall6( | |
| 1368 | SYS_epoll_pwait, | |
| 1369 | @bitCast(usize, isize(epoll_fd)), | |
| 1370 | @ptrToInt(events), | |
| 1371 | @intCast(usize, maxevents), | |
| 1372 | @bitCast(usize, isize(timeout)), | |
| 1373 | @ptrToInt(sigmask), | |
| 1374 | @sizeOf(sigset_t), | |
| 1375 | ); | |
| 1376 | } | |
| 1377 | ||
| 1378 | pub fn eventfd(count: u32, flags: u32) usize { | |
| 1379 | return syscall2(SYS_eventfd2, count, flags); | |
| 1380 | } | |
| 1381 | ||
| 1382 | pub fn timerfd_create(clockid: i32, flags: u32) usize { | |
| 1383 | return syscall2(SYS_timerfd_create, @bitCast(usize, isize(clockid)), flags); | |
| 1384 | } | |
| 1385 | ||
| 1386 | pub const itimerspec = extern struct { | |
| 1387 | it_interval: timespec, | |
| 1388 | it_value: timespec, | |
| 1389 | }; | |
| 1390 | ||
| 1391 | pub fn timerfd_gettime(fd: i32, curr_value: *itimerspec) usize { | |
| 1392 | return syscall2(SYS_timerfd_gettime, @bitCast(usize, isize(fd)), @ptrToInt(curr_value)); | |
| 1393 | } | |
| 1394 | ||
| 1395 | pub fn timerfd_settime(fd: i32, flags: u32, new_value: *const itimerspec, old_value: ?*itimerspec) usize { | |
| 1396 | return syscall4(SYS_timerfd_settime, @bitCast(usize, isize(fd)), flags, @ptrToInt(new_value), @ptrToInt(old_value)); | |
| 1397 | } | |
| 1398 | ||
| 1399 | pub const _LINUX_CAPABILITY_VERSION_1 = 0x19980330; | |
| 1400 | pub const _LINUX_CAPABILITY_U32S_1 = 1; | |
| 1401 | ||
| 1402 | pub const _LINUX_CAPABILITY_VERSION_2 = 0x20071026; | |
| 1403 | pub const _LINUX_CAPABILITY_U32S_2 = 2; | |
| 1404 | ||
| 1405 | pub const _LINUX_CAPABILITY_VERSION_3 = 0x20080522; | |
| 1406 | pub const _LINUX_CAPABILITY_U32S_3 = 2; | |
| 1407 | ||
| 1408 | pub const VFS_CAP_REVISION_MASK = 0xFF000000; | |
| 1409 | pub const VFS_CAP_REVISION_SHIFT = 24; | |
| 1410 | pub const VFS_CAP_FLAGS_MASK = ~VFS_CAP_REVISION_MASK; | |
| 1411 | pub const VFS_CAP_FLAGS_EFFECTIVE = 0x000001; | |
| 1412 | ||
| 1413 | pub const VFS_CAP_REVISION_1 = 0x01000000; | |
| 1414 | pub const VFS_CAP_U32_1 = 1; | |
| 1415 | pub const XATTR_CAPS_SZ_1 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_1); | |
| 1416 | ||
| 1417 | pub const VFS_CAP_REVISION_2 = 0x02000000; | |
| 1418 | pub const VFS_CAP_U32_2 = 2; | |
| 1419 | pub const XATTR_CAPS_SZ_2 = @sizeOf(u32) * (1 + 2 * VFS_CAP_U32_2); | |
| 1420 | ||
| 1421 | pub const XATTR_CAPS_SZ = XATTR_CAPS_SZ_2; | |
| 1422 | pub const VFS_CAP_U32 = VFS_CAP_U32_2; | |
| 1423 | pub const VFS_CAP_REVISION = VFS_CAP_REVISION_2; | |
| 1424 | ||
| 1425 | pub const vfs_cap_data = extern struct { | |
| 1426 | //all of these are mandated as little endian | |
| 1427 | //when on disk. | |
| 1428 | const Data = struct { | |
| 1429 | permitted: u32, | |
| 1430 | inheritable: u32, | |
| 1431 | }; | |
| 1432 | ||
| 1433 | magic_etc: u32, | |
| 1434 | data: [VFS_CAP_U32]Data, | |
| 1435 | }; | |
| 1436 | ||
| 1437 | pub const CAP_CHOWN = 0; | |
| 1438 | pub const CAP_DAC_OVERRIDE = 1; | |
| 1439 | pub const CAP_DAC_READ_SEARCH = 2; | |
| 1440 | pub const CAP_FOWNER = 3; | |
| 1441 | pub const CAP_FSETID = 4; | |
| 1442 | pub const CAP_KILL = 5; | |
| 1443 | pub const CAP_SETGID = 6; | |
| 1444 | pub const CAP_SETUID = 7; | |
| 1445 | pub const CAP_SETPCAP = 8; | |
| 1446 | pub const CAP_LINUX_IMMUTABLE = 9; | |
| 1447 | pub const CAP_NET_BIND_SERVICE = 10; | |
| 1448 | pub const CAP_NET_BROADCAST = 11; | |
| 1449 | pub const CAP_NET_ADMIN = 12; | |
| 1450 | pub const CAP_NET_RAW = 13; | |
| 1451 | pub const CAP_IPC_LOCK = 14; | |
| 1452 | pub const CAP_IPC_OWNER = 15; | |
| 1453 | pub const CAP_SYS_MODULE = 16; | |
| 1454 | pub const CAP_SYS_RAWIO = 17; | |
| 1455 | pub const CAP_SYS_CHROOT = 18; | |
| 1456 | pub const CAP_SYS_PTRACE = 19; | |
| 1457 | pub const CAP_SYS_PACCT = 20; | |
| 1458 | pub const CAP_SYS_ADMIN = 21; | |
| 1459 | pub const CAP_SYS_BOOT = 22; | |
| 1460 | pub const CAP_SYS_NICE = 23; | |
| 1461 | pub const CAP_SYS_RESOURCE = 24; | |
| 1462 | pub const CAP_SYS_TIME = 25; | |
| 1463 | pub const CAP_SYS_TTY_CONFIG = 26; | |
| 1464 | pub const CAP_MKNOD = 27; | |
| 1465 | pub const CAP_LEASE = 28; | |
| 1466 | pub const CAP_AUDIT_WRITE = 29; | |
| 1467 | pub const CAP_AUDIT_CONTROL = 30; | |
| 1468 | pub const CAP_SETFCAP = 31; | |
| 1469 | pub const CAP_MAC_OVERRIDE = 32; | |
| 1470 | pub const CAP_MAC_ADMIN = 33; | |
| 1471 | pub const CAP_SYSLOG = 34; | |
| 1472 | pub const CAP_WAKE_ALARM = 35; | |
| 1473 | pub const CAP_BLOCK_SUSPEND = 36; | |
| 1474 | pub const CAP_AUDIT_READ = 37; | |
| 1475 | pub const CAP_LAST_CAP = CAP_AUDIT_READ; | |
| 1476 | ||
| 1477 | pub fn cap_valid(u8: x) bool { | |
| 1478 | return x >= 0 and x <= CAP_LAST_CAP; | |
| 1479 | } | |
| 1480 | ||
| 1481 | pub fn CAP_TO_MASK(cap: u8) u32 { | |
| 1482 | return u32(1) << u5(cap & 31); | |
| 1483 | } | |
| 1484 | ||
| 1485 | pub fn CAP_TO_INDEX(cap: u8) u8 { | |
| 1486 | return cap >> 5; | |
| 1487 | } | |
| 1488 | ||
| 1489 | pub const cap_t = extern struct { | |
| 1490 | hdrp: *cap_user_header_t, | |
| 1491 | datap: *cap_user_data_t, | |
| 1492 | }; | |
| 1493 | ||
| 1494 | pub const cap_user_header_t = extern struct { | |
| 1495 | version: u32, | |
| 1496 | pid: usize, | |
| 1497 | }; | |
| 1498 | ||
| 1499 | pub const cap_user_data_t = extern struct { | |
| 1500 | effective: u32, | |
| 1501 | permitted: u32, | |
| 1502 | inheritable: u32, | |
| 1503 | }; | |
| 1504 | ||
| 1505 | pub fn unshare(flags: usize) usize { | |
| 1506 | return syscall1(SYS_unshare, flags); | |
| 1507 | } | |
| 1508 | ||
| 1509 | pub fn capget(hdrp: *cap_user_header_t, datap: *cap_user_data_t) usize { | |
| 1510 | return syscall2(SYS_capget, @ptrToInt(hdrp), @ptrToInt(datap)); | |
| 1511 | } | |
| 1512 | ||
| 1513 | pub fn capset(hdrp: *cap_user_header_t, datap: *const cap_user_data_t) usize { | |
| 1514 | return syscall2(SYS_capset, @ptrToInt(hdrp), @ptrToInt(datap)); | |
| 1515 | } | |
| 1516 | ||
| 1517 | pub const inotify_event = extern struct { | |
| 1518 | wd: i32, | |
| 1519 | mask: u32, | |
| 1520 | cookie: u32, | |
| 1521 | len: u32, | |
| 1522 | //name: [?]u8, | |
| 1523 | }; | |
| 1524 | ||
| 1525 | pub const dirent64 = extern struct { | |
| 1526 | d_ino: u64, | |
| 1527 | d_off: u64, | |
| 1528 | d_reclen: u16, | |
| 1529 | d_type: u8, | |
| 1530 | d_name: u8, // field address is the address of first byte of name https://github.com/ziglang/zig/issues/173 | |
| 1531 | }; | |
| 1532 | ||
| 1533 | test "import" { | |
| 1534 | if (builtin.os == builtin.Os.linux) { | |
| 1535 | _ = @import("test.zig"); | |
| 1536 | } | |
| 1537 | } |
std/os/linux/test.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const expect = std.testing.expect; |
std/os/linux/vdso.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const elf = std.elf; |
| 3 | 3 | const linux = std.os.linux; |
| 4 | 4 | const cstr = std.cstr; |
std/os/linux/x86_64.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const linux = std.os.linux; |
| 3 | 3 | const socklen_t = linux.socklen_t; |
| 4 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Os = builtin.Os; |
| 4 | 4 | const debug = std.debug; |
std/os/test.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const os = std.os; |
| 3 | 3 | const expect = std.testing.expect; |
| 4 | 4 | const io = std.io; |
std/os/time.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const Os = builtin.Os; |
| 4 | 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 | use @import("index.zig"); | |
| 1 | use @import("../windows.zig"); | |
| 2 | 2 | |
| 3 | 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 | use @import("index.zig"); | |
| 1 | use @import("../windows.zig"); | |
| 2 | 2 | |
| 3 | 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 | 262 | |
| 263 | 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 | 267 | pub const RTL_RUN_ONCE = extern struct { |
| 268 | 268 | Ptr: ?*c_void, |
| 269 | 269 | }; |
| 270 | 270 | |
| 271 | pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE { | |
| 272 | .Ptr = null, | |
| 273 | }; | |
| 271 | pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null }; |
std/os/windows/ntdll.zig+1-1| ... | ... | @@ -1,3 +1,3 @@ |
| 1 | use @import("index.zig"); | |
| 1 | use @import("../windows.zig"); | |
| 2 | 2 | |
| 3 | 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 | use @import("index.zig"); | |
| 1 | use @import("../windows.zig"); | |
| 2 | 2 | |
| 3 | 3 | pub extern "ole32" stdcallcc fn CoTaskMemFree(pv: LPVOID) void; |
| 4 | 4 | pub extern "ole32" stdcallcc fn CoUninitialize() void; |
std/os/windows/shell32.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | use @import("index.zig"); | |
| 1 | use @import("../windows.zig"); | |
| 2 | 2 | |
| 3 | 3 | pub extern "shell32" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT; |
| 4 | 4 |
std/os/windows/tls.zig deleted-36| ... | ... | @@ -1,36 +0,0 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 2 | ||
| 3 | export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; | |
| 4 | export var _tls_start: u8 linksection(".tls") = 0; | |
| 5 | export var _tls_end: u8 linksection(".tls$ZZZ") = 0; | |
| 6 | export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; | |
| 7 | export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; | |
| 8 | ||
| 9 | // TODO this is how I would like it to be expressed | |
| 10 | // TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate | |
| 11 | // why they do that. | |
| 12 | //export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY { | |
| 13 | // .StartAddressOfRawData = @ptrToInt(&_tls_start), | |
| 14 | // .EndAddressOfRawData = @ptrToInt(&_tls_end), | |
| 15 | // .AddressOfIndex = @ptrToInt(&_tls_index), | |
| 16 | // .AddressOfCallBacks = @ptrToInt(__xl_a), | |
| 17 | // .SizeOfZeroFill = 0, | |
| 18 | // .Characteristics = 0, | |
| 19 | //}; | |
| 20 | // This is the workaround because we can't do @ptrToInt at comptime like that. | |
| 21 | pub const IMAGE_TLS_DIRECTORY = extern struct { | |
| 22 | StartAddressOfRawData: *c_void, | |
| 23 | EndAddressOfRawData: *c_void, | |
| 24 | AddressOfIndex: *c_void, | |
| 25 | AddressOfCallBacks: *c_void, | |
| 26 | SizeOfZeroFill: u32, | |
| 27 | Characteristics: u32, | |
| 28 | }; | |
| 29 | export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY { | |
| 30 | .StartAddressOfRawData = &_tls_start, | |
| 31 | .EndAddressOfRawData = &_tls_end, | |
| 32 | .AddressOfIndex = &_tls_index, | |
| 33 | .AddressOfCallBacks = &__xl_a, | |
| 34 | .SizeOfZeroFill = 0, | |
| 35 | .Characteristics = 0, | |
| 36 | }; |
std/os/windows/util.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../../index.zig"); | |
| 1 | const std = @import("../../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const os = std.os; |
| 4 | 4 | const unicode = std.unicode; |
std/os/zen.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | |
| 4 | 4 | ////////////////////////// |
std/pdb.zig+2-4| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("index.zig"); | |
| 2 | const std = @import("std.zig"); | |
| 3 | 3 | const io = std.io; |
| 4 | 4 | const math = std.math; |
| 5 | 5 | const mem = std.mem; |
| ... | ... | @@ -12,7 +12,6 @@ const ArrayList = std.ArrayList; |
| 12 | 12 | // Note: most of this is based on information gathered from LLVM source code, |
| 13 | 13 | // documentation and/or contributors. |
| 14 | 14 | |
| 15 | ||
| 16 | 15 | // https://llvm.org/docs/PDB/DbiStream.html#stream-header |
| 17 | 16 | pub const DbiStreamHeader = packed struct { |
| 18 | 17 | VersionSignature: i32, |
| ... | ... | @@ -393,10 +392,9 @@ pub const LineNumberEntry = packed struct { |
| 393 | 392 | Flags: u32, |
| 394 | 393 | |
| 395 | 394 | /// TODO runtime crash when I make the actual type of Flags this |
| 396 | const Flags = packed struct { | |
| 395 | pub const Flags = packed struct { | |
| 397 | 396 | /// Start line number |
| 398 | 397 | Start: u24, |
| 399 | ||
| 400 | 398 | /// Delta of lines to the end of the expression. Still unclear. |
| 401 | 399 | // TODO figure out the point of this field. |
| 402 | 400 | End: u7, |
std/priority_queue.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | const debug = std.debug; |
| 4 | 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 | 8 | // NOTE: This seems interesting but reference code is a bit hard to grok: |
| 9 | 9 | // https://sbarral.github.io/etf. |
| 10 | 10 | |
| 11 | const std = @import("../index.zig"); | |
| 11 | const std = @import("../std.zig"); | |
| 12 | 12 | const math = std.math; |
| 13 | 13 | const Random = std.rand.Random; |
| 14 | 14 |
std/rb.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const mem = std.mem; // For mem.Compare |
std/segmented_list.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
std/sort.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const mem = std.mem; |
std/special/bootstrap.zig+1-1| ... | ... | @@ -46,7 +46,7 @@ nakedcc fn _start() noreturn { |
| 46 | 46 | extern fn WinMainCRTStartup() noreturn { |
| 47 | 47 | @setAlignStack(16); |
| 48 | 48 | if (!builtin.single_threaded) { |
| 49 | _ = @import("../os/windows/tls.zig"); | |
| 49 | _ = @import("bootstrap_windows_tls.zig"); | |
| 50 | 50 | } |
| 51 | 51 | std.os.windows.ExitProcess(callMain()); |
| 52 | 52 | } |
std/special/bootstrap_windows_tls.zig created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | const std = @import("std"); | |
| 2 | ||
| 3 | export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES; | |
| 4 | export var _tls_start: u8 linksection(".tls") = 0; | |
| 5 | export var _tls_end: u8 linksection(".tls$ZZZ") = 0; | |
| 6 | export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null; | |
| 7 | export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null; | |
| 8 | ||
| 9 | // TODO this is how I would like it to be expressed | |
| 10 | // TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate | |
| 11 | // why they do that. | |
| 12 | //export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY { | |
| 13 | // .StartAddressOfRawData = @ptrToInt(&_tls_start), | |
| 14 | // .EndAddressOfRawData = @ptrToInt(&_tls_end), | |
| 15 | // .AddressOfIndex = @ptrToInt(&_tls_index), | |
| 16 | // .AddressOfCallBacks = @ptrToInt(__xl_a), | |
| 17 | // .SizeOfZeroFill = 0, | |
| 18 | // .Characteristics = 0, | |
| 19 | //}; | |
| 20 | // This is the workaround because we can't do @ptrToInt at comptime like that. | |
| 21 | pub const IMAGE_TLS_DIRECTORY = extern struct { | |
| 22 | StartAddressOfRawData: *c_void, | |
| 23 | EndAddressOfRawData: *c_void, | |
| 24 | AddressOfIndex: *c_void, | |
| 25 | AddressOfCallBacks: *c_void, | |
| 26 | SizeOfZeroFill: u32, | |
| 27 | Characteristics: u32, | |
| 28 | }; | |
| 29 | export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY{ | |
| 30 | .StartAddressOfRawData = &_tls_start, | |
| 31 | .EndAddressOfRawData = &_tls_end, | |
| 32 | .AddressOfIndex = &_tls_index, | |
| 33 | .AddressOfCallBacks = &__xl_a, | |
| 34 | .SizeOfZeroFill = 0, | |
| 35 | .Characteristics = 0, | |
| 36 | }; |
std/special/builtin.zig+1-1| ... | ... | @@ -134,7 +134,7 @@ nakedcc fn clone() void { |
| 134 | 134 | } |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | const math = @import("../math/index.zig"); | |
| 137 | const math = std.math; | |
| 138 | 138 | |
| 139 | 139 | export fn fmodf(x: f32, y: f32) f32 { |
| 140 | 140 | return generic_fmod(f32, x, y); |
std/special/compiler_rt.zig created+1038| ... | ... | @@ -0,0 +1,1038 @@ |
| 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.aarch64, | |
| 169 | builtin.Arch.aarch64_be, | |
| 170 | => true, | |
| 171 | else => false, | |
| 172 | }; | |
| 173 | ||
| 174 | const is_arm_arch = switch (builtin.arch) { | |
| 175 | builtin.Arch.arm, | |
| 176 | builtin.Arch.armeb, | |
| 177 | builtin.Arch.aarch64, | |
| 178 | builtin.Arch.aarch64_be, | |
| 179 | builtin.Arch.thumb, | |
| 180 | builtin.Arch.thumbeb, | |
| 181 | => true, | |
| 182 | else => false, | |
| 183 | }; | |
| 184 | ||
| 185 | nakedcc fn __aeabi_uidivmod() void { | |
| 186 | @setRuntimeSafety(false); | |
| 187 | asm volatile ( | |
| 188 | \\ push { lr } | |
| 189 | \\ sub sp, sp, #4 | |
| 190 | \\ mov r2, sp | |
| 191 | \\ bl __udivmodsi4 | |
| 192 | \\ ldr r1, [sp] | |
| 193 | \\ add sp, sp, #4 | |
| 194 | \\ pop { pc } | |
| 195 | : | |
| 196 | : | |
| 197 | : "r2", "r1" | |
| 198 | ); | |
| 199 | } | |
| 200 | ||
| 201 | // _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments, | |
| 202 | // then decrement %esp by %eax. Preserves all registers except %esp and flags. | |
| 203 | // This routine is windows specific | |
| 204 | // http://msdn.microsoft.com/en-us/library/ms648426.aspx | |
| 205 | nakedcc fn _chkstk() align(4) void { | |
| 206 | @setRuntimeSafety(false); | |
| 207 | ||
| 208 | asm volatile ( | |
| 209 | \\ push %%ecx | |
| 210 | \\ push %%eax | |
| 211 | \\ cmp $0x1000,%%eax | |
| 212 | \\ lea 12(%%esp),%%ecx | |
| 213 | \\ jb 1f | |
| 214 | \\ 2: | |
| 215 | \\ sub $0x1000,%%ecx | |
| 216 | \\ test %%ecx,(%%ecx) | |
| 217 | \\ sub $0x1000,%%eax | |
| 218 | \\ cmp $0x1000,%%eax | |
| 219 | \\ ja 2b | |
| 220 | \\ 1: | |
| 221 | \\ sub %%eax,%%ecx | |
| 222 | \\ test %%ecx,(%%ecx) | |
| 223 | \\ pop %%eax | |
| 224 | \\ pop %%ecx | |
| 225 | \\ ret | |
| 226 | ); | |
| 227 | } | |
| 228 | ||
| 229 | nakedcc fn __chkstk() align(4) void { | |
| 230 | @setRuntimeSafety(false); | |
| 231 | ||
| 232 | asm volatile ( | |
| 233 | \\ push %%rcx | |
| 234 | \\ push %%rax | |
| 235 | \\ cmp $0x1000,%%rax | |
| 236 | \\ lea 24(%%rsp),%%rcx | |
| 237 | \\ jb 1f | |
| 238 | \\2: | |
| 239 | \\ sub $0x1000,%%rcx | |
| 240 | \\ test %%rcx,(%%rcx) | |
| 241 | \\ sub $0x1000,%%rax | |
| 242 | \\ cmp $0x1000,%%rax | |
| 243 | \\ ja 2b | |
| 244 | \\1: | |
| 245 | \\ sub %%rax,%%rcx | |
| 246 | \\ test %%rcx,(%%rcx) | |
| 247 | \\ pop %%rax | |
| 248 | \\ pop %%rcx | |
| 249 | \\ ret | |
| 250 | ); | |
| 251 | } | |
| 252 | ||
| 253 | // _chkstk routine | |
| 254 | // This routine is windows specific | |
| 255 | // http://msdn.microsoft.com/en-us/library/ms648426.aspx | |
| 256 | nakedcc fn __chkstk_ms() align(4) void { | |
| 257 | @setRuntimeSafety(false); | |
| 258 | ||
| 259 | asm volatile ( | |
| 260 | \\ push %%ecx | |
| 261 | \\ push %%eax | |
| 262 | \\ cmp $0x1000,%%eax | |
| 263 | \\ lea 12(%%esp),%%ecx | |
| 264 | \\ jb 1f | |
| 265 | \\ 2: | |
| 266 | \\ sub $0x1000,%%ecx | |
| 267 | \\ test %%ecx,(%%ecx) | |
| 268 | \\ sub $0x1000,%%eax | |
| 269 | \\ cmp $0x1000,%%eax | |
| 270 | \\ ja 2b | |
| 271 | \\ 1: | |
| 272 | \\ sub %%eax,%%ecx | |
| 273 | \\ test %%ecx,(%%ecx) | |
| 274 | \\ pop %%eax | |
| 275 | \\ pop %%ecx | |
| 276 | \\ ret | |
| 277 | ); | |
| 278 | } | |
| 279 | ||
| 280 | nakedcc fn ___chkstk_ms() align(4) void { | |
| 281 | @setRuntimeSafety(false); | |
| 282 | ||
| 283 | asm volatile ( | |
| 284 | \\ push %%rcx | |
| 285 | \\ push %%rax | |
| 286 | \\ cmp $0x1000,%%rax | |
| 287 | \\ lea 24(%%rsp),%%rcx | |
| 288 | \\ jb 1f | |
| 289 | \\2: | |
| 290 | \\ sub $0x1000,%%rcx | |
| 291 | \\ test %%rcx,(%%rcx) | |
| 292 | \\ sub $0x1000,%%rax | |
| 293 | \\ cmp $0x1000,%%rax | |
| 294 | \\ ja 2b | |
| 295 | \\1: | |
| 296 | \\ sub %%rax,%%rcx | |
| 297 | \\ test %%rcx,(%%rcx) | |
| 298 | \\ pop %%rax | |
| 299 | \\ pop %%rcx | |
| 300 | \\ ret | |
| 301 | ); | |
| 302 | } | |
| 303 | ||
| 304 | extern fn __udivmodsi4(a: u32, b: u32, rem: *u32) u32 { | |
| 305 | @setRuntimeSafety(is_test); | |
| 306 | ||
| 307 | const d = __udivsi3(a, b); | |
| 308 | rem.* = @bitCast(u32, @bitCast(i32, a) -% (@bitCast(i32, d) * @bitCast(i32, b))); | |
| 309 | return d; | |
| 310 | } | |
| 311 | ||
| 312 | extern fn __udivsi3(n: u32, d: u32) u32 { | |
| 313 | @setRuntimeSafety(is_test); | |
| 314 | ||
| 315 | const n_uword_bits: c_uint = u32.bit_count; | |
| 316 | // special cases | |
| 317 | if (d == 0) return 0; // ?! | |
| 318 | if (n == 0) return 0; | |
| 319 | var sr = @bitCast(c_uint, c_int(@clz(d)) - c_int(@clz(n))); | |
| 320 | // 0 <= sr <= n_uword_bits - 1 or sr large | |
| 321 | if (sr > n_uword_bits - 1) { | |
| 322 | // d > r | |
| 323 | return 0; | |
| 324 | } | |
| 325 | if (sr == n_uword_bits - 1) { | |
| 326 | // d == 1 | |
| 327 | return n; | |
| 328 | } | |
| 329 | sr += 1; | |
| 330 | // 1 <= sr <= n_uword_bits - 1 | |
| 331 | // Not a special case | |
| 332 | var q: u32 = n << @intCast(u5, n_uword_bits - sr); | |
| 333 | var r: u32 = n >> @intCast(u5, sr); | |
| 334 | var carry: u32 = 0; | |
| 335 | while (sr > 0) : (sr -= 1) { | |
| 336 | // r:q = ((r:q) << 1) | carry | |
| 337 | r = (r << 1) | (q >> @intCast(u5, n_uword_bits - 1)); | |
| 338 | q = (q << 1) | carry; | |
| 339 | // carry = 0; | |
| 340 | // if (r.all >= d.all) | |
| 341 | // { | |
| 342 | // r.all -= d.all; | |
| 343 | // carry = 1; | |
| 344 | // } | |
| 345 | const s = @intCast(i32, d -% r -% 1) >> @intCast(u5, n_uword_bits - 1); | |
| 346 | carry = @intCast(u32, s & 1); | |
| 347 | r -= d & @bitCast(u32, s); | |
| 348 | } | |
| 349 | q = (q << 1) | carry; | |
| 350 | return q; | |
| 351 | } | |
| 352 | ||
| 353 | test "test_umoddi3" { | |
| 354 | test_one_umoddi3(0, 1, 0); | |
| 355 | test_one_umoddi3(2, 1, 0); | |
| 356 | test_one_umoddi3(0x8000000000000000, 1, 0x0); | |
| 357 | test_one_umoddi3(0x8000000000000000, 2, 0x0); | |
| 358 | test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1); | |
| 359 | } | |
| 360 | ||
| 361 | fn test_one_umoddi3(a: u64, b: u64, expected_r: u64) void { | |
| 362 | const r = __umoddi3(a, b); | |
| 363 | testing.expect(r == expected_r); | |
| 364 | } | |
| 365 | ||
| 366 | test "test_udivsi3" { | |
| 367 | const cases = [][3]u32{ | |
| 368 | []u32{ | |
| 369 | 0x00000000, | |
| 370 | 0x00000001, | |
| 371 | 0x00000000, | |
| 372 | }, | |
| 373 | []u32{ | |
| 374 | 0x00000000, | |
| 375 | 0x00000002, | |
| 376 | 0x00000000, | |
| 377 | }, | |
| 378 | []u32{ | |
| 379 | 0x00000000, | |
| 380 | 0x00000003, | |
| 381 | 0x00000000, | |
| 382 | }, | |
| 383 | []u32{ | |
| 384 | 0x00000000, | |
| 385 | 0x00000010, | |
| 386 | 0x00000000, | |
| 387 | }, | |
| 388 | []u32{ | |
| 389 | 0x00000000, | |
| 390 | 0x078644FA, | |
| 391 | 0x00000000, | |
| 392 | }, | |
| 393 | []u32{ | |
| 394 | 0x00000000, | |
| 395 | 0x0747AE14, | |
| 396 | 0x00000000, | |
| 397 | }, | |
| 398 | []u32{ | |
| 399 | 0x00000000, | |
| 400 | 0x7FFFFFFF, | |
| 401 | 0x00000000, | |
| 402 | }, | |
| 403 | []u32{ | |
| 404 | 0x00000000, | |
| 405 | 0x80000000, | |
| 406 | 0x00000000, | |
| 407 | }, | |
| 408 | []u32{ | |
| 409 | 0x00000000, | |
| 410 | 0xFFFFFFFD, | |
| 411 | 0x00000000, | |
| 412 | }, | |
| 413 | []u32{ | |
| 414 | 0x00000000, | |
| 415 | 0xFFFFFFFE, | |
| 416 | 0x00000000, | |
| 417 | }, | |
| 418 | []u32{ | |
| 419 | 0x00000000, | |
| 420 | 0xFFFFFFFF, | |
| 421 | 0x00000000, | |
| 422 | }, | |
| 423 | []u32{ | |
| 424 | 0x00000001, | |
| 425 | 0x00000001, | |
| 426 | 0x00000001, | |
| 427 | }, | |
| 428 | []u32{ | |
| 429 | 0x00000001, | |
| 430 | 0x00000002, | |
| 431 | 0x00000000, | |
| 432 | }, | |
| 433 | []u32{ | |
| 434 | 0x00000001, | |
| 435 | 0x00000003, | |
| 436 | 0x00000000, | |
| 437 | }, | |
| 438 | []u32{ | |
| 439 | 0x00000001, | |
| 440 | 0x00000010, | |
| 441 | 0x00000000, | |
| 442 | }, | |
| 443 | []u32{ | |
| 444 | 0x00000001, | |
| 445 | 0x078644FA, | |
| 446 | 0x00000000, | |
| 447 | }, | |
| 448 | []u32{ | |
| 449 | 0x00000001, | |
| 450 | 0x0747AE14, | |
| 451 | 0x00000000, | |
| 452 | }, | |
| 453 | []u32{ | |
| 454 | 0x00000001, | |
| 455 | 0x7FFFFFFF, | |
| 456 | 0x00000000, | |
| 457 | }, | |
| 458 | []u32{ | |
| 459 | 0x00000001, | |
| 460 | 0x80000000, | |
| 461 | 0x00000000, | |
| 462 | }, | |
| 463 | []u32{ | |
| 464 | 0x00000001, | |
| 465 | 0xFFFFFFFD, | |
| 466 | 0x00000000, | |
| 467 | }, | |
| 468 | []u32{ | |
| 469 | 0x00000001, | |
| 470 | 0xFFFFFFFE, | |
| 471 | 0x00000000, | |
| 472 | }, | |
| 473 | []u32{ | |
| 474 | 0x00000001, | |
| 475 | 0xFFFFFFFF, | |
| 476 | 0x00000000, | |
| 477 | }, | |
| 478 | []u32{ | |
| 479 | 0x00000002, | |
| 480 | 0x00000001, | |
| 481 | 0x00000002, | |
| 482 | }, | |
| 483 | []u32{ | |
| 484 | 0x00000002, | |
| 485 | 0x00000002, | |
| 486 | 0x00000001, | |
| 487 | }, | |
| 488 | []u32{ | |
| 489 | 0x00000002, | |
| 490 | 0x00000003, | |
| 491 | 0x00000000, | |
| 492 | }, | |
| 493 | []u32{ | |
| 494 | 0x00000002, | |
| 495 | 0x00000010, | |
| 496 | 0x00000000, | |
| 497 | }, | |
| 498 | []u32{ | |
| 499 | 0x00000002, | |
| 500 | 0x078644FA, | |
| 501 | 0x00000000, | |
| 502 | }, | |
| 503 | []u32{ | |
| 504 | 0x00000002, | |
| 505 | 0x0747AE14, | |
| 506 | 0x00000000, | |
| 507 | }, | |
| 508 | []u32{ | |
| 509 | 0x00000002, | |
| 510 | 0x7FFFFFFF, | |
| 511 | 0x00000000, | |
| 512 | }, | |
| 513 | []u32{ | |
| 514 | 0x00000002, | |
| 515 | 0x80000000, | |
| 516 | 0x00000000, | |
| 517 | }, | |
| 518 | []u32{ | |
| 519 | 0x00000002, | |
| 520 | 0xFFFFFFFD, | |
| 521 | 0x00000000, | |
| 522 | }, | |
| 523 | []u32{ | |
| 524 | 0x00000002, | |
| 525 | 0xFFFFFFFE, | |
| 526 | 0x00000000, | |
| 527 | }, | |
| 528 | []u32{ | |
| 529 | 0x00000002, | |
| 530 | 0xFFFFFFFF, | |
| 531 | 0x00000000, | |
| 532 | }, | |
| 533 | []u32{ | |
| 534 | 0x00000003, | |
| 535 | 0x00000001, | |
| 536 | 0x00000003, | |
| 537 | }, | |
| 538 | []u32{ | |
| 539 | 0x00000003, | |
| 540 | 0x00000002, | |
| 541 | 0x00000001, | |
| 542 | }, | |
| 543 | []u32{ | |
| 544 | 0x00000003, | |
| 545 | 0x00000003, | |
| 546 | 0x00000001, | |
| 547 | }, | |
| 548 | []u32{ | |
| 549 | 0x00000003, | |
| 550 | 0x00000010, | |
| 551 | 0x00000000, | |
| 552 | }, | |
| 553 | []u32{ | |
| 554 | 0x00000003, | |
| 555 | 0x078644FA, | |
| 556 | 0x00000000, | |
| 557 | }, | |
| 558 | []u32{ | |
| 559 | 0x00000003, | |
| 560 | 0x0747AE14, | |
| 561 | 0x00000000, | |
| 562 | }, | |
| 563 | []u32{ | |
| 564 | 0x00000003, | |
| 565 | 0x7FFFFFFF, | |
| 566 | 0x00000000, | |
| 567 | }, | |
| 568 | []u32{ | |
| 569 | 0x00000003, | |
| 570 | 0x80000000, | |
| 571 | 0x00000000, | |
| 572 | }, | |
| 573 | []u32{ | |
| 574 | 0x00000003, | |
| 575 | 0xFFFFFFFD, | |
| 576 | 0x00000000, | |
| 577 | }, | |
| 578 | []u32{ | |
| 579 | 0x00000003, | |
| 580 | 0xFFFFFFFE, | |
| 581 | 0x00000000, | |
| 582 | }, | |
| 583 | []u32{ | |
| 584 | 0x00000003, | |
| 585 | 0xFFFFFFFF, | |
| 586 | 0x00000000, | |
| 587 | }, | |
| 588 | []u32{ | |
| 589 | 0x00000010, | |
| 590 | 0x00000001, | |
| 591 | 0x00000010, | |
| 592 | }, | |
| 593 | []u32{ | |
| 594 | 0x00000010, | |
| 595 | 0x00000002, | |
| 596 | 0x00000008, | |
| 597 | }, | |
| 598 | []u32{ | |
| 599 | 0x00000010, | |
| 600 | 0x00000003, | |
| 601 | 0x00000005, | |
| 602 | }, | |
| 603 | []u32{ | |
| 604 | 0x00000010, | |
| 605 | 0x00000010, | |
| 606 | 0x00000001, | |
| 607 | }, | |
| 608 | []u32{ | |
| 609 | 0x00000010, | |
| 610 | 0x078644FA, | |
| 611 | 0x00000000, | |
| 612 | }, | |
| 613 | []u32{ | |
| 614 | 0x00000010, | |
| 615 | 0x0747AE14, | |
| 616 | 0x00000000, | |
| 617 | }, | |
| 618 | []u32{ | |
| 619 | 0x00000010, | |
| 620 | 0x7FFFFFFF, | |
| 621 | 0x00000000, | |
| 622 | }, | |
| 623 | []u32{ | |
| 624 | 0x00000010, | |
| 625 | 0x80000000, | |
| 626 | 0x00000000, | |
| 627 | }, | |
| 628 | []u32{ | |
| 629 | 0x00000010, | |
| 630 | 0xFFFFFFFD, | |
| 631 | 0x00000000, | |
| 632 | }, | |
| 633 | []u32{ | |
| 634 | 0x00000010, | |
| 635 | 0xFFFFFFFE, | |
| 636 | 0x00000000, | |
| 637 | }, | |
| 638 | []u32{ | |
| 639 | 0x00000010, | |
| 640 | 0xFFFFFFFF, | |
| 641 | 0x00000000, | |
| 642 | }, | |
| 643 | []u32{ | |
| 644 | 0x078644FA, | |
| 645 | 0x00000001, | |
| 646 | 0x078644FA, | |
| 647 | }, | |
| 648 | []u32{ | |
| 649 | 0x078644FA, | |
| 650 | 0x00000002, | |
| 651 | 0x03C3227D, | |
| 652 | }, | |
| 653 | []u32{ | |
| 654 | 0x078644FA, | |
| 655 | 0x00000003, | |
| 656 | 0x028216FE, | |
| 657 | }, | |
| 658 | []u32{ | |
| 659 | 0x078644FA, | |
| 660 | 0x00000010, | |
| 661 | 0x0078644F, | |
| 662 | }, | |
| 663 | []u32{ | |
| 664 | 0x078644FA, | |
| 665 | 0x078644FA, | |
| 666 | 0x00000001, | |
| 667 | }, | |
| 668 | []u32{ | |
| 669 | 0x078644FA, | |
| 670 | 0x0747AE14, | |
| 671 | 0x00000001, | |
| 672 | }, | |
| 673 | []u32{ | |
| 674 | 0x078644FA, | |
| 675 | 0x7FFFFFFF, | |
| 676 | 0x00000000, | |
| 677 | }, | |
| 678 | []u32{ | |
| 679 | 0x078644FA, | |
| 680 | 0x80000000, | |
| 681 | 0x00000000, | |
| 682 | }, | |
| 683 | []u32{ | |
| 684 | 0x078644FA, | |
| 685 | 0xFFFFFFFD, | |
| 686 | 0x00000000, | |
| 687 | }, | |
| 688 | []u32{ | |
| 689 | 0x078644FA, | |
| 690 | 0xFFFFFFFE, | |
| 691 | 0x00000000, | |
| 692 | }, | |
| 693 | []u32{ | |
| 694 | 0x078644FA, | |
| 695 | 0xFFFFFFFF, | |
| 696 | 0x00000000, | |
| 697 | }, | |
| 698 | []u32{ | |
| 699 | 0x0747AE14, | |
| 700 | 0x00000001, | |
| 701 | 0x0747AE14, | |
| 702 | }, | |
| 703 | []u32{ | |
| 704 | 0x0747AE14, | |
| 705 | 0x00000002, | |
| 706 | 0x03A3D70A, | |
| 707 | }, | |
| 708 | []u32{ | |
| 709 | 0x0747AE14, | |
| 710 | 0x00000003, | |
| 711 | 0x026D3A06, | |
| 712 | }, | |
| 713 | []u32{ | |
| 714 | 0x0747AE14, | |
| 715 | 0x00000010, | |
| 716 | 0x00747AE1, | |
| 717 | }, | |
| 718 | []u32{ | |
| 719 | 0x0747AE14, | |
| 720 | 0x078644FA, | |
| 721 | 0x00000000, | |
| 722 | }, | |
| 723 | []u32{ | |
| 724 | 0x0747AE14, | |
| 725 | 0x0747AE14, | |
| 726 | 0x00000001, | |
| 727 | }, | |
| 728 | []u32{ | |
| 729 | 0x0747AE14, | |
| 730 | 0x7FFFFFFF, | |
| 731 | 0x00000000, | |
| 732 | }, | |
| 733 | []u32{ | |
| 734 | 0x0747AE14, | |
| 735 | 0x80000000, | |
| 736 | 0x00000000, | |
| 737 | }, | |
| 738 | []u32{ | |
| 739 | 0x0747AE14, | |
| 740 | 0xFFFFFFFD, | |
| 741 | 0x00000000, | |
| 742 | }, | |
| 743 | []u32{ | |
| 744 | 0x0747AE14, | |
| 745 | 0xFFFFFFFE, | |
| 746 | 0x00000000, | |
| 747 | }, | |
| 748 | []u32{ | |
| 749 | 0x0747AE14, | |
| 750 | 0xFFFFFFFF, | |
| 751 | 0x00000000, | |
| 752 | }, | |
| 753 | []u32{ | |
| 754 | 0x7FFFFFFF, | |
| 755 | 0x00000001, | |
| 756 | 0x7FFFFFFF, | |
| 757 | }, | |
| 758 | []u32{ | |
| 759 | 0x7FFFFFFF, | |
| 760 | 0x00000002, | |
| 761 | 0x3FFFFFFF, | |
| 762 | }, | |
| 763 | []u32{ | |
| 764 | 0x7FFFFFFF, | |
| 765 | 0x00000003, | |
| 766 | 0x2AAAAAAA, | |
| 767 | }, | |
| 768 | []u32{ | |
| 769 | 0x7FFFFFFF, | |
| 770 | 0x00000010, | |
| 771 | 0x07FFFFFF, | |
| 772 | }, | |
| 773 | []u32{ | |
| 774 | 0x7FFFFFFF, | |
| 775 | 0x078644FA, | |
| 776 | 0x00000011, | |
| 777 | }, | |
| 778 | []u32{ | |
| 779 | 0x7FFFFFFF, | |
| 780 | 0x0747AE14, | |
| 781 | 0x00000011, | |
| 782 | }, | |
| 783 | []u32{ | |
| 784 | 0x7FFFFFFF, | |
| 785 | 0x7FFFFFFF, | |
| 786 | 0x00000001, | |
| 787 | }, | |
| 788 | []u32{ | |
| 789 | 0x7FFFFFFF, | |
| 790 | 0x80000000, | |
| 791 | 0x00000000, | |
| 792 | }, | |
| 793 | []u32{ | |
| 794 | 0x7FFFFFFF, | |
| 795 | 0xFFFFFFFD, | |
| 796 | 0x00000000, | |
| 797 | }, | |
| 798 | []u32{ | |
| 799 | 0x7FFFFFFF, | |
| 800 | 0xFFFFFFFE, | |
| 801 | 0x00000000, | |
| 802 | }, | |
| 803 | []u32{ | |
| 804 | 0x7FFFFFFF, | |
| 805 | 0xFFFFFFFF, | |
| 806 | 0x00000000, | |
| 807 | }, | |
| 808 | []u32{ | |
| 809 | 0x80000000, | |
| 810 | 0x00000001, | |
| 811 | 0x80000000, | |
| 812 | }, | |
| 813 | []u32{ | |
| 814 | 0x80000000, | |
| 815 | 0x00000002, | |
| 816 | 0x40000000, | |
| 817 | }, | |
| 818 | []u32{ | |
| 819 | 0x80000000, | |
| 820 | 0x00000003, | |
| 821 | 0x2AAAAAAA, | |
| 822 | }, | |
| 823 | []u32{ | |
| 824 | 0x80000000, | |
| 825 | 0x00000010, | |
| 826 | 0x08000000, | |
| 827 | }, | |
| 828 | []u32{ | |
| 829 | 0x80000000, | |
| 830 | 0x078644FA, | |
| 831 | 0x00000011, | |
| 832 | }, | |
| 833 | []u32{ | |
| 834 | 0x80000000, | |
| 835 | 0x0747AE14, | |
| 836 | 0x00000011, | |
| 837 | }, | |
| 838 | []u32{ | |
| 839 | 0x80000000, | |
| 840 | 0x7FFFFFFF, | |
| 841 | 0x00000001, | |
| 842 | }, | |
| 843 | []u32{ | |
| 844 | 0x80000000, | |
| 845 | 0x80000000, | |
| 846 | 0x00000001, | |
| 847 | }, | |
| 848 | []u32{ | |
| 849 | 0x80000000, | |
| 850 | 0xFFFFFFFD, | |
| 851 | 0x00000000, | |
| 852 | }, | |
| 853 | []u32{ | |
| 854 | 0x80000000, | |
| 855 | 0xFFFFFFFE, | |
| 856 | 0x00000000, | |
| 857 | }, | |
| 858 | []u32{ | |
| 859 | 0x80000000, | |
| 860 | 0xFFFFFFFF, | |
| 861 | 0x00000000, | |
| 862 | }, | |
| 863 | []u32{ | |
| 864 | 0xFFFFFFFD, | |
| 865 | 0x00000001, | |
| 866 | 0xFFFFFFFD, | |
| 867 | }, | |
| 868 | []u32{ | |
| 869 | 0xFFFFFFFD, | |
| 870 | 0x00000002, | |
| 871 | 0x7FFFFFFE, | |
| 872 | }, | |
| 873 | []u32{ | |
| 874 | 0xFFFFFFFD, | |
| 875 | 0x00000003, | |
| 876 | 0x55555554, | |
| 877 | }, | |
| 878 | []u32{ | |
| 879 | 0xFFFFFFFD, | |
| 880 | 0x00000010, | |
| 881 | 0x0FFFFFFF, | |
| 882 | }, | |
| 883 | []u32{ | |
| 884 | 0xFFFFFFFD, | |
| 885 | 0x078644FA, | |
| 886 | 0x00000022, | |
| 887 | }, | |
| 888 | []u32{ | |
| 889 | 0xFFFFFFFD, | |
| 890 | 0x0747AE14, | |
| 891 | 0x00000023, | |
| 892 | }, | |
| 893 | []u32{ | |
| 894 | 0xFFFFFFFD, | |
| 895 | 0x7FFFFFFF, | |
| 896 | 0x00000001, | |
| 897 | }, | |
| 898 | []u32{ | |
| 899 | 0xFFFFFFFD, | |
| 900 | 0x80000000, | |
| 901 | 0x00000001, | |
| 902 | }, | |
| 903 | []u32{ | |
| 904 | 0xFFFFFFFD, | |
| 905 | 0xFFFFFFFD, | |
| 906 | 0x00000001, | |
| 907 | }, | |
| 908 | []u32{ | |
| 909 | 0xFFFFFFFD, | |
| 910 | 0xFFFFFFFE, | |
| 911 | 0x00000000, | |
| 912 | }, | |
| 913 | []u32{ | |
| 914 | 0xFFFFFFFD, | |
| 915 | 0xFFFFFFFF, | |
| 916 | 0x00000000, | |
| 917 | }, | |
| 918 | []u32{ | |
| 919 | 0xFFFFFFFE, | |
| 920 | 0x00000001, | |
| 921 | 0xFFFFFFFE, | |
| 922 | }, | |
| 923 | []u32{ | |
| 924 | 0xFFFFFFFE, | |
| 925 | 0x00000002, | |
| 926 | 0x7FFFFFFF, | |
| 927 | }, | |
| 928 | []u32{ | |
| 929 | 0xFFFFFFFE, | |
| 930 | 0x00000003, | |
| 931 | 0x55555554, | |
| 932 | }, | |
| 933 | []u32{ | |
| 934 | 0xFFFFFFFE, | |
| 935 | 0x00000010, | |
| 936 | 0x0FFFFFFF, | |
| 937 | }, | |
| 938 | []u32{ | |
| 939 | 0xFFFFFFFE, | |
| 940 | 0x078644FA, | |
| 941 | 0x00000022, | |
| 942 | }, | |
| 943 | []u32{ | |
| 944 | 0xFFFFFFFE, | |
| 945 | 0x0747AE14, | |
| 946 | 0x00000023, | |
| 947 | }, | |
| 948 | []u32{ | |
| 949 | 0xFFFFFFFE, | |
| 950 | 0x7FFFFFFF, | |
| 951 | 0x00000002, | |
| 952 | }, | |
| 953 | []u32{ | |
| 954 | 0xFFFFFFFE, | |
| 955 | 0x80000000, | |
| 956 | 0x00000001, | |
| 957 | }, | |
| 958 | []u32{ | |
| 959 | 0xFFFFFFFE, | |
| 960 | 0xFFFFFFFD, | |
| 961 | 0x00000001, | |
| 962 | }, | |
| 963 | []u32{ | |
| 964 | 0xFFFFFFFE, | |
| 965 | 0xFFFFFFFE, | |
| 966 | 0x00000001, | |
| 967 | }, | |
| 968 | []u32{ | |
| 969 | 0xFFFFFFFE, | |
| 970 | 0xFFFFFFFF, | |
| 971 | 0x00000000, | |
| 972 | }, | |
| 973 | []u32{ | |
| 974 | 0xFFFFFFFF, | |
| 975 | 0x00000001, | |
| 976 | 0xFFFFFFFF, | |
| 977 | }, | |
| 978 | []u32{ | |
| 979 | 0xFFFFFFFF, | |
| 980 | 0x00000002, | |
| 981 | 0x7FFFFFFF, | |
| 982 | }, | |
| 983 | []u32{ | |
| 984 | 0xFFFFFFFF, | |
| 985 | 0x00000003, | |
| 986 | 0x55555555, | |
| 987 | }, | |
| 988 | []u32{ | |
| 989 | 0xFFFFFFFF, | |
| 990 | 0x00000010, | |
| 991 | 0x0FFFFFFF, | |
| 992 | }, | |
| 993 | []u32{ | |
| 994 | 0xFFFFFFFF, | |
| 995 | 0x078644FA, | |
| 996 | 0x00000022, | |
| 997 | }, | |
| 998 | []u32{ | |
| 999 | 0xFFFFFFFF, | |
| 1000 | 0x0747AE14, | |
| 1001 | 0x00000023, | |
| 1002 | }, | |
| 1003 | []u32{ | |
| 1004 | 0xFFFFFFFF, | |
| 1005 | 0x7FFFFFFF, | |
| 1006 | 0x00000002, | |
| 1007 | }, | |
| 1008 | []u32{ | |
| 1009 | 0xFFFFFFFF, | |
| 1010 | 0x80000000, | |
| 1011 | 0x00000001, | |
| 1012 | }, | |
| 1013 | []u32{ | |
| 1014 | 0xFFFFFFFF, | |
| 1015 | 0xFFFFFFFD, | |
| 1016 | 0x00000001, | |
| 1017 | }, | |
| 1018 | []u32{ | |
| 1019 | 0xFFFFFFFF, | |
| 1020 | 0xFFFFFFFE, | |
| 1021 | 0x00000001, | |
| 1022 | }, | |
| 1023 | []u32{ | |
| 1024 | 0xFFFFFFFF, | |
| 1025 | 0xFFFFFFFF, | |
| 1026 | 0x00000001, | |
| 1027 | }, | |
| 1028 | }; | |
| 1029 | ||
| 1030 | for (cases) |case| { | |
| 1031 | test_one_udivsi3(case[0], case[1], case[2]); | |
| 1032 | } | |
| 1033 | } | |
| 1034 | ||
| 1035 | fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) void { | |
| 1036 | const q: u32 = __udivsi3(a, b); | |
| 1037 | testing.expect(q == expected_q); | |
| 1038 | } |
std/special/compiler_rt/addXf3.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | |
| 5 | 5 | const std = @import("std"); |
| 6 | 6 | const builtin = @import("builtin"); |
| 7 | const compiler_rt = @import("index.zig"); | |
| 7 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 8 | 8 | |
| 9 | 9 | pub extern fn __addtf3(a: f128, b: f128) f128 { |
| 10 | 10 | return addXf3(f128, a, b); |
std/special/compiler_rt/divti3.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | |
| 3 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 4 | 4 | |
| 5 | 5 | pub extern fn __divti3(a: i128, b: i128) i128 { |
| 6 | 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 | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | |
| 3 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 4 | 4 | |
| 5 | 5 | pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 { |
| 6 | 6 | @setRuntimeSafety(builtin.is_test); |
std/special/compiler_rt/multi3.zig+1-1| ... | ... | @@ -1,5 +1,5 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | const compiler_rt = @import("index.zig"); | |
| 2 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 3 | 3 | |
| 4 | 4 | // Ported from git@github.com:llvm-project/llvm-project-20170507.git |
| 5 | 5 | // ae684fad6d34858c014c94da69c15e7774a633c3 |
std/special/compiler_rt/udivmodti4.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const udivmod = @import("udivmod.zig").udivmod; |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | |
| 3 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 4 | 4 | |
| 5 | 5 | pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?*u128) u128 { |
| 6 | 6 | @setRuntimeSafety(builtin.is_test); |
std/special/compiler_rt/umodti3.zig+1-1| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const udivmodti4 = @import("udivmodti4.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | const compiler_rt = @import("index.zig"); | |
| 3 | const compiler_rt = @import("../compiler_rt.zig"); | |
| 4 | 4 | |
| 5 | 5 | pub extern fn __umodti3(a: u128, b: u128) u128 { |
| 6 | 6 | @setRuntimeSafety(builtin.is_test); |
std/special/panic.zig+1-1| ... | ... | @@ -18,7 +18,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn |
| 18 | 18 | std.os.abort(); |
| 19 | 19 | }, |
| 20 | 20 | else => { |
| 21 | const first_trace_addr = @ptrToInt(@returnAddress()); | |
| 21 | const first_trace_addr = @returnAddress(); | |
| 22 | 22 | std.debug.panicExtra(error_return_trace, first_trace_addr, "{}", msg); |
| 23 | 23 | }, |
| 24 | 24 | } |
std/spinlock.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
std/statically_initialized_mutex.zig+5-8| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("index.zig"); | |
| 1 | const std = @import("std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const AtomicOrder = builtin.AtomicOrder; |
| 4 | 4 | const AtomicRmwOp = builtin.AtomicRmwOp; |
| ... | ... | @@ -13,7 +13,7 @@ const windows = std.os.windows; |
| 13 | 13 | /// On Windows, this mutex allocates resources when it is |
| 14 | 14 | /// first used, and the resources cannot be freed. |
| 15 | 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 | 17 | builtin.Os.linux => std.Mutex, |
| 18 | 18 | builtin.Os.windows => struct { |
| 19 | 19 | lock: windows.CRITICAL_SECTION, |
| ... | ... | @@ -28,7 +28,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { |
| 28 | 28 | }; |
| 29 | 29 | |
| 30 | 30 | pub fn init() StaticallyInitializedMutex { |
| 31 | return StaticallyInitializedMutex { | |
| 31 | return StaticallyInitializedMutex{ | |
| 32 | 32 | .lock = undefined, |
| 33 | 33 | .init_once = windows.INIT_ONCE_STATIC_INIT, |
| 34 | 34 | }; |
| ... | ... | @@ -54,7 +54,7 @@ pub const StaticallyInitializedMutex = switch(builtin.os) { |
| 54 | 54 | pub fn acquire(self: *StaticallyInitializedMutex) Held { |
| 55 | 55 | assert(windows.InitOnceExecuteOnce(&self.init_once, initCriticalSection, &self.lock, null) != 0); |
| 56 | 56 | windows.EnterCriticalSection(&self.lock); |
| 57 | return Held { .mutex = self }; | |
| 57 | return Held{ .mutex = self }; | |
| 58 | 58 | } |
| 59 | 59 | }, |
| 60 | 60 | else => std.Mutex, |
| ... | ... | @@ -89,10 +89,7 @@ test "std.StaticallyInitializedMutex" { |
| 89 | 89 | var fixed_buffer_allocator = std.heap.ThreadSafeFixedBufferAllocator.init(plenty_of_memory); |
| 90 | 90 | var a = &fixed_buffer_allocator.allocator; |
| 91 | 91 | |
| 92 | ||
| 93 | var context = TestContext{ | |
| 94 | .data = 0, | |
| 95 | }; | |
| 92 | var context = TestContext{ .data = 0 }; | |
| 96 | 93 | |
| 97 | 94 | if (builtin.single_threaded) { |
| 98 | 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-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const builtin = @import("builtin"); |
| 2 | 2 | const TypeId = builtin.TypeId; |
| 3 | const std = @import("index.zig"); | |
| 3 | const std = @import("std.zig"); | |
| 4 | 4 | |
| 5 | 5 | /// This function is intended to be used only in tests. It prints diagnostics to stderr |
| 6 | 6 | /// and then aborts when actual_error_union is not expected_error. |
| ... | ... | @@ -44,7 +44,6 @@ pub fn expectEqual(expected: var, actual: @typeOf(expected)) void { |
| 44 | 44 | TypeId.ComptimeFloat, |
| 45 | 45 | TypeId.ComptimeInt, |
| 46 | 46 | TypeId.Enum, |
| 47 | TypeId.Namespace, | |
| 48 | 47 | TypeId.Fn, |
| 49 | 48 | TypeId.Promise, |
| 50 | 49 | TypeId.Vector, |
std/unicode.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("./index.zig"); | |
| 1 | const std = @import("./std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 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+7-7| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const testing = std.testing; |
| 4 | 4 | const SegmentedList = std.SegmentedList; |
| ... | ... | @@ -617,7 +617,7 @@ pub const Node = struct { |
| 617 | 617 | |
| 618 | 618 | pub const DeclList = Root.DeclList; |
| 619 | 619 | |
| 620 | const InitArg = union(enum) { | |
| 620 | pub const InitArg = union(enum) { | |
| 621 | 621 | None, |
| 622 | 622 | Enum: ?*Node, |
| 623 | 623 | Type: *Node, |
| ... | ... | @@ -1744,7 +1744,7 @@ pub const Node = struct { |
| 1744 | 1744 | kind: Kind, |
| 1745 | 1745 | rhs: ?*Node, |
| 1746 | 1746 | |
| 1747 | const Kind = union(enum) { | |
| 1747 | pub const Kind = union(enum) { | |
| 1748 | 1748 | Break: ?*Node, |
| 1749 | 1749 | Continue: ?*Node, |
| 1750 | 1750 | Return, |
| ... | ... | @@ -2022,7 +2022,7 @@ pub const Node = struct { |
| 2022 | 2022 | kind: Kind, |
| 2023 | 2023 | rparen: TokenIndex, |
| 2024 | 2024 | |
| 2025 | const Kind = union(enum) { | |
| 2025 | pub const Kind = union(enum) { | |
| 2026 | 2026 | Variable: *Identifier, |
| 2027 | 2027 | Return: *Node, |
| 2028 | 2028 | }; |
| ... | ... | @@ -2101,9 +2101,9 @@ pub const Node = struct { |
| 2101 | 2101 | clobbers: ClobberList, |
| 2102 | 2102 | rparen: TokenIndex, |
| 2103 | 2103 | |
| 2104 | const OutputList = SegmentedList(*AsmOutput, 2); | |
| 2105 | const InputList = SegmentedList(*AsmInput, 2); | |
| 2106 | const ClobberList = SegmentedList(TokenIndex, 2); | |
| 2104 | pub const OutputList = SegmentedList(*AsmOutput, 2); | |
| 2105 | pub const InputList = SegmentedList(*AsmInput, 2); | |
| 2106 | pub const ClobberList = SegmentedList(TokenIndex, 2); | |
| 2107 | 2107 | |
| 2108 | 2108 | pub fn iterate(self: *Asm, index: usize) ?*Node { |
| 2109 | 2109 | var i = index; |
std/zig/bench.zig+1-1| ... | ... | @@ -5,7 +5,7 @@ const Tokenizer = std.zig.Tokenizer; |
| 5 | 5 | const Parser = std.zig.Parser; |
| 6 | 6 | const io = std.io; |
| 7 | 7 | |
| 8 | const source = @embedFile("../os/index.zig"); | |
| 8 | const source = @embedFile("../os.zig"); | |
| 9 | 9 | var fixed_buffer_mem: [10 * 1024 * 1024]u8 = undefined; |
| 10 | 10 | |
| 11 | 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 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const ast = std.zig.ast; |
std/zig/parse_string_literal.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const assert = std.debug.assert; |
| 3 | 3 | |
| 4 | 4 | const State = enum { |
std/zig/render.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const mem = std.mem; |
std/zig/tokenizer.zig+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | const std = @import("../index.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | |
| 4 | 4 | pub const Token = struct { |
test/build_examples.zig+2| ... | ... | @@ -7,8 +7,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { |
| 7 | 7 | cases.addC("example/hello_world/hello_libc.zig"); |
| 8 | 8 | cases.add("example/cat/main.zig"); |
| 9 | 9 | cases.add("example/guess_number/main.zig"); |
| 10 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig"); | |
| 10 | 11 | cases.addBuildFile("example/shared_library/build.zig"); |
| 11 | 12 | cases.addBuildFile("example/mix_o_files/build.zig"); |
| 13 | cases.addBuildFile("test/standalone/static_c_lib/build.zig"); | |
| 12 | 14 | if (builtin.os != builtin.Os.macosx) { |
| 13 | 15 | // TODO https://github.com/ziglang/zig/issues/1126 |
| 14 | 16 | cases.addBuildFile("test/standalone/issue_339/build.zig"); |
test/compile_errors.zig+701-690| ... | ... | @@ -2,6 +2,122 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add( | |
| 6 | "import outside package path", | |
| 7 | \\comptime{ | |
| 8 | \\ _ = @import("../a.zig"); | |
| 9 | \\} | |
| 10 | , | |
| 11 | "tmp.zig:2:9: error: import of file outside package path: '../a.zig'", | |
| 12 | ); | |
| 13 | ||
| 14 | cases.add( | |
| 15 | "bogus compile var", | |
| 16 | \\const x = @import("builtin").bogus; | |
| 17 | \\export fn entry() usize { return @sizeOf(@typeOf(x)); } | |
| 18 | , | |
| 19 | "tmp.zig:1:29: error: container 'builtin' has no member called 'bogus'", | |
| 20 | ); | |
| 21 | ||
| 22 | cases.add( | |
| 23 | "wrong panic signature, runtime function", | |
| 24 | \\test "" {} | |
| 25 | \\ | |
| 26 | \\pub fn panic() void {} | |
| 27 | \\ | |
| 28 | , | |
| 29 | "tmp.zig:3:5: error: expected type 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn() void'", | |
| 30 | ); | |
| 31 | ||
| 32 | cases.add( | |
| 33 | "wrong panic signature, generic function", | |
| 34 | \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | |
| 35 | \\ while (true) {} | |
| 36 | \\} | |
| 37 | , | |
| 38 | "tmp.zig:1:5: error: expected type 'fn([]const u8, ?*builtin.StackTrace) noreturn', found 'fn([]const u8,var)var'", | |
| 39 | "tmp.zig:1:5: note: only one of the functions is generic", | |
| 40 | ); | |
| 41 | ||
| 42 | cases.add( | |
| 43 | "direct struct loop", | |
| 44 | \\const A = struct { a : A, }; | |
| 45 | \\export fn entry() usize { return @sizeOf(A); } | |
| 46 | , | |
| 47 | "tmp.zig:1:11: error: struct 'A' contains itself", | |
| 48 | ); | |
| 49 | ||
| 50 | cases.add( | |
| 51 | "indirect struct loop", | |
| 52 | \\const A = struct { b : B, }; | |
| 53 | \\const B = struct { c : C, }; | |
| 54 | \\const C = struct { a : A, }; | |
| 55 | \\export fn entry() usize { return @sizeOf(A); } | |
| 56 | , | |
| 57 | "tmp.zig:1:11: error: struct 'A' contains itself", | |
| 58 | ); | |
| 59 | ||
| 60 | cases.add( | |
| 61 | "instantiating an undefined value for an invalid struct that contains itself", | |
| 62 | \\const Foo = struct { | |
| 63 | \\ x: Foo, | |
| 64 | \\}; | |
| 65 | \\ | |
| 66 | \\var foo: Foo = undefined; | |
| 67 | \\ | |
| 68 | \\export fn entry() usize { | |
| 69 | \\ return @sizeOf(@typeOf(foo.x)); | |
| 70 | \\} | |
| 71 | , | |
| 72 | "tmp.zig:1:13: error: struct 'Foo' contains itself", | |
| 73 | "tmp.zig:8:28: note: referenced here", | |
| 74 | ); | |
| 75 | ||
| 76 | cases.add( | |
| 77 | "@typeInfo causing depend on itself compile error", | |
| 78 | \\const start = struct { | |
| 79 | \\ fn crash() bug() { | |
| 80 | \\ return bug; | |
| 81 | \\ } | |
| 82 | \\}; | |
| 83 | \\fn bug() void { | |
| 84 | \\ _ = @typeInfo(start).Struct; | |
| 85 | \\} | |
| 86 | \\export fn entry() void { | |
| 87 | \\ var boom = start.crash(); | |
| 88 | \\} | |
| 89 | , | |
| 90 | "tmp.zig:7:9: error: dependency loop detected", | |
| 91 | "tmp.zig:2:19: note: called from here", | |
| 92 | "tmp.zig:10:21: note: referenced here", | |
| 93 | ); | |
| 94 | ||
| 95 | cases.add( | |
| 96 | "enum field value references enum", | |
| 97 | \\pub const Foo = extern enum { | |
| 98 | \\ A = Foo.B, | |
| 99 | \\ C = D, | |
| 100 | \\}; | |
| 101 | \\export fn entry() void { | |
| 102 | \\ var s: Foo = Foo.E; | |
| 103 | \\} | |
| 104 | , | |
| 105 | "tmp.zig:1:17: error: 'Foo' depends on itself", | |
| 106 | ); | |
| 107 | ||
| 108 | cases.add( | |
| 109 | "top level decl dependency loop", | |
| 110 | \\const a : @typeOf(b) = 0; | |
| 111 | \\const b : @typeOf(a) = 0; | |
| 112 | \\export fn entry() void { | |
| 113 | \\ const c = a + b; | |
| 114 | \\} | |
| 115 | , | |
| 116 | "tmp.zig:2:19: error: dependency loop detected", | |
| 117 | "tmp.zig:1:19: note: referenced here", | |
| 118 | "tmp.zig:4:15: note: referenced here", | |
| 119 | ); | |
| 120 | ||
| 5 | 121 | cases.addTest( |
| 6 | 122 | "not an enum type", |
| 7 | 123 | \\export fn entry() void { |
| ... | ... | @@ -18,7 +134,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 18 | 134 | \\const InvalidToken = struct {}; |
| 19 | 135 | \\const ExpectedVarDeclOrFn = struct {}; |
| 20 | 136 | , |
| 21 | ".tmp_source.zig:4:9: error: not an enum type", | |
| 137 | "tmp.zig:4:9: error: not an enum type", | |
| 22 | 138 | ); |
| 23 | 139 | |
| 24 | 140 | cases.addTest( |
| ... | ... | @@ -29,7 +145,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 29 | 145 | \\ var x: AB = undefined; |
| 30 | 146 | \\} |
| 31 | 147 | , |
| 32 | ".tmp_source.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", | |
| 148 | "tmp.zig:2:18: error: invalid operands to binary expression: 'error{A}' and 'error{B}'", | |
| 33 | 149 | ); |
| 34 | 150 | |
| 35 | 151 | if (builtin.os == builtin.Os.linux) { |
| ... | ... | @@ -40,7 +156,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 40 | 156 | \\ exit(0); |
| 41 | 157 | \\} |
| 42 | 158 | , |
| 43 | ".tmp_source.zig:3:5: error: dependency on library c must be explicitly specified in the build command", | |
| 159 | "tmp.zig:3:5: error: dependency on library c must be explicitly specified in the build command", | |
| 44 | 160 | ); |
| 45 | 161 | |
| 46 | 162 | cases.addTest( |
| ... | ... | @@ -50,8 +166,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 50 | 166 | \\ _ = c.printf(c"hello, world!\n"); |
| 51 | 167 | \\} |
| 52 | 168 | , |
| 53 | ".tmp_source.zig:1:11: error: C import failed", | |
| 54 | ".tmp_source.zig:1:11: note: libc headers not available; compilation does not link against libc", | |
| 169 | "tmp.zig:1:11: error: C import failed", | |
| 170 | "tmp.zig:1:11: note: libc headers not available; compilation does not link against libc", | |
| 55 | 171 | ); |
| 56 | 172 | } |
| 57 | 173 | |
| ... | ... | @@ -63,8 +179,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 63 | 179 | \\ var x = a + b; |
| 64 | 180 | \\} |
| 65 | 181 | , |
| 66 | ".tmp_source.zig:4:15: error: operation caused overflow", | |
| 67 | ".tmp_source.zig:4:15: note: when computing vector element at index 2", | |
| 182 | "tmp.zig:4:15: error: operation caused overflow", | |
| 183 | "tmp.zig:4:15: note: when computing vector element at index 2", | |
| 68 | 184 | ); |
| 69 | 185 | |
| 70 | 186 | cases.addTest( |
| ... | ... | @@ -111,14 +227,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 111 | 227 | \\ B, |
| 112 | 228 | \\}; |
| 113 | 229 | , |
| 114 | ".tmp_source.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", | |
| 115 | ".tmp_source.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits", | |
| 116 | ".tmp_source.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", | |
| 117 | ".tmp_source.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation", | |
| 118 | ".tmp_source.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation", | |
| 119 | ".tmp_source.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation", | |
| 120 | ".tmp_source.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation", | |
| 121 | ".tmp_source.zig:38:14: note: enum declaration does not specify an integer tag type", | |
| 230 | "tmp.zig:2:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", | |
| 231 | "tmp.zig:5:5: error: array of 'u24' not allowed in packed struct due to padding bits", | |
| 232 | "tmp.zig:8:5: error: type 'anyerror' not allowed in packed struct; no guaranteed in-memory representation", | |
| 233 | "tmp.zig:11:5: error: non-packed, non-extern struct 'S' not allowed in packed struct; no guaranteed in-memory representation", | |
| 234 | "tmp.zig:14:5: error: non-packed, non-extern struct 'U' not allowed in packed struct; no guaranteed in-memory representation", | |
| 235 | "tmp.zig:17:5: error: type '?anyerror' not allowed in packed struct; no guaranteed in-memory representation", | |
| 236 | "tmp.zig:20:5: error: type 'Enum' not allowed in packed struct; no guaranteed in-memory representation", | |
| 237 | "tmp.zig:38:14: note: enum declaration does not specify an integer tag type", | |
| 122 | 238 | ); |
| 123 | 239 | |
| 124 | 240 | cases.addCase(x: { |
| ... | ... | @@ -131,7 +247,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 131 | 247 | \\ x += 1; |
| 132 | 248 | \\} |
| 133 | 249 | , |
| 134 | ".tmp_source.zig:2:5: error: use of undeclared identifier 'x'", | |
| 250 | "tmp.zig:2:5: error: use of undeclared identifier 'x'", | |
| 135 | 251 | ); |
| 136 | 252 | tc.expect_exact = true; |
| 137 | 253 | break :x tc; |
| ... | ... | @@ -143,7 +259,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 143 | 259 | \\ return 0; |
| 144 | 260 | \\} |
| 145 | 261 | , |
| 146 | ".tmp_source.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'ccc'", | |
| 262 | "tmp.zig:1:15: error: parameter of type 'var' not allowed in function with calling convention 'ccc'", | |
| 147 | 263 | ); |
| 148 | 264 | |
| 149 | 265 | cases.addTest( |
| ... | ... | @@ -153,7 +269,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 153 | 269 | \\ var y: [*c]c_void = x; |
| 154 | 270 | \\} |
| 155 | 271 | , |
| 156 | ".tmp_source.zig:3:12: error: C pointers cannot point opaque types", | |
| 272 | "tmp.zig:3:12: error: C pointers cannot point opaque types", | |
| 157 | 273 | ); |
| 158 | 274 | |
| 159 | 275 | cases.addTest( |
| ... | ... | @@ -173,8 +289,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 173 | 289 | \\ var bar: Bar = undefined; |
| 174 | 290 | \\} |
| 175 | 291 | , |
| 176 | ".tmp_source.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs", | |
| 177 | ".tmp_source.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions", | |
| 292 | "tmp.zig:3:8: error: opaque types have unknown size and therefore cannot be directly embedded in structs", | |
| 293 | "tmp.zig:7:10: error: opaque types have unknown size and therefore cannot be directly embedded in unions", | |
| 178 | 294 | ); |
| 179 | 295 | |
| 180 | 296 | cases.addTest( |
| ... | ... | @@ -204,13 +320,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 204 | 320 | \\ var x: [*c]u32 = y; |
| 205 | 321 | \\} |
| 206 | 322 | , |
| 207 | ".tmp_source.zig:3:27: error: cast increases pointer alignment", | |
| 208 | ".tmp_source.zig:7:18: error: cast discards const qualifier", | |
| 209 | ".tmp_source.zig:11:19: error: expected type '*u32', found '[*c]u8'", | |
| 210 | ".tmp_source.zig:11:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32'", | |
| 211 | ".tmp_source.zig:15:22: error: cast increases pointer alignment", | |
| 212 | ".tmp_source.zig:19:21: error: cast discards const qualifier", | |
| 213 | ".tmp_source.zig:23:22: error: expected type '[*c]u32', found '*u8'", | |
| 323 | "tmp.zig:3:27: error: cast increases pointer alignment", | |
| 324 | "tmp.zig:7:18: error: cast discards const qualifier", | |
| 325 | "tmp.zig:11:19: error: expected type '*u32', found '[*c]u8'", | |
| 326 | "tmp.zig:11:19: note: pointer type child 'u8' cannot cast into pointer type child 'u32'", | |
| 327 | "tmp.zig:15:22: error: cast increases pointer alignment", | |
| 328 | "tmp.zig:19:21: error: cast discards const qualifier", | |
| 329 | "tmp.zig:23:22: error: expected type '[*c]u32', found '*u8'", | |
| 214 | 330 | ); |
| 215 | 331 | |
| 216 | 332 | cases.addTest( |
| ... | ... | @@ -220,7 +336,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 220 | 336 | \\ var zig_ptr: *u8 = c_ptr; |
| 221 | 337 | \\} |
| 222 | 338 | , |
| 223 | ".tmp_source.zig:3:24: error: null pointer casted to type '*u8'", | |
| 339 | "tmp.zig:3:24: error: null pointer casted to type '*u8'", | |
| 224 | 340 | ); |
| 225 | 341 | |
| 226 | 342 | cases.addTest( |
| ... | ... | @@ -230,7 +346,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 230 | 346 | \\ var zig_ptr: *u8 = c_ptr; |
| 231 | 347 | \\} |
| 232 | 348 | , |
| 233 | ".tmp_source.zig:3:24: error: use of undefined value here causes undefined behavior", | |
| 349 | "tmp.zig:3:24: error: use of undefined value here causes undefined behavior", | |
| 234 | 350 | ); |
| 235 | 351 | |
| 236 | 352 | cases.addTest( |
| ... | ... | @@ -250,12 +366,12 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 250 | 366 | \\ var c_ptr: [*c][*c]const u8 = ptr_opt_many_ptr; |
| 251 | 367 | \\} |
| 252 | 368 | , |
| 253 | ".tmp_source.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'", | |
| 254 | ".tmp_source.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'", | |
| 255 | ".tmp_source.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'", | |
| 256 | ".tmp_source.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'", | |
| 257 | ".tmp_source.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'", | |
| 258 | ".tmp_source.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'", | |
| 369 | "tmp.zig:6:24: error: expected type '*const [*]const u8', found '[*c]const [*c]const u8'", | |
| 370 | "tmp.zig:6:24: note: pointer type child '[*c]const u8' cannot cast into pointer type child '[*]const u8'", | |
| 371 | "tmp.zig:6:24: note: '[*c]const u8' could have null values which are illegal in type '[*]const u8'", | |
| 372 | "tmp.zig:13:35: error: expected type '[*c][*c]const u8', found '*[*]u8'", | |
| 373 | "tmp.zig:13:35: note: pointer type child '[*]u8' cannot cast into pointer type child '[*c]const u8'", | |
| 374 | "tmp.zig:13:35: note: mutable '[*c]const u8' allows illegal null values stored to type '[*]u8'", | |
| 259 | 375 | ); |
| 260 | 376 | |
| 261 | 377 | cases.addTest( |
| ... | ... | @@ -268,8 +384,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 268 | 384 | \\ var ptr: [*c]u8 = x; |
| 269 | 385 | \\} |
| 270 | 386 | , |
| 271 | ".tmp_source.zig:2:33: error: integer value 71615590737044764481 cannot be implicitly casted to type 'usize'", | |
| 272 | ".tmp_source.zig:6:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'", | |
| 387 | "tmp.zig:2:33: error: integer value 71615590737044764481 cannot be implicitly casted to type 'usize'", | |
| 388 | "tmp.zig:6:23: error: integer type 'u65' too big for implicit @intToPtr to type '[*c]u8'", | |
| 273 | 389 | ); |
| 274 | 390 | |
| 275 | 391 | cases.addTest( |
| ... | ... | @@ -279,7 +395,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 279 | 395 | \\ const T = [*c]Foo; |
| 280 | 396 | \\} |
| 281 | 397 | , |
| 282 | ".tmp_source.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", | |
| 398 | "tmp.zig:3:15: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", | |
| 283 | 399 | ); |
| 284 | 400 | |
| 285 | 401 | cases.addCase(x: { |
| ... | ... | @@ -294,7 +410,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 294 | 410 | \\ inline while (i < n) : (i += 1) { @compileLog("!@#$"); } |
| 295 | 411 | \\} |
| 296 | 412 | , |
| 297 | ".tmp_source.zig:7:39: error: found compile log statement", | |
| 413 | "tmp.zig:7:39: error: found compile log statement", | |
| 298 | 414 | ); |
| 299 | 415 | tc.expect_exact = true; |
| 300 | 416 | break :x tc; |
| ... | ... | @@ -306,7 +422,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 306 | 422 | \\ 'a'.* = 1; |
| 307 | 423 | \\} |
| 308 | 424 | , |
| 309 | ".tmp_source.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int'", | |
| 425 | "tmp.zig:2:8: error: attempt to dereference non-pointer type 'comptime_int'", | |
| 310 | 426 | ); |
| 311 | 427 | |
| 312 | 428 | cases.addTest( |
| ... | ... | @@ -315,7 +431,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 315 | 431 | \\ const x = 'a'.*[0..]; |
| 316 | 432 | \\} |
| 317 | 433 | , |
| 318 | ".tmp_source.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int'", | |
| 434 | "tmp.zig:2:18: error: attempt to dereference non-pointer type 'comptime_int'", | |
| 319 | 435 | ); |
| 320 | 436 | |
| 321 | 437 | cases.addTest( |
| ... | ... | @@ -324,14 +440,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 324 | 440 | \\ var z = @truncate(u8, u16(undefined)); |
| 325 | 441 | \\} |
| 326 | 442 | , |
| 327 | ".tmp_source.zig:2:30: error: use of undefined value here causes undefined behavior", | |
| 443 | "tmp.zig:2:30: error: use of undefined value here causes undefined behavior", | |
| 328 | 444 | ); |
| 329 | 445 | |
| 330 | 446 | cases.addTest( |
| 331 | 447 | "return invalid type from test", |
| 332 | 448 | \\test "example" { return 1; } |
| 333 | 449 | , |
| 334 | ".tmp_source.zig:1:25: error: integer value 1 cannot be implicitly casted to type 'void'", | |
| 450 | "tmp.zig:1:25: error: integer value 1 cannot be implicitly casted to type 'void'", | |
| 335 | 451 | ); |
| 336 | 452 | |
| 337 | 453 | cases.add( |
| ... | ... | @@ -341,7 +457,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 341 | 457 | \\ return x; |
| 342 | 458 | \\} |
| 343 | 459 | , |
| 344 | ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant", | |
| 460 | "tmp.zig:1:13: error: threadlocal variable cannot be constant", | |
| 345 | 461 | ); |
| 346 | 462 | |
| 347 | 463 | cases.add( |
| ... | ... | @@ -350,7 +466,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 350 | 466 | \\ threadlocal var x: i32 = 1234; |
| 351 | 467 | \\} |
| 352 | 468 | , |
| 353 | ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal", | |
| 469 | "tmp.zig:2:5: error: function-local variable 'x' cannot be threadlocal", | |
| 354 | 470 | ); |
| 355 | 471 | |
| 356 | 472 | cases.add( |
| ... | ... | @@ -359,7 +475,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 359 | 475 | \\ var oops = @bitCast(u7, byte); |
| 360 | 476 | \\} |
| 361 | 477 | , |
| 362 | ".tmp_source.zig:2:16: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits", | |
| 478 | "tmp.zig:2:16: error: destination type 'u7' has 7 bits but source type 'u8' has 8 bits", | |
| 363 | 479 | ); |
| 364 | 480 | |
| 365 | 481 | cases.add( |
| ... | ... | @@ -371,7 +487,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 371 | 487 | \\ return 5678; |
| 372 | 488 | \\} |
| 373 | 489 | , |
| 374 | ".tmp_source.zig:2:12: error: `&&` is invalid. Note that `and` is boolean AND.", | |
| 490 | "tmp.zig:2:12: error: `&&` is invalid. Note that `and` is boolean AND.", | |
| 375 | 491 | ); |
| 376 | 492 | |
| 377 | 493 | cases.add( |
| ... | ... | @@ -383,8 +499,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 383 | 499 | \\ return 5678; |
| 384 | 500 | \\} |
| 385 | 501 | , |
| 386 | ".tmp_source.zig:2:9: error: expected error set type, found 'bool'", | |
| 387 | ".tmp_source.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", | |
| 502 | "tmp.zig:2:9: error: expected error set type, found 'bool'", | |
| 503 | "tmp.zig:2:11: note: `||` merges error sets; `or` performs boolean OR", | |
| 388 | 504 | ); |
| 389 | 505 | |
| 390 | 506 | cases.add( |
| ... | ... | @@ -393,7 +509,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 393 | 509 | \\ @compileLog(@ptrCast(*const c_void, &entry)); |
| 394 | 510 | \\} |
| 395 | 511 | , |
| 396 | ".tmp_source.zig:2:5: error: found compile log statement", | |
| 512 | "tmp.zig:2:5: error: found compile log statement", | |
| 397 | 513 | ); |
| 398 | 514 | |
| 399 | 515 | cases.add( |
| ... | ... | @@ -413,8 +529,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 413 | 529 | \\ }; |
| 414 | 530 | \\} |
| 415 | 531 | , |
| 416 | ".tmp_source.zig:5:9: error: duplicate switch value", | |
| 417 | ".tmp_source.zig:12:9: error: duplicate switch value", | |
| 532 | "tmp.zig:5:9: error: duplicate switch value", | |
| 533 | "tmp.zig:12:9: error: duplicate switch value", | |
| 418 | 534 | ); |
| 419 | 535 | |
| 420 | 536 | cases.add( |
| ... | ... | @@ -430,8 +546,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 430 | 546 | \\ }; |
| 431 | 547 | \\} |
| 432 | 548 | , |
| 433 | ".tmp_source.zig:2:15: error: switch must handle all possibilities", | |
| 434 | ".tmp_source.zig:7:15: error: switch must handle all possibilities", | |
| 549 | "tmp.zig:2:15: error: switch must handle all possibilities", | |
| 550 | "tmp.zig:7:15: error: switch must handle all possibilities", | |
| 435 | 551 | ); |
| 436 | 552 | |
| 437 | 553 | cases.add( |
| ... | ... | @@ -443,7 +559,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 443 | 559 | \\ const deref = int_ptr.*; |
| 444 | 560 | \\} |
| 445 | 561 | , |
| 446 | ".tmp_source.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", | |
| 562 | "tmp.zig:5:26: error: attempt to read 4 bytes from [4]u8 at index 1 which is 3 bytes", | |
| 447 | 563 | ); |
| 448 | 564 | |
| 449 | 565 | cases.add( |
| ... | ... | @@ -454,8 +570,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 454 | 570 | \\ do_the_thing(bar); |
| 455 | 571 | \\} |
| 456 | 572 | , |
| 457 | ".tmp_source.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", | |
| 458 | ".tmp_source.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", | |
| 573 | "tmp.zig:4:18: error: expected type 'fn(i32) void', found 'fn(bool) void", | |
| 574 | "tmp.zig:4:18: note: parameter 0: 'bool' cannot cast into 'i32'", | |
| 459 | 575 | ); |
| 460 | 576 | |
| 461 | 577 | cases.add( |
| ... | ... | @@ -469,8 +585,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 469 | 585 | \\ const unsigned: u32 = value; |
| 470 | 586 | \\} |
| 471 | 587 | , |
| 472 | ".tmp_source.zig:3:36: error: cannot cast negative value -1 to unsigned integer type 'u32'", | |
| 473 | ".tmp_source.zig:7:27: error: cannot cast negative value -1 to unsigned integer type 'u32'", | |
| 588 | "tmp.zig:3:36: error: cannot cast negative value -1 to unsigned integer type 'u32'", | |
| 589 | "tmp.zig:7:27: error: cannot cast negative value -1 to unsigned integer type 'u32'", | |
| 474 | 590 | ); |
| 475 | 591 | |
| 476 | 592 | cases.add( |
| ... | ... | @@ -488,9 +604,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 488 | 604 | \\ var byte: u8 = spartan_count; |
| 489 | 605 | \\} |
| 490 | 606 | , |
| 491 | ".tmp_source.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'", | |
| 492 | ".tmp_source.zig:7:22: error: integer value 300 cannot be implicitly casted to type 'u8'", | |
| 493 | ".tmp_source.zig:11:20: error: expected type 'u8', found 'u16'", | |
| 607 | "tmp.zig:3:31: error: integer value 300 cannot be implicitly casted to type 'u8'", | |
| 608 | "tmp.zig:7:22: error: integer value 300 cannot be implicitly casted to type 'u8'", | |
| 609 | "tmp.zig:11:20: error: expected type 'u8', found 'u16'", | |
| 494 | 610 | ); |
| 495 | 611 | |
| 496 | 612 | cases.add( |
| ... | ... | @@ -500,7 +616,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 500 | 616 | \\ const y: f32 = x; |
| 501 | 617 | \\} |
| 502 | 618 | , |
| 503 | ".tmp_source.zig:3:20: error: cast of value 16777217.000000 to type 'f32' loses information", | |
| 619 | "tmp.zig:3:20: error: cast of value 16777217.000000 to type 'f32' loses information", | |
| 504 | 620 | ); |
| 505 | 621 | |
| 506 | 622 | cases.add( |
| ... | ... | @@ -510,7 +626,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 510 | 626 | \\ |
| 511 | 627 | \\export fn entry() usize { return @sizeOf(@typeOf(y)); } |
| 512 | 628 | , |
| 513 | ".tmp_source.zig:2:14: error: expected type 'f32', found 'f64'", | |
| 629 | "tmp.zig:2:14: error: expected type 'f32', found 'f64'", | |
| 514 | 630 | ); |
| 515 | 631 | |
| 516 | 632 | cases.add( |
| ... | ... | @@ -522,28 +638,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 522 | 638 | \\ var x: i65536 = 1; |
| 523 | 639 | \\} |
| 524 | 640 | , |
| 525 | ".tmp_source.zig:2:31: error: integer value 65536 cannot be implicitly casted to type 'u16'", | |
| 526 | ".tmp_source.zig:5:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535", | |
| 527 | ); | |
| 528 | ||
| 529 | cases.add( | |
| 530 | "wrong panic signature, runtime function", | |
| 531 | \\test "" {} | |
| 532 | \\ | |
| 533 | \\pub fn panic() void {} | |
| 534 | \\ | |
| 535 | , | |
| 536 | ".tmp_source.zig:3:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn() void'", | |
| 537 | ); | |
| 538 | ||
| 539 | cases.add( | |
| 540 | "wrong panic signature, generic function", | |
| 541 | \\pub fn panic(comptime msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn { | |
| 542 | \\ while (true) {} | |
| 543 | \\} | |
| 544 | , | |
| 545 | ".tmp_source.zig:1:5: error: expected type 'fn([]const u8, ?*StackTrace) noreturn', found 'fn([]const u8,var)var'", | |
| 546 | ".tmp_source.zig:1:5: note: only one of the functions is generic", | |
| 641 | "tmp.zig:2:31: error: integer value 65536 cannot be implicitly casted to type 'u16'", | |
| 642 | "tmp.zig:5:12: error: primitive integer type 'i65536' exceeds maximum bit width of 65535", | |
| 547 | 643 | ); |
| 548 | 644 | |
| 549 | 645 | cases.add( |
| ... | ... | @@ -556,7 +652,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 556 | 652 | \\ const car = Car.init(); |
| 557 | 653 | \\} |
| 558 | 654 | , |
| 559 | ".tmp_source.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist'", | |
| 655 | "tmp.zig:2:11: error: use of undeclared identifier 'SymbolThatDoesNotExist'", | |
| 560 | 656 | ); |
| 561 | 657 | |
| 562 | 658 | cases.add( |
| ... | ... | @@ -568,7 +664,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 568 | 664 | \\ var ptr2: *c_void = &b; |
| 569 | 665 | \\} |
| 570 | 666 | , |
| 571 | ".tmp_source.zig:5:26: error: expected type '*c_void', found '**u32'", | |
| 667 | "tmp.zig:5:26: error: expected type '*c_void', found '**u32'", | |
| 572 | 668 | ); |
| 573 | 669 | |
| 574 | 670 | cases.add( |
| ... | ... | @@ -584,7 +680,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 584 | 680 | \\ const field = @typeInfo(Struct).Struct.fields[index]; |
| 585 | 681 | \\} |
| 586 | 682 | , |
| 587 | ".tmp_source.zig:9:51: error: values of type 'StructField' must be comptime known, but index value is runtime known", | |
| 683 | "tmp.zig:9:51: error: values of type 'builtin.StructField' must be comptime known, but index value is runtime known", | |
| 588 | 684 | ); |
| 589 | 685 | |
| 590 | 686 | cases.add( |
| ... | ... | @@ -598,7 +694,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 598 | 694 | \\ _ = @typeName(Foo(i32)); |
| 599 | 695 | \\} |
| 600 | 696 | , |
| 601 | ".tmp_source.zig:2:5: error: found compile log statement", | |
| 697 | "tmp.zig:2:5: error: found compile log statement", | |
| 602 | 698 | ); |
| 603 | 699 | |
| 604 | 700 | cases.add( |
| ... | ... | @@ -608,7 +704,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 608 | 704 | \\ var b = a[0..10]; |
| 609 | 705 | \\} |
| 610 | 706 | , |
| 611 | ".tmp_source.zig:3:14: error: slice of undefined", | |
| 707 | "tmp.zig:3:14: error: slice of undefined", | |
| 612 | 708 | ); |
| 613 | 709 | |
| 614 | 710 | cases.add( |
| ... | ... | @@ -618,7 +714,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 618 | 714 | \\ const sliceA: []u8 = &buffer; |
| 619 | 715 | \\} |
| 620 | 716 | , |
| 621 | ".tmp_source.zig:3:27: error: expected type '[]u8', found '*const [1]u8'", | |
| 717 | "tmp.zig:3:27: error: expected type '[]u8', found '*const [1]u8'", | |
| 622 | 718 | ); |
| 623 | 719 | |
| 624 | 720 | cases.add( |
| ... | ... | @@ -628,7 +724,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 628 | 724 | \\ _ = a.*.len; |
| 629 | 725 | \\} |
| 630 | 726 | , |
| 631 | ".tmp_source.zig:3:10: error: attempt to dereference non-pointer type '[]u8'", | |
| 727 | "tmp.zig:3:10: error: attempt to dereference non-pointer type '[]u8'", | |
| 632 | 728 | ); |
| 633 | 729 | |
| 634 | 730 | cases.add( |
| ... | ... | @@ -639,9 +735,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 639 | 735 | \\ return p == null; |
| 640 | 736 | \\} |
| 641 | 737 | , |
| 642 | ".tmp_source.zig:3:15: error: '*u0' and '?*u0' do not have the same in-memory representation", | |
| 643 | ".tmp_source.zig:3:31: note: '*u0' has no in-memory bits", | |
| 644 | ".tmp_source.zig:3:24: note: '?*u0' has in-memory bits", | |
| 738 | "tmp.zig:3:15: error: '*u0' and '?*u0' do not have the same in-memory representation", | |
| 739 | "tmp.zig:3:31: note: '*u0' has no in-memory bits", | |
| 740 | "tmp.zig:3:24: note: '?*u0' has in-memory bits", | |
| 645 | 741 | ); |
| 646 | 742 | |
| 647 | 743 | cases.add( |
| ... | ... | @@ -651,7 +747,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 651 | 747 | \\ _ = &x == null; |
| 652 | 748 | \\} |
| 653 | 749 | , |
| 654 | ".tmp_source.zig:3:12: error: comparison against null can only be done with optionals", | |
| 750 | "tmp.zig:3:12: error: comparison against null can only be done with optionals", | |
| 655 | 751 | ); |
| 656 | 752 | |
| 657 | 753 | cases.add( |
| ... | ... | @@ -663,10 +759,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 663 | 759 | \\ const Errors = error{} || u16; |
| 664 | 760 | \\} |
| 665 | 761 | , |
| 666 | ".tmp_source.zig:2:20: error: expected error set type, found type 'u8'", | |
| 667 | ".tmp_source.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", | |
| 668 | ".tmp_source.zig:5:31: error: expected error set type, found type 'u16'", | |
| 669 | ".tmp_source.zig:5:28: note: `||` merges error sets; `or` performs boolean OR", | |
| 762 | "tmp.zig:2:20: error: expected error set type, found type 'u8'", | |
| 763 | "tmp.zig:2:23: note: `||` merges error sets; `or` performs boolean OR", | |
| 764 | "tmp.zig:5:31: error: expected error set type, found type 'u16'", | |
| 765 | "tmp.zig:5:28: note: `||` merges error sets; `or` performs boolean OR", | |
| 670 | 766 | ); |
| 671 | 767 | |
| 672 | 768 | cases.add( |
| ... | ... | @@ -684,7 +780,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 684 | 780 | \\ const S = Gen(); |
| 685 | 781 | \\} |
| 686 | 782 | , |
| 687 | ".tmp_source.zig:2:12: error: use of undeclared identifier 'T'", | |
| 783 | "tmp.zig:2:12: error: use of undeclared identifier 'T'", | |
| 688 | 784 | ); |
| 689 | 785 | |
| 690 | 786 | cases.add( |
| ... | ... | @@ -695,7 +791,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 695 | 791 | \\ f(i32); |
| 696 | 792 | \\} |
| 697 | 793 | , |
| 698 | ".tmp_source.zig:4:5: error: use of undefined value here causes undefined behavior", | |
| 794 | "tmp.zig:4:5: error: use of undefined value here causes undefined behavior", | |
| 699 | 795 | ); |
| 700 | 796 | |
| 701 | 797 | cases.add( |
| ... | ... | @@ -713,9 +809,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 713 | 809 | \\ var x = func(3); |
| 714 | 810 | \\} |
| 715 | 811 | , |
| 716 | ".tmp_source.zig:4:24: error: 'y' not accessible from inner function", | |
| 717 | ".tmp_source.zig:3:28: note: crossed function definition here", | |
| 718 | ".tmp_source.zig:1:10: note: declared here", | |
| 812 | "tmp.zig:4:24: error: 'y' not accessible from inner function", | |
| 813 | "tmp.zig:3:28: note: crossed function definition here", | |
| 814 | "tmp.zig:1:10: note: declared here", | |
| 719 | 815 | ); |
| 720 | 816 | |
| 721 | 817 | cases.add( |
| ... | ... | @@ -724,7 +820,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 724 | 820 | \\ const x = @intToFloat(f32, 1.1); |
| 725 | 821 | \\} |
| 726 | 822 | , |
| 727 | ".tmp_source.zig:2:32: error: expected int type, found 'comptime_float'", | |
| 823 | "tmp.zig:2:32: error: expected int type, found 'comptime_float'", | |
| 728 | 824 | ); |
| 729 | 825 | |
| 730 | 826 | cases.add( |
| ... | ... | @@ -733,7 +829,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 733 | 829 | \\ const x = @floatToInt(i32, i32(54)); |
| 734 | 830 | \\} |
| 735 | 831 | , |
| 736 | ".tmp_source.zig:2:35: error: expected float type, found 'i32'", | |
| 832 | "tmp.zig:2:35: error: expected float type, found 'i32'", | |
| 737 | 833 | ); |
| 738 | 834 | |
| 739 | 835 | cases.add( |
| ... | ... | @@ -742,7 +838,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 742 | 838 | \\ const x = @floatToInt(i8, 200); |
| 743 | 839 | \\} |
| 744 | 840 | , |
| 745 | ".tmp_source.zig:2:31: error: integer value 200 cannot be implicitly casted to type 'i8'", | |
| 841 | "tmp.zig:2:31: error: integer value 200 cannot be implicitly casted to type 'i8'", | |
| 746 | 842 | ); |
| 747 | 843 | |
| 748 | 844 | cases.add( |
| ... | ... | @@ -754,7 +850,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 754 | 850 | \\ const int_val = int_ptr.*; |
| 755 | 851 | \\} |
| 756 | 852 | , |
| 757 | ".tmp_source.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes", | |
| 853 | "tmp.zig:5:28: error: attempt to read 8 bytes from pointer to f32 which is 4 bytes", | |
| 758 | 854 | ); |
| 759 | 855 | |
| 760 | 856 | cases.add( |
| ... | ... | @@ -767,7 +863,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 767 | 863 | \\ const a = items[0]; |
| 768 | 864 | \\} |
| 769 | 865 | , |
| 770 | ".tmp_source.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'", | |
| 866 | "tmp.zig:2:12: error: use of undeclared identifier 'SomeNonexistentType'", | |
| 771 | 867 | ); |
| 772 | 868 | |
| 773 | 869 | cases.add( |
| ... | ... | @@ -778,7 +874,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 778 | 874 | \\ @noInlineCall(foo); |
| 779 | 875 | \\} |
| 780 | 876 | , |
| 781 | ".tmp_source.zig:4:5: error: no-inline call of inline function", | |
| 877 | "tmp.zig:4:5: error: no-inline call of inline function", | |
| 782 | 878 | ); |
| 783 | 879 | |
| 784 | 880 | cases.add( |
| ... | ... | @@ -795,8 +891,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 795 | 891 | \\ } |
| 796 | 892 | \\} |
| 797 | 893 | , |
| 798 | ".tmp_source.zig:6:19: error: comptime control flow inside runtime block", | |
| 799 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 894 | "tmp.zig:6:19: error: comptime control flow inside runtime block", | |
| 895 | "tmp.zig:5:9: note: runtime block created here", | |
| 800 | 896 | ); |
| 801 | 897 | |
| 802 | 898 | cases.add( |
| ... | ... | @@ -812,8 +908,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 812 | 908 | \\ } |
| 813 | 909 | \\} |
| 814 | 910 | , |
| 815 | ".tmp_source.zig:6:13: error: comptime control flow inside runtime block", | |
| 816 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 911 | "tmp.zig:6:13: error: comptime control flow inside runtime block", | |
| 912 | "tmp.zig:5:9: note: runtime block created here", | |
| 817 | 913 | ); |
| 818 | 914 | |
| 819 | 915 | cases.add( |
| ... | ... | @@ -827,8 +923,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 827 | 923 | \\ } |
| 828 | 924 | \\} |
| 829 | 925 | , |
| 830 | ".tmp_source.zig:5:23: error: comptime control flow inside runtime block", | |
| 831 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 926 | "tmp.zig:5:23: error: comptime control flow inside runtime block", | |
| 927 | "tmp.zig:5:9: note: runtime block created here", | |
| 832 | 928 | ); |
| 833 | 929 | |
| 834 | 930 | cases.add( |
| ... | ... | @@ -842,8 +938,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 842 | 938 | \\ } |
| 843 | 939 | \\} |
| 844 | 940 | , |
| 845 | ".tmp_source.zig:5:25: error: comptime control flow inside runtime block", | |
| 846 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 941 | "tmp.zig:5:25: error: comptime control flow inside runtime block", | |
| 942 | "tmp.zig:5:9: note: runtime block created here", | |
| 847 | 943 | ); |
| 848 | 944 | |
| 849 | 945 | cases.add( |
| ... | ... | @@ -857,8 +953,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 857 | 953 | \\ } |
| 858 | 954 | \\} |
| 859 | 955 | , |
| 860 | ".tmp_source.zig:5:20: error: comptime control flow inside runtime block", | |
| 861 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 956 | "tmp.zig:5:20: error: comptime control flow inside runtime block", | |
| 957 | "tmp.zig:5:9: note: runtime block created here", | |
| 862 | 958 | ); |
| 863 | 959 | |
| 864 | 960 | cases.add( |
| ... | ... | @@ -872,8 +968,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 872 | 968 | \\ } |
| 873 | 969 | \\} |
| 874 | 970 | , |
| 875 | ".tmp_source.zig:5:20: error: comptime control flow inside runtime block", | |
| 876 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 971 | "tmp.zig:5:20: error: comptime control flow inside runtime block", | |
| 972 | "tmp.zig:5:9: note: runtime block created here", | |
| 877 | 973 | ); |
| 878 | 974 | |
| 879 | 975 | cases.add( |
| ... | ... | @@ -887,8 +983,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 887 | 983 | \\ } |
| 888 | 984 | \\} |
| 889 | 985 | , |
| 890 | ".tmp_source.zig:5:22: error: comptime control flow inside runtime block", | |
| 891 | ".tmp_source.zig:5:9: note: runtime block created here", | |
| 986 | "tmp.zig:5:22: error: comptime control flow inside runtime block", | |
| 987 | "tmp.zig:5:9: note: runtime block created here", | |
| 892 | 988 | ); |
| 893 | 989 | |
| 894 | 990 | cases.add( |
| ... | ... | @@ -904,7 +1000,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 904 | 1000 | \\ }; |
| 905 | 1001 | \\} |
| 906 | 1002 | , |
| 907 | ".tmp_source.zig:7:17: error: switch on type 'type' provides no expression parameter", | |
| 1003 | "tmp.zig:7:17: error: switch on type 'type' provides no expression parameter", | |
| 908 | 1004 | ); |
| 909 | 1005 | |
| 910 | 1006 | cases.add( |
| ... | ... | @@ -914,24 +1010,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 914 | 1010 | \\ foo(); |
| 915 | 1011 | \\} |
| 916 | 1012 | , |
| 917 | ".tmp_source.zig:1:1: error: non-extern function has no body", | |
| 918 | ); | |
| 919 | ||
| 920 | cases.add( | |
| 921 | "@typeInfo causing depend on itself compile error", | |
| 922 | \\const start = struct { | |
| 923 | \\ fn crash() bug() { | |
| 924 | \\ return bug; | |
| 925 | \\ } | |
| 926 | \\}; | |
| 927 | \\fn bug() void { | |
| 928 | \\ _ = @typeInfo(start).Struct; | |
| 929 | \\} | |
| 930 | \\export fn entry() void { | |
| 931 | \\ var boom = start.crash(); | |
| 932 | \\} | |
| 933 | , | |
| 934 | ".tmp_source.zig:2:5: error: 'crash' depends on itself", | |
| 1013 | "tmp.zig:1:1: error: non-extern function has no body", | |
| 935 | 1014 | ); |
| 936 | 1015 | |
| 937 | 1016 | cases.add( |
| ... | ... | @@ -942,7 +1021,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 942 | 1021 | \\ return handle_undef == handle_dummy; |
| 943 | 1022 | \\} |
| 944 | 1023 | , |
| 945 | ".tmp_source.zig:2:29: error: @handle() called outside of function definition", | |
| 1024 | "tmp.zig:2:29: error: @handle() called outside of function definition", | |
| 946 | 1025 | ); |
| 947 | 1026 | |
| 948 | 1027 | cases.add( |
| ... | ... | @@ -952,7 +1031,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 952 | 1031 | \\ return handle_undef == @handle(); |
| 953 | 1032 | \\} |
| 954 | 1033 | , |
| 955 | ".tmp_source.zig:3:28: error: @handle() in non-async function", | |
| 1034 | "tmp.zig:3:28: error: @handle() in non-async function", | |
| 956 | 1035 | ); |
| 957 | 1036 | |
| 958 | 1037 | cases.add( |
| ... | ... | @@ -962,8 +1041,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 962 | 1041 | \\ return _; |
| 963 | 1042 | \\} |
| 964 | 1043 | , |
| 965 | ".tmp_source.zig:2:5: error: `_` is not a declarable symbol", | |
| 966 | ".tmp_source.zig:3:12: error: use of undeclared identifier '_'", | |
| 1044 | "tmp.zig:2:5: error: `_` is not a declarable symbol", | |
| 1045 | "tmp.zig:3:12: error: use of undeclared identifier '_'", | |
| 967 | 1046 | ); |
| 968 | 1047 | |
| 969 | 1048 | cases.add( |
| ... | ... | @@ -976,7 +1055,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 976 | 1055 | \\ } |
| 977 | 1056 | \\} |
| 978 | 1057 | , |
| 979 | ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", | |
| 1058 | "tmp.zig:4:20: error: use of undeclared identifier '_'", | |
| 980 | 1059 | ); |
| 981 | 1060 | |
| 982 | 1061 | cases.add( |
| ... | ... | @@ -992,7 +1071,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 992 | 1071 | \\ return 1; |
| 993 | 1072 | \\} |
| 994 | 1073 | , |
| 995 | ".tmp_source.zig:4:20: error: use of undeclared identifier '_'", | |
| 1074 | "tmp.zig:4:20: error: use of undeclared identifier '_'", | |
| 996 | 1075 | ); |
| 997 | 1076 | |
| 998 | 1077 | cases.add( |
| ... | ... | @@ -1010,7 +1089,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1010 | 1089 | \\ return error.optionalReturnError; |
| 1011 | 1090 | \\} |
| 1012 | 1091 | , |
| 1013 | ".tmp_source.zig:6:17: error: use of undeclared identifier '_'", | |
| 1092 | "tmp.zig:6:17: error: use of undeclared identifier '_'", | |
| 1014 | 1093 | ); |
| 1015 | 1094 | |
| 1016 | 1095 | cases.add( |
| ... | ... | @@ -1030,9 +1109,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1030 | 1109 | \\ while (x) |_| returns() else |_| unreachable; |
| 1031 | 1110 | \\} |
| 1032 | 1111 | , |
| 1033 | ".tmp_source.zig:5:25: error: expression value is ignored", | |
| 1034 | ".tmp_source.zig:9:26: error: expression value is ignored", | |
| 1035 | ".tmp_source.zig:13:26: error: expression value is ignored", | |
| 1112 | "tmp.zig:5:25: error: expression value is ignored", | |
| 1113 | "tmp.zig:9:26: error: expression value is ignored", | |
| 1114 | "tmp.zig:13:26: error: expression value is ignored", | |
| 1036 | 1115 | ); |
| 1037 | 1116 | |
| 1038 | 1117 | cases.add( |
| ... | ... | @@ -1043,7 +1122,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1043 | 1122 | \\ dump(a); |
| 1044 | 1123 | \\} |
| 1045 | 1124 | , |
| 1046 | ".tmp_source.zig:1:9: error: missing parameter name", | |
| 1125 | "tmp.zig:1:9: error: missing parameter name", | |
| 1047 | 1126 | ); |
| 1048 | 1127 | |
| 1049 | 1128 | cases.add( |
| ... | ... | @@ -1057,7 +1136,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1057 | 1136 | \\ for (xx) |f| {} |
| 1058 | 1137 | \\} |
| 1059 | 1138 | , |
| 1060 | ".tmp_source.zig:7:15: error: variable of type 'Foo' must be const or comptime", | |
| 1139 | "tmp.zig:7:15: error: variable of type 'Foo' must be const or comptime", | |
| 1061 | 1140 | ); |
| 1062 | 1141 | |
| 1063 | 1142 | cases.add( |
| ... | ... | @@ -1068,7 +1147,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1068 | 1147 | \\ f(g); |
| 1069 | 1148 | \\} |
| 1070 | 1149 | , |
| 1071 | ".tmp_source.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime", | |
| 1150 | "tmp.zig:1:9: error: parameter of type 'fn(var)var' must be declared comptime", | |
| 1072 | 1151 | ); |
| 1073 | 1152 | |
| 1074 | 1153 | cases.add( |
| ... | ... | @@ -1082,7 +1161,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1082 | 1161 | \\}; |
| 1083 | 1162 | \\export fn entry(bar: *Bar) void {} |
| 1084 | 1163 | , |
| 1085 | ".tmp_source.zig:2:5: error: extern structs cannot contain fields of type '?*const void'", | |
| 1164 | "tmp.zig:2:5: error: extern structs cannot contain fields of type '?*const void'", | |
| 1086 | 1165 | ); |
| 1087 | 1166 | |
| 1088 | 1167 | cases.add( |
| ... | ... | @@ -1095,7 +1174,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1095 | 1174 | \\ command.exec(); |
| 1096 | 1175 | \\} |
| 1097 | 1176 | , |
| 1098 | ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior", | |
| 1177 | "tmp.zig:6:12: error: use of undefined value here causes undefined behavior", | |
| 1099 | 1178 | ); |
| 1100 | 1179 | |
| 1101 | 1180 | cases.add( |
| ... | ... | @@ -1108,7 +1187,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1108 | 1187 | \\ command.exec(); |
| 1109 | 1188 | \\} |
| 1110 | 1189 | , |
| 1111 | ".tmp_source.zig:6:12: error: use of undefined value here causes undefined behavior", | |
| 1190 | "tmp.zig:6:12: error: use of undefined value here causes undefined behavior", | |
| 1112 | 1191 | ); |
| 1113 | 1192 | |
| 1114 | 1193 | cases.add( |
| ... | ... | @@ -1118,7 +1197,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1118 | 1197 | \\ const aligned = @alignCast(4, ptr); |
| 1119 | 1198 | \\} |
| 1120 | 1199 | , |
| 1121 | ".tmp_source.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", | |
| 1200 | "tmp.zig:3:35: error: pointer address 0x1 is not aligned to 4 bytes", | |
| 1122 | 1201 | ); |
| 1123 | 1202 | |
| 1124 | 1203 | cases.add( |
| ... | ... | @@ -1127,7 +1206,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1127 | 1206 | \\ return @ptrToInt(&{}) == @ptrToInt(&{}); |
| 1128 | 1207 | \\} |
| 1129 | 1208 | , |
| 1130 | ".tmp_source.zig:2:23: error: pointer to size 0 type has no address", | |
| 1209 | "tmp.zig:2:23: error: pointer to size 0 type has no address", | |
| 1131 | 1210 | ); |
| 1132 | 1211 | |
| 1133 | 1212 | cases.add( |
| ... | ... | @@ -1136,7 +1215,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1136 | 1215 | \\ return @popCount(x); |
| 1137 | 1216 | \\} |
| 1138 | 1217 | , |
| 1139 | ".tmp_source.zig:2:22: error: expected integer type, found 'f32'", | |
| 1218 | "tmp.zig:2:22: error: expected integer type, found 'f32'", | |
| 1140 | 1219 | ); |
| 1141 | 1220 | |
| 1142 | 1221 | cases.add( |
| ... | ... | @@ -1145,7 +1224,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1145 | 1224 | \\ _ = @popCount(-1); |
| 1146 | 1225 | \\} |
| 1147 | 1226 | , |
| 1148 | ".tmp_source.zig:2:9: error: @popCount on negative comptime_int value -1", | |
| 1227 | "tmp.zig:2:9: error: @popCount on negative comptime_int value -1", | |
| 1149 | 1228 | ); |
| 1150 | 1229 | |
| 1151 | 1230 | cases.addCase(x: { |
| ... | ... | @@ -1161,10 +1240,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1161 | 1240 | \\ |
| 1162 | 1241 | \\fn bar(x: *b.Foo) void {} |
| 1163 | 1242 | , |
| 1164 | ".tmp_source.zig:6:10: error: expected type '*Foo', found '*Foo'", | |
| 1165 | ".tmp_source.zig:6:10: note: pointer type child 'Foo' cannot cast into pointer type child 'Foo'", | |
| 1166 | "a.zig:1:17: note: Foo declared here", | |
| 1167 | "b.zig:1:17: note: Foo declared here", | |
| 1243 | "tmp.zig:6:10: error: expected type '*b.Foo', found '*a.Foo'", | |
| 1244 | "tmp.zig:6:10: note: pointer type child 'a.Foo' cannot cast into pointer type child 'b.Foo'", | |
| 1245 | "a.zig:1:17: note: a.Foo declared here", | |
| 1246 | "b.zig:1:17: note: b.Foo declared here", | |
| 1168 | 1247 | ); |
| 1169 | 1248 | |
| 1170 | 1249 | tc.addSourceFile("a.zig", |
| ... | ... | @@ -1182,19 +1261,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1182 | 1261 | break :x tc; |
| 1183 | 1262 | }); |
| 1184 | 1263 | |
| 1185 | cases.add( | |
| 1186 | "enum field value references enum", | |
| 1187 | \\pub const Foo = extern enum { | |
| 1188 | \\ A = Foo.B, | |
| 1189 | \\ C = D, | |
| 1190 | \\}; | |
| 1191 | \\export fn entry() void { | |
| 1192 | \\ var s: Foo = Foo.E; | |
| 1193 | \\} | |
| 1194 | , | |
| 1195 | ".tmp_source.zig:1:17: error: 'Foo' depends on itself", | |
| 1196 | ); | |
| 1197 | ||
| 1198 | 1264 | cases.add( |
| 1199 | 1265 | "@floatToInt comptime safety", |
| 1200 | 1266 | \\comptime { |
| ... | ... | @@ -1207,9 +1273,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1207 | 1273 | \\ _ = @floatToInt(u8, f32(256.1)); |
| 1208 | 1274 | \\} |
| 1209 | 1275 | , |
| 1210 | ".tmp_source.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", | |
| 1211 | ".tmp_source.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", | |
| 1212 | ".tmp_source.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", | |
| 1276 | "tmp.zig:2:9: error: integer value '-129' cannot be stored in type 'i8'", | |
| 1277 | "tmp.zig:5:9: error: integer value '-1' cannot be stored in type 'u8'", | |
| 1278 | "tmp.zig:8:9: error: integer value '256' cannot be stored in type 'u8'", | |
| 1213 | 1279 | ); |
| 1214 | 1280 | |
| 1215 | 1281 | cases.add( |
| ... | ... | @@ -1218,7 +1284,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1218 | 1284 | \\ const a: fn () c_void = undefined; |
| 1219 | 1285 | \\} |
| 1220 | 1286 | , |
| 1221 | ".tmp_source.zig:2:20: error: return type cannot be opaque", | |
| 1287 | "tmp.zig:2:20: error: return type cannot be opaque", | |
| 1222 | 1288 | ); |
| 1223 | 1289 | |
| 1224 | 1290 | cases.add( |
| ... | ... | @@ -1231,7 +1297,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1231 | 1297 | \\ var y = p.*; |
| 1232 | 1298 | \\} |
| 1233 | 1299 | , |
| 1234 | ".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'", | |
| 1300 | "tmp.zig:4:23: error: expected type '*?*i32', found '**i32'", | |
| 1235 | 1301 | ); |
| 1236 | 1302 | |
| 1237 | 1303 | cases.add( |
| ... | ... | @@ -1240,7 +1306,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1240 | 1306 | \\ const x: [*]const bool = true; |
| 1241 | 1307 | \\} |
| 1242 | 1308 | , |
| 1243 | ".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'", | |
| 1309 | "tmp.zig:2:30: error: expected type '[*]const bool', found 'bool'", | |
| 1244 | 1310 | ); |
| 1245 | 1311 | |
| 1246 | 1312 | cases.add( |
| ... | ... | @@ -1249,7 +1315,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1249 | 1315 | \\ return x.*; |
| 1250 | 1316 | \\} |
| 1251 | 1317 | , |
| 1252 | ".tmp_source.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'", | |
| 1318 | "tmp.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'", | |
| 1253 | 1319 | ); |
| 1254 | 1320 | |
| 1255 | 1321 | cases.add( |
| ... | ... | @@ -1262,14 +1328,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1262 | 1328 | \\ foo.a += 1; |
| 1263 | 1329 | \\} |
| 1264 | 1330 | , |
| 1265 | ".tmp_source.zig:6:8: error: type '[*]Foo' does not support field access", | |
| 1331 | "tmp.zig:6:8: error: type '[*]Foo' does not support field access", | |
| 1266 | 1332 | ); |
| 1267 | 1333 | |
| 1268 | 1334 | cases.add( |
| 1269 | 1335 | "unknown length pointer to opaque", |
| 1270 | 1336 | \\export const T = [*]@OpaqueType(); |
| 1271 | 1337 | , |
| 1272 | ".tmp_source.zig:1:18: error: unknown-length pointer to opaque", | |
| 1338 | "tmp.zig:1:18: error: unknown-length pointer to opaque", | |
| 1273 | 1339 | ); |
| 1274 | 1340 | |
| 1275 | 1341 | cases.add( |
| ... | ... | @@ -1285,7 +1351,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1285 | 1351 | \\ var rule_set = try Foo.init(); |
| 1286 | 1352 | \\} |
| 1287 | 1353 | , |
| 1288 | ".tmp_source.zig:2:13: error: expected type 'i32', found 'type'", | |
| 1354 | "tmp.zig:2:13: error: expected type 'i32', found 'type'", | |
| 1289 | 1355 | ); |
| 1290 | 1356 | |
| 1291 | 1357 | cases.add( |
| ... | ... | @@ -1294,7 +1360,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1294 | 1360 | \\ const slice = ptr[0..2]; |
| 1295 | 1361 | \\} |
| 1296 | 1362 | , |
| 1297 | ".tmp_source.zig:2:22: error: slice of single-item pointer", | |
| 1363 | "tmp.zig:2:22: error: slice of single-item pointer", | |
| 1298 | 1364 | ); |
| 1299 | 1365 | |
| 1300 | 1366 | cases.add( |
| ... | ... | @@ -1303,7 +1369,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1303 | 1369 | \\ return ptr[1]; |
| 1304 | 1370 | \\} |
| 1305 | 1371 | , |
| 1306 | ".tmp_source.zig:2:15: error: index of single-item pointer", | |
| 1372 | "tmp.zig:2:15: error: index of single-item pointer", | |
| 1307 | 1373 | ); |
| 1308 | 1374 | |
| 1309 | 1375 | cases.add( |
| ... | ... | @@ -1319,10 +1385,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1319 | 1385 | \\ return null; |
| 1320 | 1386 | \\} |
| 1321 | 1387 | , |
| 1322 | ".tmp_source.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", | |
| 1323 | ".tmp_source.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", | |
| 1324 | ".tmp_source.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", | |
| 1325 | ".tmp_source.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", | |
| 1388 | "tmp.zig:5:34: error: expected type '?NextError!i32', found '?OtherError!i32'", | |
| 1389 | "tmp.zig:5:34: note: optional type child 'OtherError!i32' cannot cast into optional type child 'NextError!i32'", | |
| 1390 | "tmp.zig:5:34: note: error set 'OtherError' cannot cast into error set 'NextError'", | |
| 1391 | "tmp.zig:2:26: note: 'error.OutOfMemory' not a member of destination error set", | |
| 1326 | 1392 | ); |
| 1327 | 1393 | |
| 1328 | 1394 | cases.add( |
| ... | ... | @@ -1339,14 +1405,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1339 | 1405 | \\ Filled, |
| 1340 | 1406 | \\}; |
| 1341 | 1407 | , |
| 1342 | ".tmp_source.zig:3:17: error: attempt to dereference non-pointer type 'Tile'", | |
| 1408 | "tmp.zig:3:17: error: attempt to dereference non-pointer type 'Tile'", | |
| 1343 | 1409 | ); |
| 1344 | 1410 | |
| 1345 | 1411 | cases.add( |
| 1346 | 1412 | "invalid field access in comptime", |
| 1347 | 1413 | \\comptime { var x = doesnt_exist.whatever; } |
| 1348 | 1414 | , |
| 1349 | ".tmp_source.zig:1:20: error: use of undeclared identifier 'doesnt_exist'", | |
| 1415 | "tmp.zig:1:20: error: use of undeclared identifier 'doesnt_exist'", | |
| 1350 | 1416 | ); |
| 1351 | 1417 | |
| 1352 | 1418 | cases.add( |
| ... | ... | @@ -1367,8 +1433,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1367 | 1433 | \\ } |
| 1368 | 1434 | \\} |
| 1369 | 1435 | , |
| 1370 | ".tmp_source.zig:12:9: error: cannot suspend inside suspend block", | |
| 1371 | ".tmp_source.zig:11:5: note: other suspend block here", | |
| 1436 | "tmp.zig:12:9: error: cannot suspend inside suspend block", | |
| 1437 | "tmp.zig:11:5: note: other suspend block here", | |
| 1372 | 1438 | ); |
| 1373 | 1439 | |
| 1374 | 1440 | cases.add( |
| ... | ... | @@ -1378,8 +1444,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1378 | 1444 | \\} |
| 1379 | 1445 | \\inline fn b() void { } |
| 1380 | 1446 | , |
| 1381 | ".tmp_source.zig:2:5: error: functions marked inline must be stored in const or comptime var", | |
| 1382 | ".tmp_source.zig:4:1: note: declared here", | |
| 1447 | "tmp.zig:2:5: error: functions marked inline must be stored in const or comptime var", | |
| 1448 | "tmp.zig:4:1: note: declared here", | |
| 1383 | 1449 | ); |
| 1384 | 1450 | |
| 1385 | 1451 | cases.add( |
| ... | ... | @@ -1389,7 +1455,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1389 | 1455 | \\ @panic(e); |
| 1390 | 1456 | \\} |
| 1391 | 1457 | , |
| 1392 | ".tmp_source.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", | |
| 1458 | "tmp.zig:3:12: error: expected type '[]const u8', found 'error{Foo}'", | |
| 1393 | 1459 | ); |
| 1394 | 1460 | |
| 1395 | 1461 | cases.add( |
| ... | ... | @@ -1403,8 +1469,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1403 | 1469 | \\ var tagName = @tagName(fi); |
| 1404 | 1470 | \\} |
| 1405 | 1471 | , |
| 1406 | ".tmp_source.zig:7:19: error: union has no associated enum", | |
| 1407 | ".tmp_source.zig:1:18: note: declared here", | |
| 1472 | "tmp.zig:7:19: error: union has no associated enum", | |
| 1473 | "tmp.zig:1:18: note: declared here", | |
| 1408 | 1474 | ); |
| 1409 | 1475 | |
| 1410 | 1476 | cases.add( |
| ... | ... | @@ -1417,7 +1483,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1417 | 1483 | \\ return error.ShouldBeCompileError; |
| 1418 | 1484 | \\} |
| 1419 | 1485 | , |
| 1420 | ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", | |
| 1486 | "tmp.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'", | |
| 1421 | 1487 | ); |
| 1422 | 1488 | |
| 1423 | 1489 | cases.add( |
| ... | ... | @@ -1426,7 +1492,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1426 | 1492 | \\ var s = (struct{v: var}){.v=i32(10)}; |
| 1427 | 1493 | \\} |
| 1428 | 1494 | , |
| 1429 | ".tmp_source.zig:2:23: error: invalid token: 'var'", | |
| 1495 | "tmp.zig:2:23: error: invalid token: 'var'", | |
| 1430 | 1496 | ); |
| 1431 | 1497 | |
| 1432 | 1498 | cases.add( |
| ... | ... | @@ -1436,7 +1502,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1436 | 1502 | \\ const y = @ptrCast(*i32, &x); |
| 1437 | 1503 | \\} |
| 1438 | 1504 | , |
| 1439 | ".tmp_source.zig:3:15: error: cast discards const qualifier", | |
| 1505 | "tmp.zig:3:15: error: cast discards const qualifier", | |
| 1440 | 1506 | ); |
| 1441 | 1507 | |
| 1442 | 1508 | cases.add( |
| ... | ... | @@ -1445,7 +1511,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1445 | 1511 | \\ const slice = ([*]i32)(undefined)[0..1]; |
| 1446 | 1512 | \\} |
| 1447 | 1513 | , |
| 1448 | ".tmp_source.zig:2:38: error: non-zero length slice of undefined pointer", | |
| 1514 | "tmp.zig:2:38: error: non-zero length slice of undefined pointer", | |
| 1449 | 1515 | ); |
| 1450 | 1516 | |
| 1451 | 1517 | cases.add( |
| ... | ... | @@ -1460,7 +1526,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1460 | 1526 | \\ a(c); |
| 1461 | 1527 | \\} |
| 1462 | 1528 | , |
| 1463 | ".tmp_source.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", | |
| 1529 | "tmp.zig:8:7: error: expected type 'fn(*const u8) void', found 'fn(u8) void'", | |
| 1464 | 1530 | ); |
| 1465 | 1531 | |
| 1466 | 1532 | cases.add( |
| ... | ... | @@ -1474,7 +1540,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1474 | 1540 | \\ } |
| 1475 | 1541 | \\} |
| 1476 | 1542 | , |
| 1477 | ".tmp_source.zig:5:5: error: else prong required when switching on type 'anyerror'", | |
| 1543 | "tmp.zig:5:5: error: else prong required when switching on type 'anyerror'", | |
| 1478 | 1544 | ); |
| 1479 | 1545 | |
| 1480 | 1546 | cases.add( |
| ... | ... | @@ -1485,7 +1551,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1485 | 1551 | \\fn foo() !void { |
| 1486 | 1552 | \\} |
| 1487 | 1553 | , |
| 1488 | ".tmp_source.zig:4:11: error: function with inferred error set must return at least one possible error", | |
| 1554 | "tmp.zig:4:11: error: function with inferred error set must return at least one possible error", | |
| 1489 | 1555 | ); |
| 1490 | 1556 | |
| 1491 | 1557 | cases.add( |
| ... | ... | @@ -1504,8 +1570,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1504 | 1570 | \\ } |
| 1505 | 1571 | \\} |
| 1506 | 1572 | , |
| 1507 | ".tmp_source.zig:2:26: error: error.Baz not handled in switch", | |
| 1508 | ".tmp_source.zig:2:26: error: error.Bar not handled in switch", | |
| 1573 | "tmp.zig:2:26: error: error.Baz not handled in switch", | |
| 1574 | "tmp.zig:2:26: error: error.Bar not handled in switch", | |
| 1509 | 1575 | ); |
| 1510 | 1576 | |
| 1511 | 1577 | cases.add( |
| ... | ... | @@ -1526,8 +1592,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1526 | 1592 | \\ } |
| 1527 | 1593 | \\} |
| 1528 | 1594 | , |
| 1529 | ".tmp_source.zig:5:14: error: duplicate switch value: '@typeOf(foo).ReturnType.ErrorSet.Foo'", | |
| 1530 | ".tmp_source.zig:3:14: note: other value is here", | |
| 1595 | "tmp.zig:5:14: error: duplicate switch value: '@typeOf(foo).ReturnType.ErrorSet.Foo'", | |
| 1596 | "tmp.zig:3:14: note: other value is here", | |
| 1531 | 1597 | ); |
| 1532 | 1598 | |
| 1533 | 1599 | cases.add("invalid cast from integral type to enum", |
| ... | ... | @@ -1542,7 +1608,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1542 | 1608 | \\ E.One => {}, |
| 1543 | 1609 | \\ } |
| 1544 | 1610 | \\} |
| 1545 | , ".tmp_source.zig:9:10: error: expected type 'usize', found 'E'"); | |
| 1611 | , "tmp.zig:9:10: error: expected type 'usize', found 'E'"); | |
| 1546 | 1612 | |
| 1547 | 1613 | cases.add( |
| 1548 | 1614 | "range operator in switch used on error set", |
| ... | ... | @@ -1560,7 +1626,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1560 | 1626 | \\ } |
| 1561 | 1627 | \\} |
| 1562 | 1628 | , |
| 1563 | ".tmp_source.zig:3:17: error: operator not allowed for errors", | |
| 1629 | "tmp.zig:3:17: error: operator not allowed for errors", | |
| 1564 | 1630 | ); |
| 1565 | 1631 | |
| 1566 | 1632 | cases.add( |
| ... | ... | @@ -1569,7 +1635,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1569 | 1635 | \\ const z: ?fn()!void = null; |
| 1570 | 1636 | \\} |
| 1571 | 1637 | , |
| 1572 | ".tmp_source.zig:2:15: error: inferring error set of return type valid only for function definitions", | |
| 1638 | "tmp.zig:2:15: error: inferring error set of return type valid only for function definitions", | |
| 1573 | 1639 | ); |
| 1574 | 1640 | |
| 1575 | 1641 | cases.add( |
| ... | ... | @@ -1579,7 +1645,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1579 | 1645 | \\ const z = Foo.Bar; |
| 1580 | 1646 | \\} |
| 1581 | 1647 | , |
| 1582 | ".tmp_source.zig:3:18: error: no error named 'Bar' in 'Foo'", | |
| 1648 | "tmp.zig:3:18: error: no error named 'Bar' in 'Foo'", | |
| 1583 | 1649 | ); |
| 1584 | 1650 | |
| 1585 | 1651 | cases.add( |
| ... | ... | @@ -1588,7 +1654,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1588 | 1654 | \\ const z = i32!i32; |
| 1589 | 1655 | \\} |
| 1590 | 1656 | , |
| 1591 | ".tmp_source.zig:2:15: error: expected error set type, found type 'i32'", | |
| 1657 | "tmp.zig:2:15: error: expected error set type, found type 'i32'", | |
| 1592 | 1658 | ); |
| 1593 | 1659 | |
| 1594 | 1660 | cases.add( |
| ... | ... | @@ -1604,7 +1670,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1604 | 1670 | \\ } |
| 1605 | 1671 | \\} |
| 1606 | 1672 | , |
| 1607 | ".tmp_source.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors", | |
| 1673 | "tmp.zig:7:11: error: error sets 'Set1' and 'Set2' have no common errors", | |
| 1608 | 1674 | ); |
| 1609 | 1675 | |
| 1610 | 1676 | cases.add( |
| ... | ... | @@ -1613,7 +1679,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1613 | 1679 | \\ const z = error.A > error.B; |
| 1614 | 1680 | \\} |
| 1615 | 1681 | , |
| 1616 | ".tmp_source.zig:2:23: error: operator not allowed for errors", | |
| 1682 | "tmp.zig:2:23: error: operator not allowed for errors", | |
| 1617 | 1683 | ); |
| 1618 | 1684 | |
| 1619 | 1685 | cases.add( |
| ... | ... | @@ -1625,7 +1691,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1625 | 1691 | \\ var y = @errSetCast(Set2, x); |
| 1626 | 1692 | \\} |
| 1627 | 1693 | , |
| 1628 | ".tmp_source.zig:5:13: error: error.B not a member of error set 'Set2'", | |
| 1694 | "tmp.zig:5:13: error: error.B not a member of error set 'Set2'", | |
| 1629 | 1695 | ); |
| 1630 | 1696 | |
| 1631 | 1697 | cases.add( |
| ... | ... | @@ -1638,9 +1704,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1638 | 1704 | \\ return error.B; |
| 1639 | 1705 | \\} |
| 1640 | 1706 | , |
| 1641 | ".tmp_source.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", | |
| 1642 | ".tmp_source.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", | |
| 1643 | ".tmp_source.zig:3:35: note: cannot cast global error set into smaller set", | |
| 1707 | "tmp.zig:3:35: error: expected type 'SmallErrorSet!i32', found 'anyerror!i32'", | |
| 1708 | "tmp.zig:3:35: note: error set 'anyerror' cannot cast into error set 'SmallErrorSet'", | |
| 1709 | "tmp.zig:3:35: note: cannot cast global error set into smaller set", | |
| 1644 | 1710 | ); |
| 1645 | 1711 | |
| 1646 | 1712 | cases.add( |
| ... | ... | @@ -1653,10 +1719,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1653 | 1719 | \\ return error.B; |
| 1654 | 1720 | \\} |
| 1655 | 1721 | , |
| 1656 | ".tmp_source.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", | |
| 1657 | ".tmp_source.zig:3:31: note: cannot cast global error set into smaller set", | |
| 1722 | "tmp.zig:3:31: error: expected type 'SmallErrorSet', found 'anyerror'", | |
| 1723 | "tmp.zig:3:31: note: cannot cast global error set into smaller set", | |
| 1658 | 1724 | ); |
| 1659 | ||
| 1660 | 1725 | cases.add( |
| 1661 | 1726 | "recursive inferred error set", |
| 1662 | 1727 | \\export fn entry() void { |
| ... | ... | @@ -1666,7 +1731,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1666 | 1731 | \\ try foo(); |
| 1667 | 1732 | \\} |
| 1668 | 1733 | , |
| 1669 | ".tmp_source.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet", | |
| 1734 | "tmp.zig:5:5: error: cannot resolve inferred error set '@typeOf(foo).ReturnType.ErrorSet': function 'foo' not fully analyzed yet", | |
| 1670 | 1735 | ); |
| 1671 | 1736 | |
| 1672 | 1737 | cases.add( |
| ... | ... | @@ -1680,8 +1745,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1680 | 1745 | \\ var x: Set2 = set1; |
| 1681 | 1746 | \\} |
| 1682 | 1747 | , |
| 1683 | ".tmp_source.zig:7:19: error: expected type 'Set2', found 'Set1'", | |
| 1684 | ".tmp_source.zig:1:23: note: 'error.B' not a member of destination error set", | |
| 1748 | "tmp.zig:7:19: error: expected type 'Set2', found 'Set1'", | |
| 1749 | "tmp.zig:1:23: note: 'error.B' not a member of destination error set", | |
| 1685 | 1750 | ); |
| 1686 | 1751 | |
| 1687 | 1752 | cases.add( |
| ... | ... | @@ -1695,7 +1760,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1695 | 1760 | \\ var y = @intToError(x); |
| 1696 | 1761 | \\} |
| 1697 | 1762 | , |
| 1698 | ".tmp_source.zig:7:13: error: integer value 3 represents no error", | |
| 1763 | "tmp.zig:7:13: error: integer value 3 represents no error", | |
| 1699 | 1764 | ); |
| 1700 | 1765 | |
| 1701 | 1766 | cases.add( |
| ... | ... | @@ -1713,7 +1778,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1713 | 1778 | \\ var y = @errSetCast(Set2, @intToError(x)); |
| 1714 | 1779 | \\} |
| 1715 | 1780 | , |
| 1716 | ".tmp_source.zig:11:13: error: error.B not a member of error set 'Set2'", | |
| 1781 | "tmp.zig:11:13: error: error.B not a member of error set 'Set2'", | |
| 1717 | 1782 | ); |
| 1718 | 1783 | |
| 1719 | 1784 | cases.add( |
| ... | ... | @@ -1722,7 +1787,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1722 | 1787 | \\ _ = @memberCount(anyerror); |
| 1723 | 1788 | \\} |
| 1724 | 1789 | , |
| 1725 | ".tmp_source.zig:2:9: error: global error set member count not available at comptime", | |
| 1790 | "tmp.zig:2:9: error: global error set member count not available at comptime", | |
| 1726 | 1791 | ); |
| 1727 | 1792 | |
| 1728 | 1793 | cases.add( |
| ... | ... | @@ -1735,8 +1800,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1735 | 1800 | \\ const a: Foo = undefined; |
| 1736 | 1801 | \\} |
| 1737 | 1802 | , |
| 1738 | ".tmp_source.zig:3:5: error: duplicate error: 'Bar'", | |
| 1739 | ".tmp_source.zig:2:5: note: other error here", | |
| 1803 | "tmp.zig:3:5: error: duplicate error: 'Bar'", | |
| 1804 | "tmp.zig:2:5: note: other error here", | |
| 1740 | 1805 | ); |
| 1741 | 1806 | |
| 1742 | 1807 | cases.add( |
| ... | ... | @@ -1745,7 +1810,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1745 | 1810 | \\ const x = usize(-10); |
| 1746 | 1811 | \\} |
| 1747 | 1812 | , |
| 1748 | ".tmp_source.zig:2:21: error: cannot cast negative value -10 to unsigned integer type 'usize'", | |
| 1813 | "tmp.zig:2:21: error: cannot cast negative value -10 to unsigned integer type 'usize'", | |
| 1749 | 1814 | ); |
| 1750 | 1815 | |
| 1751 | 1816 | cases.add( |
| ... | ... | @@ -1755,7 +1820,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1755 | 1820 | \\ var arr: [v]u8 = undefined; |
| 1756 | 1821 | \\} |
| 1757 | 1822 | , |
| 1758 | ".tmp_source.zig:1:1: error: unable to infer variable type", | |
| 1823 | "tmp.zig:1:1: error: unable to infer variable type", | |
| 1759 | 1824 | ); |
| 1760 | 1825 | |
| 1761 | 1826 | cases.add( |
| ... | ... | @@ -1768,8 +1833,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1768 | 1833 | \\ const a: Foo = undefined; |
| 1769 | 1834 | \\} |
| 1770 | 1835 | , |
| 1771 | ".tmp_source.zig:3:5: error: duplicate struct field: 'Bar'", | |
| 1772 | ".tmp_source.zig:2:5: note: other field here", | |
| 1836 | "tmp.zig:3:5: error: duplicate struct field: 'Bar'", | |
| 1837 | "tmp.zig:2:5: note: other field here", | |
| 1773 | 1838 | ); |
| 1774 | 1839 | |
| 1775 | 1840 | cases.add( |
| ... | ... | @@ -1782,8 +1847,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1782 | 1847 | \\ const a: Foo = undefined; |
| 1783 | 1848 | \\} |
| 1784 | 1849 | , |
| 1785 | ".tmp_source.zig:3:5: error: duplicate union field: 'Bar'", | |
| 1786 | ".tmp_source.zig:2:5: note: other field here", | |
| 1850 | "tmp.zig:3:5: error: duplicate union field: 'Bar'", | |
| 1851 | "tmp.zig:2:5: note: other field here", | |
| 1787 | 1852 | ); |
| 1788 | 1853 | |
| 1789 | 1854 | cases.add( |
| ... | ... | @@ -1797,8 +1862,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1797 | 1862 | \\ const a: Foo = undefined; |
| 1798 | 1863 | \\} |
| 1799 | 1864 | , |
| 1800 | ".tmp_source.zig:3:5: error: duplicate enum field: 'Bar'", | |
| 1801 | ".tmp_source.zig:2:5: note: other field here", | |
| 1865 | "tmp.zig:3:5: error: duplicate enum field: 'Bar'", | |
| 1866 | "tmp.zig:2:5: note: other field here", | |
| 1802 | 1867 | ); |
| 1803 | 1868 | |
| 1804 | 1869 | cases.add( |
| ... | ... | @@ -1808,15 +1873,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1808 | 1873 | \\} |
| 1809 | 1874 | \\nakedcc fn foo() void { } |
| 1810 | 1875 | , |
| 1811 | ".tmp_source.zig:2:5: error: unable to call function with naked calling convention", | |
| 1812 | ".tmp_source.zig:4:1: note: declared here", | |
| 1876 | "tmp.zig:2:5: error: unable to call function with naked calling convention", | |
| 1877 | "tmp.zig:4:1: note: declared here", | |
| 1813 | 1878 | ); |
| 1814 | 1879 | |
| 1815 | 1880 | cases.add( |
| 1816 | 1881 | "function with invalid return type", |
| 1817 | 1882 | \\export fn foo() boid {} |
| 1818 | 1883 | , |
| 1819 | ".tmp_source.zig:1:17: error: use of undeclared identifier 'boid'", | |
| 1884 | "tmp.zig:1:17: error: use of undeclared identifier 'boid'", | |
| 1820 | 1885 | ); |
| 1821 | 1886 | |
| 1822 | 1887 | cases.add( |
| ... | ... | @@ -1824,7 +1889,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1824 | 1889 | \\const Foo = enum { A, B, C }; |
| 1825 | 1890 | \\export fn entry(foo: Foo) void { } |
| 1826 | 1891 | , |
| 1827 | ".tmp_source.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", | |
| 1892 | "tmp.zig:2:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", | |
| 1828 | 1893 | ); |
| 1829 | 1894 | |
| 1830 | 1895 | cases.add( |
| ... | ... | @@ -1836,7 +1901,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1836 | 1901 | \\}; |
| 1837 | 1902 | \\export fn entry(foo: Foo) void { } |
| 1838 | 1903 | , |
| 1839 | ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", | |
| 1904 | "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", | |
| 1840 | 1905 | ); |
| 1841 | 1906 | |
| 1842 | 1907 | cases.add( |
| ... | ... | @@ -1848,7 +1913,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1848 | 1913 | \\}; |
| 1849 | 1914 | \\export fn entry(foo: Foo) void { } |
| 1850 | 1915 | , |
| 1851 | ".tmp_source.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", | |
| 1916 | "tmp.zig:6:22: error: parameter of type 'Foo' not allowed in function with calling convention 'ccc'", | |
| 1852 | 1917 | ); |
| 1853 | 1918 | |
| 1854 | 1919 | cases.add( |
| ... | ... | @@ -1860,7 +1925,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1860 | 1925 | \\ switch (f) {} |
| 1861 | 1926 | \\} |
| 1862 | 1927 | , |
| 1863 | ".tmp_source.zig:5:5: error: enumeration value 'Foo.M' not handled in switch", | |
| 1928 | "tmp.zig:5:5: error: enumeration value 'Foo.M' not handled in switch", | |
| 1864 | 1929 | ); |
| 1865 | 1930 | |
| 1866 | 1931 | cases.add( |
| ... | ... | @@ -1869,7 +1934,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1869 | 1934 | \\ var a = 1 >> -1; |
| 1870 | 1935 | \\} |
| 1871 | 1936 | , |
| 1872 | ".tmp_source.zig:2:18: error: shift by negative value -1", | |
| 1937 | "tmp.zig:2:18: error: shift by negative value -1", | |
| 1873 | 1938 | ); |
| 1874 | 1939 | |
| 1875 | 1940 | cases.add( |
| ... | ... | @@ -1880,7 +1945,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1880 | 1945 | \\ } |
| 1881 | 1946 | \\} |
| 1882 | 1947 | , |
| 1883 | ".tmp_source.zig:3:9: error: encountered @panic at compile-time", | |
| 1948 | "tmp.zig:3:9: error: encountered @panic at compile-time", | |
| 1884 | 1949 | ); |
| 1885 | 1950 | |
| 1886 | 1951 | cases.add( |
| ... | ... | @@ -1910,7 +1975,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1910 | 1975 | \\ bogus; |
| 1911 | 1976 | \\} |
| 1912 | 1977 | , |
| 1913 | ".tmp_source.zig:8:5: error: use of undeclared identifier 'bogus'", | |
| 1978 | "tmp.zig:8:5: error: use of undeclared identifier 'bogus'", | |
| 1914 | 1979 | ); |
| 1915 | 1980 | |
| 1916 | 1981 | cases.add( |
| ... | ... | @@ -1923,7 +1988,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1923 | 1988 | \\ } |
| 1924 | 1989 | \\} |
| 1925 | 1990 | , |
| 1926 | ".tmp_source.zig:4:13: error: label not found: 'outer'", | |
| 1991 | "tmp.zig:4:13: error: label not found: 'outer'", | |
| 1927 | 1992 | ); |
| 1928 | 1993 | |
| 1929 | 1994 | cases.add( |
| ... | ... | @@ -1937,7 +2002,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1937 | 2002 | \\ } |
| 1938 | 2003 | \\} |
| 1939 | 2004 | , |
| 1940 | ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'", | |
| 2005 | "tmp.zig:5:13: error: labeled loop not found: 'outer'", | |
| 1941 | 2006 | ); |
| 1942 | 2007 | |
| 1943 | 2008 | cases.add( |
| ... | ... | @@ -1950,8 +2015,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1950 | 2015 | \\ |
| 1951 | 2016 | \\extern fn bar(x: *void) void { } |
| 1952 | 2017 | , |
| 1953 | ".tmp_source.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", | |
| 1954 | ".tmp_source.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", | |
| 2018 | "tmp.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", | |
| 2019 | "tmp.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", | |
| 1955 | 2020 | ); |
| 1956 | 2021 | |
| 1957 | 2022 | cases.add( |
| ... | ... | @@ -1963,7 +2028,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1963 | 2028 | \\ var bad = {}; |
| 1964 | 2029 | \\} |
| 1965 | 2030 | , |
| 1966 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2031 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 1967 | 2032 | ); |
| 1968 | 2033 | |
| 1969 | 2034 | cases.add( |
| ... | ... | @@ -1975,7 +2040,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1975 | 2040 | \\ var bad = {}; |
| 1976 | 2041 | \\} |
| 1977 | 2042 | , |
| 1978 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2043 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 1979 | 2044 | ); |
| 1980 | 2045 | |
| 1981 | 2046 | cases.add( |
| ... | ... | @@ -1987,7 +2052,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1987 | 2052 | \\ var bad = {}; |
| 1988 | 2053 | \\} |
| 1989 | 2054 | , |
| 1990 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2055 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 1991 | 2056 | ); |
| 1992 | 2057 | |
| 1993 | 2058 | cases.add( |
| ... | ... | @@ -1999,7 +2064,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 1999 | 2064 | \\ var bad = {}; |
| 2000 | 2065 | \\} |
| 2001 | 2066 | , |
| 2002 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2067 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2003 | 2068 | ); |
| 2004 | 2069 | |
| 2005 | 2070 | cases.add( |
| ... | ... | @@ -2011,7 +2076,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2011 | 2076 | \\ var bad = {}; |
| 2012 | 2077 | \\} |
| 2013 | 2078 | , |
| 2014 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2079 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2015 | 2080 | ); |
| 2016 | 2081 | |
| 2017 | 2082 | cases.add( |
| ... | ... | @@ -2023,7 +2088,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2023 | 2088 | \\ var bad = {}; |
| 2024 | 2089 | \\} |
| 2025 | 2090 | , |
| 2026 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2091 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2027 | 2092 | ); |
| 2028 | 2093 | |
| 2029 | 2094 | cases.add( |
| ... | ... | @@ -2035,7 +2100,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2035 | 2100 | \\ var bad = {}; |
| 2036 | 2101 | \\} |
| 2037 | 2102 | , |
| 2038 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2103 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2039 | 2104 | ); |
| 2040 | 2105 | |
| 2041 | 2106 | cases.add( |
| ... | ... | @@ -2047,7 +2112,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2047 | 2112 | \\ var bad = {}; |
| 2048 | 2113 | \\} |
| 2049 | 2114 | , |
| 2050 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2115 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2051 | 2116 | ); |
| 2052 | 2117 | |
| 2053 | 2118 | cases.add( |
| ... | ... | @@ -2059,7 +2124,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2059 | 2124 | \\ var bad = {}; |
| 2060 | 2125 | \\} |
| 2061 | 2126 | , |
| 2062 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2127 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2063 | 2128 | ); |
| 2064 | 2129 | |
| 2065 | 2130 | cases.add( |
| ... | ... | @@ -2071,7 +2136,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2071 | 2136 | \\ var bad = {}; |
| 2072 | 2137 | \\} |
| 2073 | 2138 | , |
| 2074 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2139 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2075 | 2140 | ); |
| 2076 | 2141 | |
| 2077 | 2142 | cases.add( |
| ... | ... | @@ -2083,7 +2148,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2083 | 2148 | \\ var bad = {}; |
| 2084 | 2149 | \\} |
| 2085 | 2150 | , |
| 2086 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2151 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2087 | 2152 | ); |
| 2088 | 2153 | |
| 2089 | 2154 | cases.add( |
| ... | ... | @@ -2095,7 +2160,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2095 | 2160 | \\ var bad = {}; |
| 2096 | 2161 | \\} |
| 2097 | 2162 | , |
| 2098 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2163 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2099 | 2164 | ); |
| 2100 | 2165 | |
| 2101 | 2166 | cases.add( |
| ... | ... | @@ -2107,7 +2172,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2107 | 2172 | \\ var bad = {}; |
| 2108 | 2173 | \\} |
| 2109 | 2174 | , |
| 2110 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2175 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2111 | 2176 | ); |
| 2112 | 2177 | |
| 2113 | 2178 | cases.add( |
| ... | ... | @@ -2119,7 +2184,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2119 | 2184 | \\ var bad = {}; |
| 2120 | 2185 | \\} |
| 2121 | 2186 | , |
| 2122 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2187 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2123 | 2188 | ); |
| 2124 | 2189 | |
| 2125 | 2190 | cases.add( |
| ... | ... | @@ -2131,7 +2196,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2131 | 2196 | \\ var bad = {}; |
| 2132 | 2197 | \\} |
| 2133 | 2198 | , |
| 2134 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2199 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2135 | 2200 | ); |
| 2136 | 2201 | |
| 2137 | 2202 | cases.add( |
| ... | ... | @@ -2143,7 +2208,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2143 | 2208 | \\ var bad = {}; |
| 2144 | 2209 | \\} |
| 2145 | 2210 | , |
| 2146 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2211 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2147 | 2212 | ); |
| 2148 | 2213 | |
| 2149 | 2214 | cases.add( |
| ... | ... | @@ -2155,7 +2220,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2155 | 2220 | \\ var bad = {}; |
| 2156 | 2221 | \\} |
| 2157 | 2222 | , |
| 2158 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2223 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2159 | 2224 | ); |
| 2160 | 2225 | |
| 2161 | 2226 | cases.add( |
| ... | ... | @@ -2167,7 +2232,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2167 | 2232 | \\ var bad = {}; |
| 2168 | 2233 | \\} |
| 2169 | 2234 | , |
| 2170 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2235 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2171 | 2236 | ); |
| 2172 | 2237 | |
| 2173 | 2238 | cases.add( |
| ... | ... | @@ -2179,7 +2244,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2179 | 2244 | \\ var bad = {}; |
| 2180 | 2245 | \\} |
| 2181 | 2246 | , |
| 2182 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2247 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2183 | 2248 | ); |
| 2184 | 2249 | |
| 2185 | 2250 | cases.add( |
| ... | ... | @@ -2191,7 +2256,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2191 | 2256 | \\ var bad = {}; |
| 2192 | 2257 | \\} |
| 2193 | 2258 | , |
| 2194 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2259 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2195 | 2260 | ); |
| 2196 | 2261 | |
| 2197 | 2262 | cases.add( |
| ... | ... | @@ -2203,7 +2268,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2203 | 2268 | \\ var bad = {}; |
| 2204 | 2269 | \\} |
| 2205 | 2270 | , |
| 2206 | ".tmp_source.zig:5:5: error: expected token ';', found 'var'", | |
| 2271 | "tmp.zig:5:5: error: expected token ';', found 'var'", | |
| 2207 | 2272 | ); |
| 2208 | 2273 | |
| 2209 | 2274 | cases.add( |
| ... | ... | @@ -2212,7 +2277,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2212 | 2277 | \\fn a() void {} |
| 2213 | 2278 | \\export fn entry() void { a(); } |
| 2214 | 2279 | , |
| 2215 | ".tmp_source.zig:2:1: error: redefinition of 'a'", | |
| 2280 | "tmp.zig:2:1: error: redefinition of 'a'", | |
| 2216 | 2281 | ); |
| 2217 | 2282 | |
| 2218 | 2283 | cases.add( |
| ... | ... | @@ -2220,7 +2285,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2220 | 2285 | \\fn a() noreturn {return;} |
| 2221 | 2286 | \\export fn entry() void { a(); } |
| 2222 | 2287 | , |
| 2223 | ".tmp_source.zig:1:18: error: expected type 'noreturn', found 'void'", | |
| 2288 | "tmp.zig:1:18: error: expected type 'noreturn', found 'void'", | |
| 2224 | 2289 | ); |
| 2225 | 2290 | |
| 2226 | 2291 | cases.add( |
| ... | ... | @@ -2228,7 +2293,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2228 | 2293 | \\fn a() i32 {} |
| 2229 | 2294 | \\export fn entry() void { _ = a(); } |
| 2230 | 2295 | , |
| 2231 | ".tmp_source.zig:1:12: error: expected type 'i32', found 'void'", | |
| 2296 | "tmp.zig:1:12: error: expected type 'i32', found 'void'", | |
| 2232 | 2297 | ); |
| 2233 | 2298 | |
| 2234 | 2299 | cases.add( |
| ... | ... | @@ -2237,7 +2302,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2237 | 2302 | \\ b(); |
| 2238 | 2303 | \\} |
| 2239 | 2304 | , |
| 2240 | ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'", | |
| 2305 | "tmp.zig:2:5: error: use of undeclared identifier 'b'", | |
| 2241 | 2306 | ); |
| 2242 | 2307 | |
| 2243 | 2308 | cases.add( |
| ... | ... | @@ -2247,7 +2312,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2247 | 2312 | \\} |
| 2248 | 2313 | \\fn b(a: i32, b: i32, c: i32) void { } |
| 2249 | 2314 | , |
| 2250 | ".tmp_source.zig:2:6: error: expected 3 arguments, found 1", | |
| 2315 | "tmp.zig:2:6: error: expected 3 arguments, found 1", | |
| 2251 | 2316 | ); |
| 2252 | 2317 | |
| 2253 | 2318 | cases.add( |
| ... | ... | @@ -2255,7 +2320,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2255 | 2320 | \\fn a() bogus {} |
| 2256 | 2321 | \\export fn entry() void { _ = a(); } |
| 2257 | 2322 | , |
| 2258 | ".tmp_source.zig:1:8: error: use of undeclared identifier 'bogus'", | |
| 2323 | "tmp.zig:1:8: error: use of undeclared identifier 'bogus'", | |
| 2259 | 2324 | ); |
| 2260 | 2325 | |
| 2261 | 2326 | cases.add( |
| ... | ... | @@ -2263,7 +2328,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2263 | 2328 | \\fn a() *noreturn {} |
| 2264 | 2329 | \\export fn entry() void { _ = a(); } |
| 2265 | 2330 | , |
| 2266 | ".tmp_source.zig:1:8: error: pointer to noreturn not allowed", | |
| 2331 | "tmp.zig:1:8: error: pointer to noreturn not allowed", | |
| 2267 | 2332 | ); |
| 2268 | 2333 | |
| 2269 | 2334 | cases.add( |
| ... | ... | @@ -2275,7 +2340,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2275 | 2340 | \\ |
| 2276 | 2341 | \\fn b() void {} |
| 2277 | 2342 | , |
| 2278 | ".tmp_source.zig:3:5: error: unreachable code", | |
| 2343 | "tmp.zig:3:5: error: unreachable code", | |
| 2279 | 2344 | ); |
| 2280 | 2345 | |
| 2281 | 2346 | cases.add( |
| ... | ... | @@ -2283,7 +2348,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2283 | 2348 | \\const bogus = @import("bogus-does-not-exist.zig",); |
| 2284 | 2349 | \\export fn entry() void { bogus.bogo(); } |
| 2285 | 2350 | , |
| 2286 | ".tmp_source.zig:1:15: error: unable to find 'bogus-does-not-exist.zig'", | |
| 2351 | "tmp.zig:1:15: error: unable to find 'bogus-does-not-exist.zig'", | |
| 2287 | 2352 | ); |
| 2288 | 2353 | |
| 2289 | 2354 | cases.add( |
| ... | ... | @@ -2294,8 +2359,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2294 | 2359 | \\ c; |
| 2295 | 2360 | \\} |
| 2296 | 2361 | , |
| 2297 | ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", | |
| 2298 | ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'", | |
| 2362 | "tmp.zig:3:5: error: use of undeclared identifier 'b'", | |
| 2363 | "tmp.zig:4:5: error: use of undeclared identifier 'c'", | |
| 2299 | 2364 | ); |
| 2300 | 2365 | |
| 2301 | 2366 | cases.add( |
| ... | ... | @@ -2304,7 +2369,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2304 | 2369 | \\} |
| 2305 | 2370 | \\export fn entry() void { f(1, 2); } |
| 2306 | 2371 | , |
| 2307 | ".tmp_source.zig:1:15: error: redeclaration of variable 'a'", | |
| 2372 | "tmp.zig:1:15: error: redeclaration of variable 'a'", | |
| 2308 | 2373 | ); |
| 2309 | 2374 | |
| 2310 | 2375 | cases.add( |
| ... | ... | @@ -2314,7 +2379,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2314 | 2379 | \\ const a = 0; |
| 2315 | 2380 | \\} |
| 2316 | 2381 | , |
| 2317 | ".tmp_source.zig:3:5: error: redeclaration of variable 'a'", | |
| 2382 | "tmp.zig:3:5: error: redeclaration of variable 'a'", | |
| 2318 | 2383 | ); |
| 2319 | 2384 | |
| 2320 | 2385 | cases.add( |
| ... | ... | @@ -2324,7 +2389,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2324 | 2389 | \\} |
| 2325 | 2390 | \\export fn entry() void { f(1); } |
| 2326 | 2391 | , |
| 2327 | ".tmp_source.zig:2:5: error: redeclaration of variable 'a'", | |
| 2392 | "tmp.zig:2:5: error: redeclaration of variable 'a'", | |
| 2328 | 2393 | ); |
| 2329 | 2394 | |
| 2330 | 2395 | cases.add( |
| ... | ... | @@ -2334,7 +2399,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2334 | 2399 | \\ return a; |
| 2335 | 2400 | \\} |
| 2336 | 2401 | , |
| 2337 | ".tmp_source.zig:3:12: error: expected type 'i32', found '[*]const u8'", | |
| 2402 | "tmp.zig:3:12: error: expected type 'i32', found '[*]const u8'", | |
| 2338 | 2403 | ); |
| 2339 | 2404 | |
| 2340 | 2405 | cases.add( |
| ... | ... | @@ -2343,7 +2408,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2343 | 2408 | \\ if (0) {} |
| 2344 | 2409 | \\} |
| 2345 | 2410 | , |
| 2346 | ".tmp_source.zig:2:9: error: expected type 'bool', found 'comptime_int'", | |
| 2411 | "tmp.zig:2:9: error: expected type 'bool', found 'comptime_int'", | |
| 2347 | 2412 | ); |
| 2348 | 2413 | |
| 2349 | 2414 | cases.add( |
| ... | ... | @@ -2352,7 +2417,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2352 | 2417 | \\ const a = return; |
| 2353 | 2418 | \\} |
| 2354 | 2419 | , |
| 2355 | ".tmp_source.zig:2:5: error: unreachable code", | |
| 2420 | "tmp.zig:2:5: error: unreachable code", | |
| 2356 | 2421 | ); |
| 2357 | 2422 | |
| 2358 | 2423 | cases.add( |
| ... | ... | @@ -2361,7 +2426,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2361 | 2426 | \\ const a: noreturn = {}; |
| 2362 | 2427 | \\} |
| 2363 | 2428 | , |
| 2364 | ".tmp_source.zig:2:14: error: variable of type 'noreturn' not allowed", | |
| 2429 | "tmp.zig:2:14: error: variable of type 'noreturn' not allowed", | |
| 2365 | 2430 | ); |
| 2366 | 2431 | |
| 2367 | 2432 | cases.add( |
| ... | ... | @@ -2369,7 +2434,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2369 | 2434 | \\fn f(a: noreturn) void {} |
| 2370 | 2435 | \\export fn entry() void { f(); } |
| 2371 | 2436 | , |
| 2372 | ".tmp_source.zig:1:9: error: parameter of type 'noreturn' not allowed", | |
| 2437 | "tmp.zig:1:9: error: parameter of type 'noreturn' not allowed", | |
| 2373 | 2438 | ); |
| 2374 | 2439 | |
| 2375 | 2440 | cases.add( |
| ... | ... | @@ -2378,7 +2443,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2378 | 2443 | \\ 3 = 3; |
| 2379 | 2444 | \\} |
| 2380 | 2445 | , |
| 2381 | ".tmp_source.zig:2:7: error: cannot assign to constant", | |
| 2446 | "tmp.zig:2:7: error: cannot assign to constant", | |
| 2382 | 2447 | ); |
| 2383 | 2448 | |
| 2384 | 2449 | cases.add( |
| ... | ... | @@ -2388,7 +2453,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2388 | 2453 | \\ a = 4; |
| 2389 | 2454 | \\} |
| 2390 | 2455 | , |
| 2391 | ".tmp_source.zig:3:7: error: cannot assign to constant", | |
| 2456 | "tmp.zig:3:7: error: cannot assign to constant", | |
| 2392 | 2457 | ); |
| 2393 | 2458 | |
| 2394 | 2459 | cases.add( |
| ... | ... | @@ -2397,7 +2462,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2397 | 2462 | \\ b = 3; |
| 2398 | 2463 | \\} |
| 2399 | 2464 | , |
| 2400 | ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'", | |
| 2465 | "tmp.zig:2:5: error: use of undeclared identifier 'b'", | |
| 2401 | 2466 | ); |
| 2402 | 2467 | |
| 2403 | 2468 | cases.add( |
| ... | ... | @@ -2406,7 +2471,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2406 | 2471 | \\ (const a = 0); |
| 2407 | 2472 | \\} |
| 2408 | 2473 | , |
| 2409 | ".tmp_source.zig:2:6: error: invalid token: 'const'", | |
| 2474 | "tmp.zig:2:6: error: invalid token: 'const'", | |
| 2410 | 2475 | ); |
| 2411 | 2476 | |
| 2412 | 2477 | cases.add( |
| ... | ... | @@ -2415,7 +2480,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2415 | 2480 | \\ i[i] = i[i]; |
| 2416 | 2481 | \\} |
| 2417 | 2482 | , |
| 2418 | ".tmp_source.zig:2:5: error: use of undeclared identifier 'i'", | |
| 2483 | "tmp.zig:2:5: error: use of undeclared identifier 'i'", | |
| 2419 | 2484 | ); |
| 2420 | 2485 | |
| 2421 | 2486 | cases.add( |
| ... | ... | @@ -2425,8 +2490,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2425 | 2490 | \\ bad[bad] = bad[bad]; |
| 2426 | 2491 | \\} |
| 2427 | 2492 | , |
| 2428 | ".tmp_source.zig:3:8: error: array access of non-array type 'bool'", | |
| 2429 | ".tmp_source.zig:3:19: error: array access of non-array type 'bool'", | |
| 2493 | "tmp.zig:3:8: error: array access of non-array type 'bool'", | |
| 2494 | "tmp.zig:3:19: error: array access of non-array type 'bool'", | |
| 2430 | 2495 | ); |
| 2431 | 2496 | |
| 2432 | 2497 | cases.add( |
| ... | ... | @@ -2437,8 +2502,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2437 | 2502 | \\ array[bad] = array[bad]; |
| 2438 | 2503 | \\} |
| 2439 | 2504 | , |
| 2440 | ".tmp_source.zig:4:11: error: expected type 'usize', found 'bool'", | |
| 2441 | ".tmp_source.zig:4:24: error: expected type 'usize', found 'bool'", | |
| 2505 | "tmp.zig:4:11: error: expected type 'usize', found 'bool'", | |
| 2506 | "tmp.zig:4:24: error: expected type 'usize', found 'bool'", | |
| 2442 | 2507 | ); |
| 2443 | 2508 | |
| 2444 | 2509 | cases.add( |
| ... | ... | @@ -2449,7 +2514,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2449 | 2514 | \\} |
| 2450 | 2515 | \\export fn entry() void { f(); } |
| 2451 | 2516 | , |
| 2452 | ".tmp_source.zig:3:7: error: cannot assign to constant", | |
| 2517 | "tmp.zig:3:7: error: cannot assign to constant", | |
| 2453 | 2518 | ); |
| 2454 | 2519 | |
| 2455 | 2520 | cases.add( |
| ... | ... | @@ -2460,26 +2525,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2460 | 2525 | \\} |
| 2461 | 2526 | \\export fn entry() void { f(true); } |
| 2462 | 2527 | , |
| 2463 | ".tmp_source.zig:2:42: error: integer value 1 cannot be implicitly casted to type 'void'", | |
| 2464 | ".tmp_source.zig:3:15: error: incompatible types: 'i32' and 'void'", | |
| 2465 | ); | |
| 2466 | ||
| 2467 | cases.add( | |
| 2468 | "direct struct loop", | |
| 2469 | \\const A = struct { a : A, }; | |
| 2470 | \\export fn entry() usize { return @sizeOf(A); } | |
| 2471 | , | |
| 2472 | ".tmp_source.zig:1:11: error: struct 'A' contains itself", | |
| 2473 | ); | |
| 2474 | ||
| 2475 | cases.add( | |
| 2476 | "indirect struct loop", | |
| 2477 | \\const A = struct { b : B, }; | |
| 2478 | \\const B = struct { c : C, }; | |
| 2479 | \\const C = struct { a : A, }; | |
| 2480 | \\export fn entry() usize { return @sizeOf(A); } | |
| 2481 | , | |
| 2482 | ".tmp_source.zig:1:11: error: struct 'A' contains itself", | |
| 2528 | "tmp.zig:2:42: error: integer value 1 cannot be implicitly casted to type 'void'", | |
| 2529 | "tmp.zig:3:15: error: incompatible types: 'i32' and 'void'", | |
| 2483 | 2530 | ); |
| 2484 | 2531 | |
| 2485 | 2532 | cases.add( |
| ... | ... | @@ -2491,8 +2538,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2491 | 2538 | \\ const y = a.bar; |
| 2492 | 2539 | \\} |
| 2493 | 2540 | , |
| 2494 | ".tmp_source.zig:4:6: error: no member named 'foo' in struct 'A'", | |
| 2495 | ".tmp_source.zig:5:16: error: no member named 'bar' in struct 'A'", | |
| 2541 | "tmp.zig:4:6: error: no member named 'foo' in struct 'A'", | |
| 2542 | "tmp.zig:5:16: error: no member named 'bar' in struct 'A'", | |
| 2496 | 2543 | ); |
| 2497 | 2544 | |
| 2498 | 2545 | cases.add( |
| ... | ... | @@ -2500,7 +2547,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2500 | 2547 | \\const A = struct { x : i32, }; |
| 2501 | 2548 | \\const A = struct { y : i32, }; |
| 2502 | 2549 | , |
| 2503 | ".tmp_source.zig:2:1: error: redefinition of 'A'", | |
| 2550 | "tmp.zig:2:1: error: redefinition of 'A'", | |
| 2504 | 2551 | ); |
| 2505 | 2552 | |
| 2506 | 2553 | cases.add( |
| ... | ... | @@ -2508,7 +2555,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2508 | 2555 | \\const A = enum {}; |
| 2509 | 2556 | \\const A = enum {}; |
| 2510 | 2557 | , |
| 2511 | ".tmp_source.zig:2:1: error: redefinition of 'A'", | |
| 2558 | "tmp.zig:2:1: error: redefinition of 'A'", | |
| 2512 | 2559 | ); |
| 2513 | 2560 | |
| 2514 | 2561 | cases.add( |
| ... | ... | @@ -2516,8 +2563,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2516 | 2563 | \\var a : i32 = 1; |
| 2517 | 2564 | \\var a : i32 = 2; |
| 2518 | 2565 | , |
| 2519 | ".tmp_source.zig:2:1: error: redefinition of 'a'", | |
| 2520 | ".tmp_source.zig:1:1: note: previous definition is here", | |
| 2566 | "tmp.zig:2:1: error: redefinition of 'a'", | |
| 2567 | "tmp.zig:1:1: note: previous definition is here", | |
| 2521 | 2568 | ); |
| 2522 | 2569 | |
| 2523 | 2570 | cases.add( |
| ... | ... | @@ -2536,7 +2583,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2536 | 2583 | \\ }; |
| 2537 | 2584 | \\} |
| 2538 | 2585 | , |
| 2539 | ".tmp_source.zig:11:9: error: duplicate field", | |
| 2586 | "tmp.zig:11:9: error: duplicate field", | |
| 2540 | 2587 | ); |
| 2541 | 2588 | |
| 2542 | 2589 | cases.add( |
| ... | ... | @@ -2555,7 +2602,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2555 | 2602 | \\ }; |
| 2556 | 2603 | \\} |
| 2557 | 2604 | , |
| 2558 | ".tmp_source.zig:9:17: error: missing field: 'x'", | |
| 2605 | "tmp.zig:9:17: error: missing field: 'x'", | |
| 2559 | 2606 | ); |
| 2560 | 2607 | |
| 2561 | 2608 | cases.add( |
| ... | ... | @@ -2573,7 +2620,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2573 | 2620 | \\ }; |
| 2574 | 2621 | \\} |
| 2575 | 2622 | , |
| 2576 | ".tmp_source.zig:10:9: error: no member named 'foo' in struct 'A'", | |
| 2623 | "tmp.zig:10:9: error: no member named 'foo' in struct 'A'", | |
| 2577 | 2624 | ); |
| 2578 | 2625 | |
| 2579 | 2626 | cases.add( |
| ... | ... | @@ -2582,7 +2629,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2582 | 2629 | \\ break; |
| 2583 | 2630 | \\} |
| 2584 | 2631 | , |
| 2585 | ".tmp_source.zig:2:5: error: break expression outside loop", | |
| 2632 | "tmp.zig:2:5: error: break expression outside loop", | |
| 2586 | 2633 | ); |
| 2587 | 2634 | |
| 2588 | 2635 | cases.add( |
| ... | ... | @@ -2591,7 +2638,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2591 | 2638 | \\ continue; |
| 2592 | 2639 | \\} |
| 2593 | 2640 | , |
| 2594 | ".tmp_source.zig:2:5: error: continue expression outside loop", | |
| 2641 | "tmp.zig:2:5: error: continue expression outside loop", | |
| 2595 | 2642 | ); |
| 2596 | 2643 | |
| 2597 | 2644 | cases.add( |
| ... | ... | @@ -2600,7 +2647,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2600 | 2647 | \\ if (true) |x| { } |
| 2601 | 2648 | \\} |
| 2602 | 2649 | , |
| 2603 | ".tmp_source.zig:2:9: error: expected optional type, found 'bool'", | |
| 2650 | "tmp.zig:2:9: error: expected optional type, found 'bool'", | |
| 2604 | 2651 | ); |
| 2605 | 2652 | |
| 2606 | 2653 | cases.add( |
| ... | ... | @@ -2610,7 +2657,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2610 | 2657 | \\} |
| 2611 | 2658 | \\export fn entry() void { _ = f(); } |
| 2612 | 2659 | , |
| 2613 | ".tmp_source.zig:2:15: error: unreachable code", | |
| 2660 | "tmp.zig:2:15: error: unreachable code", | |
| 2614 | 2661 | ); |
| 2615 | 2662 | |
| 2616 | 2663 | cases.add( |
| ... | ... | @@ -2619,18 +2666,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2619 | 2666 | \\} |
| 2620 | 2667 | \\export fn entry() void { _ = f(); } |
| 2621 | 2668 | , |
| 2622 | ".tmp_source.zig:1:8: error: invalid builtin function: 'bogus'", | |
| 2623 | ); | |
| 2624 | ||
| 2625 | cases.add( | |
| 2626 | "top level decl dependency loop", | |
| 2627 | \\const a : @typeOf(b) = 0; | |
| 2628 | \\const b : @typeOf(a) = 0; | |
| 2629 | \\export fn entry() void { | |
| 2630 | \\ const c = a + b; | |
| 2631 | \\} | |
| 2632 | , | |
| 2633 | ".tmp_source.zig:1:1: error: 'a' depends on itself", | |
| 2669 | "tmp.zig:1:8: error: invalid builtin function: 'bogus'", | |
| 2634 | 2670 | ); |
| 2635 | 2671 | |
| 2636 | 2672 | cases.add( |
| ... | ... | @@ -2638,7 +2674,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2638 | 2674 | \\fn f(noalias x: i32) void {} |
| 2639 | 2675 | \\export fn entry() void { f(1234); } |
| 2640 | 2676 | , |
| 2641 | ".tmp_source.zig:1:6: error: noalias on non-pointer parameter", | |
| 2677 | "tmp.zig:1:6: error: noalias on non-pointer parameter", | |
| 2642 | 2678 | ); |
| 2643 | 2679 | |
| 2644 | 2680 | cases.add( |
| ... | ... | @@ -2646,7 +2682,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2646 | 2682 | \\const foo = []u16{.x = 1024,}; |
| 2647 | 2683 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 2648 | 2684 | , |
| 2649 | ".tmp_source.zig:1:18: error: type '[]u16' does not support struct initialization syntax", | |
| 2685 | "tmp.zig:1:18: error: type '[]u16' does not support struct initialization syntax", | |
| 2650 | 2686 | ); |
| 2651 | 2687 | |
| 2652 | 2688 | cases.add( |
| ... | ... | @@ -2656,7 +2692,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2656 | 2692 | \\ return 1; |
| 2657 | 2693 | \\} |
| 2658 | 2694 | , |
| 2659 | ".tmp_source.zig:1:1: error: variable of type 'type' must be constant", | |
| 2695 | "tmp.zig:1:1: error: variable of type 'type' must be constant", | |
| 2660 | 2696 | ); |
| 2661 | 2697 | |
| 2662 | 2698 | cases.add( |
| ... | ... | @@ -2672,10 +2708,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2672 | 2708 | \\ f(1234); |
| 2673 | 2709 | \\} |
| 2674 | 2710 | , |
| 2675 | ".tmp_source.zig:4:6: error: redefinition of 'Foo'", | |
| 2676 | ".tmp_source.zig:1:1: note: previous definition is here", | |
| 2677 | ".tmp_source.zig:5:5: error: redefinition of 'Bar'", | |
| 2678 | ".tmp_source.zig:2:1: note: previous definition is here", | |
| 2711 | "tmp.zig:4:6: error: redefinition of 'Foo'", | |
| 2712 | "tmp.zig:1:1: note: previous definition is here", | |
| 2713 | "tmp.zig:5:5: error: redefinition of 'Bar'", | |
| 2714 | "tmp.zig:2:1: note: previous definition is here", | |
| 2679 | 2715 | ); |
| 2680 | 2716 | |
| 2681 | 2717 | cases.add( |
| ... | ... | @@ -2696,7 +2732,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2696 | 2732 | \\ |
| 2697 | 2733 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 2698 | 2734 | , |
| 2699 | ".tmp_source.zig:8:5: error: enumeration value 'Number.Four' not handled in switch", | |
| 2735 | "tmp.zig:8:5: error: enumeration value 'Number.Four' not handled in switch", | |
| 2700 | 2736 | ); |
| 2701 | 2737 | |
| 2702 | 2738 | cases.add( |
| ... | ... | @@ -2719,8 +2755,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2719 | 2755 | \\ |
| 2720 | 2756 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 2721 | 2757 | , |
| 2722 | ".tmp_source.zig:13:15: error: duplicate switch value", | |
| 2723 | ".tmp_source.zig:10:15: note: other value is here", | |
| 2758 | "tmp.zig:13:15: error: duplicate switch value", | |
| 2759 | "tmp.zig:10:15: note: other value is here", | |
| 2724 | 2760 | ); |
| 2725 | 2761 | |
| 2726 | 2762 | cases.add( |
| ... | ... | @@ -2744,8 +2780,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2744 | 2780 | \\ |
| 2745 | 2781 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 2746 | 2782 | , |
| 2747 | ".tmp_source.zig:13:15: error: duplicate switch value", | |
| 2748 | ".tmp_source.zig:10:15: note: other value is here", | |
| 2783 | "tmp.zig:13:15: error: duplicate switch value", | |
| 2784 | "tmp.zig:10:15: note: other value is here", | |
| 2749 | 2785 | ); |
| 2750 | 2786 | |
| 2751 | 2787 | cases.add( |
| ... | ... | @@ -2761,7 +2797,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2761 | 2797 | \\ f(1234); |
| 2762 | 2798 | \\} |
| 2763 | 2799 | , |
| 2764 | ".tmp_source.zig:5:9: error: multiple else prongs in switch expression", | |
| 2800 | "tmp.zig:5:9: error: multiple else prongs in switch expression", | |
| 2765 | 2801 | ); |
| 2766 | 2802 | |
| 2767 | 2803 | cases.add( |
| ... | ... | @@ -2773,7 +2809,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2773 | 2809 | \\} |
| 2774 | 2810 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 2775 | 2811 | , |
| 2776 | ".tmp_source.zig:2:5: error: switch must handle all possibilities", | |
| 2812 | "tmp.zig:2:5: error: switch must handle all possibilities", | |
| 2777 | 2813 | ); |
| 2778 | 2814 | |
| 2779 | 2815 | cases.add( |
| ... | ... | @@ -2788,8 +2824,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2788 | 2824 | \\} |
| 2789 | 2825 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 2790 | 2826 | , |
| 2791 | ".tmp_source.zig:6:9: error: duplicate switch value", | |
| 2792 | ".tmp_source.zig:5:14: note: previous value is here", | |
| 2827 | "tmp.zig:6:9: error: duplicate switch value", | |
| 2828 | "tmp.zig:5:14: note: previous value is here", | |
| 2793 | 2829 | ); |
| 2794 | 2830 | |
| 2795 | 2831 | cases.add( |
| ... | ... | @@ -2802,7 +2838,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2802 | 2838 | \\const y: u8 = 100; |
| 2803 | 2839 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 2804 | 2840 | , |
| 2805 | ".tmp_source.zig:2:5: error: else prong required when switching on type '*u8'", | |
| 2841 | "tmp.zig:2:5: error: else prong required when switching on type '*u8'", | |
| 2806 | 2842 | ); |
| 2807 | 2843 | |
| 2808 | 2844 | cases.add( |
| ... | ... | @@ -2811,7 +2847,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2811 | 2847 | \\const x = foo(); |
| 2812 | 2848 | \\export fn entry() i32 { return x; } |
| 2813 | 2849 | , |
| 2814 | ".tmp_source.zig:2:11: error: unable to evaluate constant expression", | |
| 2850 | "tmp.zig:2:11: error: unable to evaluate constant expression", | |
| 2815 | 2851 | ); |
| 2816 | 2852 | |
| 2817 | 2853 | cases.add( |
| ... | ... | @@ -2822,7 +2858,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2822 | 2858 | \\ |
| 2823 | 2859 | \\export fn entry() usize { return @sizeOf(@typeOf(a)); } |
| 2824 | 2860 | , |
| 2825 | ".tmp_source.zig:3:11: error: expected array or C string literal, found 'usize'", | |
| 2861 | "tmp.zig:3:11: error: expected array or C string literal, found 'usize'", | |
| 2826 | 2862 | ); |
| 2827 | 2863 | |
| 2828 | 2864 | cases.add( |
| ... | ... | @@ -2833,7 +2869,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2833 | 2869 | \\var s: [10]u8 = undefined; |
| 2834 | 2870 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 2835 | 2871 | , |
| 2836 | ".tmp_source.zig:2:12: error: unable to evaluate constant expression", | |
| 2872 | "tmp.zig:2:12: error: unable to evaluate constant expression", | |
| 2837 | 2873 | ); |
| 2838 | 2874 | |
| 2839 | 2875 | cases.add( |
| ... | ... | @@ -2841,7 +2877,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2841 | 2877 | \\const c = @cImport(@cInclude("bogus.h")); |
| 2842 | 2878 | \\export fn entry() usize { return @sizeOf(@typeOf(c.bogo)); } |
| 2843 | 2879 | , |
| 2844 | ".tmp_source.zig:1:11: error: C import failed", | |
| 2880 | "tmp.zig:1:11: error: C import failed", | |
| 2845 | 2881 | ".h:1:10: note: 'bogus.h' file not found", |
| 2846 | 2882 | ); |
| 2847 | 2883 | |
| ... | ... | @@ -2852,7 +2888,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2852 | 2888 | \\fn foo() *const i32 { return y; } |
| 2853 | 2889 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 2854 | 2890 | , |
| 2855 | ".tmp_source.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", | |
| 2891 | "tmp.zig:3:30: error: expected type '*const i32', found '*const comptime_int'", | |
| 2856 | 2892 | ); |
| 2857 | 2893 | |
| 2858 | 2894 | cases.add( |
| ... | ... | @@ -2860,7 +2896,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2860 | 2896 | \\const x : u8 = 300; |
| 2861 | 2897 | \\export fn entry() usize { return @sizeOf(@typeOf(x)); } |
| 2862 | 2898 | , |
| 2863 | ".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'", | |
| 2899 | "tmp.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'", | |
| 2864 | 2900 | ); |
| 2865 | 2901 | |
| 2866 | 2902 | cases.add( |
| ... | ... | @@ -2871,8 +2907,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2871 | 2907 | \\} |
| 2872 | 2908 | \\export fn entry() u16 { return f(); } |
| 2873 | 2909 | , |
| 2874 | ".tmp_source.zig:3:14: error: RHS of shift is too large for LHS type", | |
| 2875 | ".tmp_source.zig:3:17: note: value 8 cannot fit into type u3", | |
| 2910 | "tmp.zig:3:14: error: RHS of shift is too large for LHS type", | |
| 2911 | "tmp.zig:3:17: note: value 8 cannot fit into type u3", | |
| 2876 | 2912 | ); |
| 2877 | 2913 | |
| 2878 | 2914 | cases.add( |
| ... | ... | @@ -2880,7 +2916,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2880 | 2916 | \\const x = 2 == 2.0; |
| 2881 | 2917 | \\export fn entry() usize { return @sizeOf(@typeOf(x)); } |
| 2882 | 2918 | , |
| 2883 | ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type 'comptime_float'", | |
| 2919 | "tmp.zig:1:11: error: integer value 2 cannot be implicitly casted to type 'comptime_float'", | |
| 2884 | 2920 | ); |
| 2885 | 2921 | |
| 2886 | 2922 | cases.add( |
| ... | ... | @@ -2909,7 +2945,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2909 | 2945 | \\ |
| 2910 | 2946 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 2911 | 2947 | , |
| 2912 | ".tmp_source.zig:20:34: error: expected 1 arguments, found 0", | |
| 2948 | "tmp.zig:20:34: error: expected 1 arguments, found 0", | |
| 2913 | 2949 | ); |
| 2914 | 2950 | |
| 2915 | 2951 | cases.add( |
| ... | ... | @@ -2918,8 +2954,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2918 | 2954 | \\fn f(i32) void {} |
| 2919 | 2955 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 2920 | 2956 | , |
| 2921 | ".tmp_source.zig:1:1: error: missing function name", | |
| 2922 | ".tmp_source.zig:2:6: error: missing parameter name", | |
| 2957 | "tmp.zig:1:1: error: missing function name", | |
| 2958 | "tmp.zig:2:6: error: missing parameter name", | |
| 2923 | 2959 | ); |
| 2924 | 2960 | |
| 2925 | 2961 | cases.add( |
| ... | ... | @@ -2930,7 +2966,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2930 | 2966 | \\fn c() i32 {return 2;} |
| 2931 | 2967 | \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } |
| 2932 | 2968 | , |
| 2933 | ".tmp_source.zig:1:27: error: expected type 'fn() void', found 'fn() i32'", | |
| 2969 | "tmp.zig:1:27: error: expected type 'fn() void', found 'fn() i32'", | |
| 2934 | 2970 | ); |
| 2935 | 2971 | |
| 2936 | 2972 | cases.add( |
| ... | ... | @@ -2942,7 +2978,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2942 | 2978 | \\ |
| 2943 | 2979 | \\export fn entry() usize { return @sizeOf(@typeOf(fns)); } |
| 2944 | 2980 | , |
| 2945 | ".tmp_source.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", | |
| 2981 | "tmp.zig:1:36: error: expected type 'fn(i32) i32', found 'extern fn(i32) i32'", | |
| 2946 | 2982 | ); |
| 2947 | 2983 | |
| 2948 | 2984 | cases.add( |
| ... | ... | @@ -2951,16 +2987,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2951 | 2987 | \\fn func() bogus {} |
| 2952 | 2988 | \\export fn entry() usize { return @sizeOf(@typeOf(func)); } |
| 2953 | 2989 | , |
| 2954 | ".tmp_source.zig:2:1: error: redefinition of 'func'", | |
| 2955 | ".tmp_source.zig:1:11: error: use of undeclared identifier 'bogus'", | |
| 2956 | ); | |
| 2957 | ||
| 2958 | cases.add( | |
| 2959 | "bogus compile var", | |
| 2960 | \\const x = @import("builtin").bogus; | |
| 2961 | \\export fn entry() usize { return @sizeOf(@typeOf(x)); } | |
| 2962 | , | |
| 2963 | ".tmp_source.zig:1:29: error: no member named 'bogus' in '", | |
| 2990 | "tmp.zig:2:1: error: redefinition of 'func'", | |
| 2991 | "tmp.zig:1:11: error: use of undeclared identifier 'bogus'", | |
| 2964 | 2992 | ); |
| 2965 | 2993 | |
| 2966 | 2994 | cases.add( |
| ... | ... | @@ -2973,9 +3001,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2973 | 3001 | \\ |
| 2974 | 3002 | \\export fn entry() usize { return @sizeOf(@typeOf(Foo)); } |
| 2975 | 3003 | , |
| 2976 | ".tmp_source.zig:5:25: error: unable to evaluate constant expression", | |
| 2977 | ".tmp_source.zig:2:12: note: called from here", | |
| 2978 | ".tmp_source.zig:2:8: note: called from here", | |
| 3004 | "tmp.zig:5:25: error: unable to evaluate constant expression", | |
| 3005 | "tmp.zig:2:12: note: called from here", | |
| 3006 | "tmp.zig:2:8: note: called from here", | |
| 2979 | 3007 | ); |
| 2980 | 3008 | |
| 2981 | 3009 | cases.add( |
| ... | ... | @@ -2987,7 +3015,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 2987 | 3015 | \\ |
| 2988 | 3016 | \\export fn entry() usize { return @sizeOf(@typeOf(x)); } |
| 2989 | 3017 | , |
| 2990 | ".tmp_source.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'", | |
| 3018 | "tmp.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'", | |
| 2991 | 3019 | ); |
| 2992 | 3020 | |
| 2993 | 3021 | cases.add( |
| ... | ... | @@ -3002,10 +3030,10 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3002 | 3030 | \\export fn entry3() usize { return @sizeOf(@typeOf(int_x)); } |
| 3003 | 3031 | \\export fn entry4() usize { return @sizeOf(@typeOf(float_x)); } |
| 3004 | 3032 | , |
| 3005 | ".tmp_source.zig:1:21: error: division by zero", | |
| 3006 | ".tmp_source.zig:2:25: error: division by zero", | |
| 3007 | ".tmp_source.zig:3:22: error: division by zero", | |
| 3008 | ".tmp_source.zig:4:26: error: division by zero", | |
| 3033 | "tmp.zig:1:21: error: division by zero", | |
| 3034 | "tmp.zig:2:25: error: division by zero", | |
| 3035 | "tmp.zig:3:22: error: division by zero", | |
| 3036 | "tmp.zig:4:26: error: division by zero", | |
| 3009 | 3037 | ); |
| 3010 | 3038 | |
| 3011 | 3039 | cases.add( |
| ... | ... | @@ -3015,7 +3043,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3015 | 3043 | \\ |
| 3016 | 3044 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 3017 | 3045 | , |
| 3018 | ".tmp_source.zig:1:15: error: newline not allowed in string literal", | |
| 3046 | "tmp.zig:1:15: error: newline not allowed in string literal", | |
| 3019 | 3047 | ); |
| 3020 | 3048 | |
| 3021 | 3049 | cases.add( |
| ... | ... | @@ -3025,7 +3053,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3025 | 3053 | \\ |
| 3026 | 3054 | \\export fn entry() usize { return @sizeOf(@typeOf(invalid)); } |
| 3027 | 3055 | , |
| 3028 | ".tmp_source.zig:2:21: error: operator not allowed for type 'fn() void'", | |
| 3056 | "tmp.zig:2:21: error: operator not allowed for type 'fn() void'", | |
| 3029 | 3057 | ); |
| 3030 | 3058 | |
| 3031 | 3059 | cases.add( |
| ... | ... | @@ -3037,7 +3065,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3037 | 3065 | \\ |
| 3038 | 3066 | \\export fn entry() usize { return @sizeOf(@typeOf(test1)); } |
| 3039 | 3067 | , |
| 3040 | ".tmp_source.zig:3:16: error: unable to evaluate constant expression", | |
| 3068 | "tmp.zig:3:16: error: unable to evaluate constant expression", | |
| 3041 | 3069 | ); |
| 3042 | 3070 | |
| 3043 | 3071 | cases.add( |
| ... | ... | @@ -3046,7 +3074,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3046 | 3074 | \\ |
| 3047 | 3075 | \\export fn entry() usize { return @sizeOf(@typeOf(a)); } |
| 3048 | 3076 | , |
| 3049 | ".tmp_source.zig:1:16: error: expected type '*u8', found '(null)'", | |
| 3077 | "tmp.zig:1:16: error: expected type '*u8', found '(null)'", | |
| 3050 | 3078 | ); |
| 3051 | 3079 | |
| 3052 | 3080 | cases.add( |
| ... | ... | @@ -3056,7 +3084,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3056 | 3084 | \\ const pointer = &array[0]; |
| 3057 | 3085 | \\} |
| 3058 | 3086 | , |
| 3059 | ".tmp_source.zig:3:27: error: index 0 outside array of size 0", | |
| 3087 | "tmp.zig:3:27: error: index 0 outside array of size 0", | |
| 3060 | 3088 | ); |
| 3061 | 3089 | |
| 3062 | 3090 | cases.add( |
| ... | ... | @@ -3068,8 +3096,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3068 | 3096 | \\ |
| 3069 | 3097 | \\export fn entry() usize { return @sizeOf(@typeOf(y)); } |
| 3070 | 3098 | , |
| 3071 | ".tmp_source.zig:3:14: error: division by zero", | |
| 3072 | ".tmp_source.zig:1:14: note: called from here", | |
| 3099 | "tmp.zig:3:14: error: division by zero", | |
| 3100 | "tmp.zig:1:14: note: called from here", | |
| 3073 | 3101 | ); |
| 3074 | 3102 | |
| 3075 | 3103 | cases.add( |
| ... | ... | @@ -3078,7 +3106,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3078 | 3106 | \\ |
| 3079 | 3107 | \\export fn entry() usize { return @sizeOf(@typeOf(x)); } |
| 3080 | 3108 | , |
| 3081 | ".tmp_source.zig:1:15: error: use of undefined value here causes undefined behavior", | |
| 3109 | "tmp.zig:1:15: error: use of undefined value here causes undefined behavior", | |
| 3082 | 3110 | ); |
| 3083 | 3111 | |
| 3084 | 3112 | cases.add( |
| ... | ... | @@ -3088,7 +3116,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3088 | 3116 | \\ _ = a / a; |
| 3089 | 3117 | \\} |
| 3090 | 3118 | , |
| 3091 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3119 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3092 | 3120 | ); |
| 3093 | 3121 | |
| 3094 | 3122 | cases.add( |
| ... | ... | @@ -3098,7 +3126,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3098 | 3126 | \\ a /= a; |
| 3099 | 3127 | \\} |
| 3100 | 3128 | , |
| 3101 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3129 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3102 | 3130 | ); |
| 3103 | 3131 | |
| 3104 | 3132 | cases.add( |
| ... | ... | @@ -3108,7 +3136,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3108 | 3136 | \\ _ = a % a; |
| 3109 | 3137 | \\} |
| 3110 | 3138 | , |
| 3111 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3139 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3112 | 3140 | ); |
| 3113 | 3141 | |
| 3114 | 3142 | cases.add( |
| ... | ... | @@ -3118,7 +3146,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3118 | 3146 | \\ a %= a; |
| 3119 | 3147 | \\} |
| 3120 | 3148 | , |
| 3121 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3149 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3122 | 3150 | ); |
| 3123 | 3151 | |
| 3124 | 3152 | cases.add( |
| ... | ... | @@ -3128,7 +3156,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3128 | 3156 | \\ _ = a + a; |
| 3129 | 3157 | \\} |
| 3130 | 3158 | , |
| 3131 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3159 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3132 | 3160 | ); |
| 3133 | 3161 | |
| 3134 | 3162 | cases.add( |
| ... | ... | @@ -3138,7 +3166,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3138 | 3166 | \\ a += a; |
| 3139 | 3167 | \\} |
| 3140 | 3168 | , |
| 3141 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3169 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3142 | 3170 | ); |
| 3143 | 3171 | |
| 3144 | 3172 | cases.add( |
| ... | ... | @@ -3148,7 +3176,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3148 | 3176 | \\ _ = a +% a; |
| 3149 | 3177 | \\} |
| 3150 | 3178 | , |
| 3151 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3179 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3152 | 3180 | ); |
| 3153 | 3181 | |
| 3154 | 3182 | cases.add( |
| ... | ... | @@ -3158,7 +3186,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3158 | 3186 | \\ a +%= a; |
| 3159 | 3187 | \\} |
| 3160 | 3188 | , |
| 3161 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3189 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3162 | 3190 | ); |
| 3163 | 3191 | |
| 3164 | 3192 | cases.add( |
| ... | ... | @@ -3168,7 +3196,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3168 | 3196 | \\ _ = a - a; |
| 3169 | 3197 | \\} |
| 3170 | 3198 | , |
| 3171 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3199 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3172 | 3200 | ); |
| 3173 | 3201 | |
| 3174 | 3202 | cases.add( |
| ... | ... | @@ -3178,7 +3206,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3178 | 3206 | \\ a -= a; |
| 3179 | 3207 | \\} |
| 3180 | 3208 | , |
| 3181 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3209 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3182 | 3210 | ); |
| 3183 | 3211 | |
| 3184 | 3212 | cases.add( |
| ... | ... | @@ -3188,7 +3216,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3188 | 3216 | \\ _ = a -% a; |
| 3189 | 3217 | \\} |
| 3190 | 3218 | , |
| 3191 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3219 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3192 | 3220 | ); |
| 3193 | 3221 | |
| 3194 | 3222 | cases.add( |
| ... | ... | @@ -3198,7 +3226,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3198 | 3226 | \\ a -%= a; |
| 3199 | 3227 | \\} |
| 3200 | 3228 | , |
| 3201 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3229 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3202 | 3230 | ); |
| 3203 | 3231 | |
| 3204 | 3232 | cases.add( |
| ... | ... | @@ -3208,7 +3236,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3208 | 3236 | \\ _ = a * a; |
| 3209 | 3237 | \\} |
| 3210 | 3238 | , |
| 3211 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3239 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3212 | 3240 | ); |
| 3213 | 3241 | |
| 3214 | 3242 | cases.add( |
| ... | ... | @@ -3218,7 +3246,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3218 | 3246 | \\ a *= a; |
| 3219 | 3247 | \\} |
| 3220 | 3248 | , |
| 3221 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3249 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3222 | 3250 | ); |
| 3223 | 3251 | |
| 3224 | 3252 | cases.add( |
| ... | ... | @@ -3228,7 +3256,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3228 | 3256 | \\ _ = a *% a; |
| 3229 | 3257 | \\} |
| 3230 | 3258 | , |
| 3231 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3259 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3232 | 3260 | ); |
| 3233 | 3261 | |
| 3234 | 3262 | cases.add( |
| ... | ... | @@ -3238,7 +3266,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3238 | 3266 | \\ a *%= a; |
| 3239 | 3267 | \\} |
| 3240 | 3268 | , |
| 3241 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3269 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3242 | 3270 | ); |
| 3243 | 3271 | |
| 3244 | 3272 | cases.add( |
| ... | ... | @@ -3248,7 +3276,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3248 | 3276 | \\ _ = a << 2; |
| 3249 | 3277 | \\} |
| 3250 | 3278 | , |
| 3251 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3279 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3252 | 3280 | ); |
| 3253 | 3281 | |
| 3254 | 3282 | cases.add( |
| ... | ... | @@ -3258,7 +3286,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3258 | 3286 | \\ a <<= 2; |
| 3259 | 3287 | \\} |
| 3260 | 3288 | , |
| 3261 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3289 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3262 | 3290 | ); |
| 3263 | 3291 | |
| 3264 | 3292 | cases.add( |
| ... | ... | @@ -3268,7 +3296,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3268 | 3296 | \\ _ = a >> 2; |
| 3269 | 3297 | \\} |
| 3270 | 3298 | , |
| 3271 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3299 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3272 | 3300 | ); |
| 3273 | 3301 | |
| 3274 | 3302 | cases.add( |
| ... | ... | @@ -3278,7 +3306,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3278 | 3306 | \\ a >>= 2; |
| 3279 | 3307 | \\} |
| 3280 | 3308 | , |
| 3281 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3309 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3282 | 3310 | ); |
| 3283 | 3311 | |
| 3284 | 3312 | cases.add( |
| ... | ... | @@ -3288,7 +3316,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3288 | 3316 | \\ _ = a & a; |
| 3289 | 3317 | \\} |
| 3290 | 3318 | , |
| 3291 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3319 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3292 | 3320 | ); |
| 3293 | 3321 | |
| 3294 | 3322 | cases.add( |
| ... | ... | @@ -3298,7 +3326,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3298 | 3326 | \\ a &= a; |
| 3299 | 3327 | \\} |
| 3300 | 3328 | , |
| 3301 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3329 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3302 | 3330 | ); |
| 3303 | 3331 | |
| 3304 | 3332 | cases.add( |
| ... | ... | @@ -3308,7 +3336,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3308 | 3336 | \\ _ = a | a; |
| 3309 | 3337 | \\} |
| 3310 | 3338 | , |
| 3311 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3339 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3312 | 3340 | ); |
| 3313 | 3341 | |
| 3314 | 3342 | cases.add( |
| ... | ... | @@ -3318,7 +3346,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3318 | 3346 | \\ a |= a; |
| 3319 | 3347 | \\} |
| 3320 | 3348 | , |
| 3321 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3349 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3322 | 3350 | ); |
| 3323 | 3351 | |
| 3324 | 3352 | cases.add( |
| ... | ... | @@ -3328,7 +3356,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3328 | 3356 | \\ _ = a ^ a; |
| 3329 | 3357 | \\} |
| 3330 | 3358 | , |
| 3331 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3359 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3332 | 3360 | ); |
| 3333 | 3361 | |
| 3334 | 3362 | cases.add( |
| ... | ... | @@ -3338,7 +3366,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3338 | 3366 | \\ a ^= a; |
| 3339 | 3367 | \\} |
| 3340 | 3368 | , |
| 3341 | ".tmp_source.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3369 | "tmp.zig:3:5: error: use of undefined value here causes undefined behavior", | |
| 3342 | 3370 | ); |
| 3343 | 3371 | |
| 3344 | 3372 | cases.add( |
| ... | ... | @@ -3348,7 +3376,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3348 | 3376 | \\ _ = a == a; |
| 3349 | 3377 | \\} |
| 3350 | 3378 | , |
| 3351 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3379 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3352 | 3380 | ); |
| 3353 | 3381 | |
| 3354 | 3382 | cases.add( |
| ... | ... | @@ -3358,7 +3386,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3358 | 3386 | \\ _ = a != a; |
| 3359 | 3387 | \\} |
| 3360 | 3388 | , |
| 3361 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3389 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3362 | 3390 | ); |
| 3363 | 3391 | |
| 3364 | 3392 | cases.add( |
| ... | ... | @@ -3368,7 +3396,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3368 | 3396 | \\ _ = a > a; |
| 3369 | 3397 | \\} |
| 3370 | 3398 | , |
| 3371 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3399 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3372 | 3400 | ); |
| 3373 | 3401 | |
| 3374 | 3402 | cases.add( |
| ... | ... | @@ -3378,7 +3406,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3378 | 3406 | \\ _ = a >= a; |
| 3379 | 3407 | \\} |
| 3380 | 3408 | , |
| 3381 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3409 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3382 | 3410 | ); |
| 3383 | 3411 | |
| 3384 | 3412 | cases.add( |
| ... | ... | @@ -3388,7 +3416,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3388 | 3416 | \\ _ = a < a; |
| 3389 | 3417 | \\} |
| 3390 | 3418 | , |
| 3391 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3419 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3392 | 3420 | ); |
| 3393 | 3421 | |
| 3394 | 3422 | cases.add( |
| ... | ... | @@ -3398,7 +3426,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3398 | 3426 | \\ _ = a <= a; |
| 3399 | 3427 | \\} |
| 3400 | 3428 | , |
| 3401 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3429 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3402 | 3430 | ); |
| 3403 | 3431 | |
| 3404 | 3432 | cases.add( |
| ... | ... | @@ -3408,7 +3436,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3408 | 3436 | \\ _ = a and a; |
| 3409 | 3437 | \\} |
| 3410 | 3438 | , |
| 3411 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3439 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3412 | 3440 | ); |
| 3413 | 3441 | |
| 3414 | 3442 | cases.add( |
| ... | ... | @@ -3418,7 +3446,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3418 | 3446 | \\ _ = a or a; |
| 3419 | 3447 | \\} |
| 3420 | 3448 | , |
| 3421 | ".tmp_source.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3449 | "tmp.zig:3:9: error: use of undefined value here causes undefined behavior", | |
| 3422 | 3450 | ); |
| 3423 | 3451 | |
| 3424 | 3452 | cases.add( |
| ... | ... | @@ -3428,7 +3456,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3428 | 3456 | \\ _ = -a; |
| 3429 | 3457 | \\} |
| 3430 | 3458 | , |
| 3431 | ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior", | |
| 3459 | "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", | |
| 3432 | 3460 | ); |
| 3433 | 3461 | |
| 3434 | 3462 | cases.add( |
| ... | ... | @@ -3438,7 +3466,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3438 | 3466 | \\ _ = -%a; |
| 3439 | 3467 | \\} |
| 3440 | 3468 | , |
| 3441 | ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior", | |
| 3469 | "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", | |
| 3442 | 3470 | ); |
| 3443 | 3471 | |
| 3444 | 3472 | cases.add( |
| ... | ... | @@ -3448,7 +3476,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3448 | 3476 | \\ _ = ~a; |
| 3449 | 3477 | \\} |
| 3450 | 3478 | , |
| 3451 | ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior", | |
| 3479 | "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", | |
| 3452 | 3480 | ); |
| 3453 | 3481 | |
| 3454 | 3482 | cases.add( |
| ... | ... | @@ -3458,7 +3486,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3458 | 3486 | \\ _ = !a; |
| 3459 | 3487 | \\} |
| 3460 | 3488 | , |
| 3461 | ".tmp_source.zig:3:10: error: use of undefined value here causes undefined behavior", | |
| 3489 | "tmp.zig:3:10: error: use of undefined value here causes undefined behavior", | |
| 3462 | 3490 | ); |
| 3463 | 3491 | |
| 3464 | 3492 | cases.add( |
| ... | ... | @@ -3468,7 +3496,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3468 | 3496 | \\ _ = a orelse false; |
| 3469 | 3497 | \\} |
| 3470 | 3498 | , |
| 3471 | ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior", | |
| 3499 | "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", | |
| 3472 | 3500 | ); |
| 3473 | 3501 | |
| 3474 | 3502 | cases.add( |
| ... | ... | @@ -3478,7 +3506,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3478 | 3506 | \\ _ = a catch |err| false; |
| 3479 | 3507 | \\} |
| 3480 | 3508 | , |
| 3481 | ".tmp_source.zig:3:11: error: use of undefined value here causes undefined behavior", | |
| 3509 | "tmp.zig:3:11: error: use of undefined value here causes undefined behavior", | |
| 3482 | 3510 | ); |
| 3483 | 3511 | |
| 3484 | 3512 | cases.add( |
| ... | ... | @@ -3488,7 +3516,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3488 | 3516 | \\ _ = a.*; |
| 3489 | 3517 | \\} |
| 3490 | 3518 | , |
| 3491 | ".tmp_source.zig:3:9: error: attempt to dereference undefined value", | |
| 3519 | "tmp.zig:3:9: error: attempt to dereference undefined value", | |
| 3492 | 3520 | ); |
| 3493 | 3521 | |
| 3494 | 3522 | cases.add( |
| ... | ... | @@ -3500,8 +3528,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3500 | 3528 | \\ |
| 3501 | 3529 | \\export fn entry() usize { return @sizeOf(@typeOf(seventh_fib_number)); } |
| 3502 | 3530 | , |
| 3503 | ".tmp_source.zig:3:21: error: evaluation exceeded 1000 backwards branches", | |
| 3504 | ".tmp_source.zig:3:21: note: called from here", | |
| 3531 | "tmp.zig:3:21: error: evaluation exceeded 1000 backwards branches", | |
| 3532 | "tmp.zig:3:21: note: called from here", | |
| 3505 | 3533 | ); |
| 3506 | 3534 | |
| 3507 | 3535 | cases.add( |
| ... | ... | @@ -3510,7 +3538,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3510 | 3538 | \\ |
| 3511 | 3539 | \\export fn entry() usize { return @sizeOf(@typeOf(resource)); } |
| 3512 | 3540 | , |
| 3513 | ".tmp_source.zig:1:29: error: unable to find '", | |
| 3541 | "tmp.zig:1:29: error: unable to find '", | |
| 3514 | 3542 | "bogus.txt'", |
| 3515 | 3543 | ); |
| 3516 | 3544 | |
| ... | ... | @@ -3524,7 +3552,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3524 | 3552 | \\ |
| 3525 | 3553 | \\export fn entry() usize { return @sizeOf(@typeOf(a)); } |
| 3526 | 3554 | , |
| 3527 | ".tmp_source.zig:4:21: error: unable to evaluate constant expression", | |
| 3555 | "tmp.zig:4:21: error: unable to evaluate constant expression", | |
| 3528 | 3556 | ); |
| 3529 | 3557 | |
| 3530 | 3558 | cases.add( |
| ... | ... | @@ -3541,8 +3569,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3541 | 3569 | \\ |
| 3542 | 3570 | \\export fn entry() usize { return @sizeOf(@typeOf(a)); } |
| 3543 | 3571 | , |
| 3544 | ".tmp_source.zig:6:24: error: unable to evaluate constant expression", | |
| 3545 | ".tmp_source.zig:4:17: note: called from here", | |
| 3572 | "tmp.zig:6:24: error: unable to evaluate constant expression", | |
| 3573 | "tmp.zig:4:17: note: called from here", | |
| 3546 | 3574 | ); |
| 3547 | 3575 | |
| 3548 | 3576 | cases.add( |
| ... | ... | @@ -3554,7 +3582,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3554 | 3582 | \\ bad_fn_call(); |
| 3555 | 3583 | \\} |
| 3556 | 3584 | , |
| 3557 | ".tmp_source.zig:5:5: error: use of undeclared identifier 'bad_fn_call'", | |
| 3585 | "tmp.zig:5:5: error: use of undeclared identifier 'bad_fn_call'", | |
| 3558 | 3586 | ); |
| 3559 | 3587 | |
| 3560 | 3588 | cases.add( |
| ... | ... | @@ -3573,8 +3601,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3573 | 3601 | \\export fn entry1() usize { return @sizeOf(@typeOf(bad_eql_1)); } |
| 3574 | 3602 | \\export fn entry2() usize { return @sizeOf(@typeOf(bad_eql_2)); } |
| 3575 | 3603 | , |
| 3576 | ".tmp_source.zig:2:14: error: operator not allowed for type '[]u8'", | |
| 3577 | ".tmp_source.zig:9:16: error: operator not allowed for type 'EnumWithData'", | |
| 3604 | "tmp.zig:2:14: error: operator not allowed for type '[]u8'", | |
| 3605 | "tmp.zig:9:16: error: operator not allowed for type 'EnumWithData'", | |
| 3578 | 3606 | ); |
| 3579 | 3607 | |
| 3580 | 3608 | cases.add( |
| ... | ... | @@ -3590,7 +3618,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3590 | 3618 | \\ return 2; |
| 3591 | 3619 | \\} |
| 3592 | 3620 | , |
| 3593 | ".tmp_source.zig:2:15: error: values of type 'comptime_int' must be comptime known", | |
| 3621 | "tmp.zig:2:15: error: values of type 'comptime_int' must be comptime known", | |
| 3594 | 3622 | ); |
| 3595 | 3623 | |
| 3596 | 3624 | cases.add( |
| ... | ... | @@ -3601,7 +3629,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3601 | 3629 | \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} |
| 3602 | 3630 | \\} |
| 3603 | 3631 | , |
| 3604 | ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success", | |
| 3632 | "tmp.zig:4:81: error: failure atomic ordering must be no stricter than success", | |
| 3605 | 3633 | ); |
| 3606 | 3634 | |
| 3607 | 3635 | cases.add( |
| ... | ... | @@ -3612,7 +3640,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3612 | 3640 | \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} |
| 3613 | 3641 | \\} |
| 3614 | 3642 | , |
| 3615 | ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter", | |
| 3643 | "tmp.zig:4:58: error: success atomic ordering must be Monotonic or stricter", | |
| 3616 | 3644 | ); |
| 3617 | 3645 | |
| 3618 | 3646 | cases.add( |
| ... | ... | @@ -3624,8 +3652,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3624 | 3652 | \\ |
| 3625 | 3653 | \\export fn entry() usize { return @sizeOf(@typeOf(y)); } |
| 3626 | 3654 | , |
| 3627 | ".tmp_source.zig:3:12: error: negation caused overflow", | |
| 3628 | ".tmp_source.zig:1:14: note: called from here", | |
| 3655 | "tmp.zig:3:12: error: negation caused overflow", | |
| 3656 | "tmp.zig:1:14: note: called from here", | |
| 3629 | 3657 | ); |
| 3630 | 3658 | |
| 3631 | 3659 | cases.add( |
| ... | ... | @@ -3637,8 +3665,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3637 | 3665 | \\ |
| 3638 | 3666 | \\export fn entry() usize { return @sizeOf(@typeOf(y)); } |
| 3639 | 3667 | , |
| 3640 | ".tmp_source.zig:3:14: error: operation caused overflow", | |
| 3641 | ".tmp_source.zig:1:14: note: called from here", | |
| 3668 | "tmp.zig:3:14: error: operation caused overflow", | |
| 3669 | "tmp.zig:1:14: note: called from here", | |
| 3642 | 3670 | ); |
| 3643 | 3671 | |
| 3644 | 3672 | cases.add( |
| ... | ... | @@ -3650,8 +3678,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3650 | 3678 | \\ |
| 3651 | 3679 | \\export fn entry() usize { return @sizeOf(@typeOf(y)); } |
| 3652 | 3680 | , |
| 3653 | ".tmp_source.zig:3:14: error: operation caused overflow", | |
| 3654 | ".tmp_source.zig:1:14: note: called from here", | |
| 3681 | "tmp.zig:3:14: error: operation caused overflow", | |
| 3682 | "tmp.zig:1:14: note: called from here", | |
| 3655 | 3683 | ); |
| 3656 | 3684 | |
| 3657 | 3685 | cases.add( |
| ... | ... | @@ -3663,8 +3691,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3663 | 3691 | \\ |
| 3664 | 3692 | \\export fn entry() usize { return @sizeOf(@typeOf(y)); } |
| 3665 | 3693 | , |
| 3666 | ".tmp_source.zig:3:14: error: operation caused overflow", | |
| 3667 | ".tmp_source.zig:1:14: note: called from here", | |
| 3694 | "tmp.zig:3:14: error: operation caused overflow", | |
| 3695 | "tmp.zig:1:14: note: called from here", | |
| 3668 | 3696 | ); |
| 3669 | 3697 | |
| 3670 | 3698 | cases.add( |
| ... | ... | @@ -3676,7 +3704,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3676 | 3704 | \\ |
| 3677 | 3705 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 3678 | 3706 | , |
| 3679 | ".tmp_source.zig:3:26: error: expected signed integer type, found 'u32'", | |
| 3707 | "tmp.zig:3:26: error: expected signed integer type, found 'u32'", | |
| 3680 | 3708 | ); |
| 3681 | 3709 | |
| 3682 | 3710 | cases.add( |
| ... | ... | @@ -3686,8 +3714,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3686 | 3714 | \\} |
| 3687 | 3715 | \\fn something() anyerror!void { } |
| 3688 | 3716 | , |
| 3689 | ".tmp_source.zig:2:5: error: expected type 'void', found 'anyerror'", | |
| 3690 | ".tmp_source.zig:1:15: note: return type declared here", | |
| 3717 | "tmp.zig:2:5: error: expected type 'void', found 'anyerror'", | |
| 3718 | "tmp.zig:1:15: note: return type declared here", | |
| 3691 | 3719 | ); |
| 3692 | 3720 | |
| 3693 | 3721 | cases.add( |
| ... | ... | @@ -3700,7 +3728,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3700 | 3728 | \\ } |
| 3701 | 3729 | \\} |
| 3702 | 3730 | , |
| 3703 | ".tmp_source.zig:2:13: error: unable to evaluate constant expression", | |
| 3731 | "tmp.zig:2:13: error: unable to evaluate constant expression", | |
| 3704 | 3732 | ); |
| 3705 | 3733 | |
| 3706 | 3734 | cases.add( |
| ... | ... | @@ -3709,7 +3737,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3709 | 3737 | \\ return x + y; |
| 3710 | 3738 | \\} |
| 3711 | 3739 | , |
| 3712 | ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", | |
| 3740 | "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", | |
| 3713 | 3741 | ); |
| 3714 | 3742 | |
| 3715 | 3743 | cases.add( |
| ... | ... | @@ -3720,7 +3748,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3720 | 3748 | \\} |
| 3721 | 3749 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 3722 | 3750 | , |
| 3723 | ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", | |
| 3751 | "tmp.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'", | |
| 3724 | 3752 | ); |
| 3725 | 3753 | |
| 3726 | 3754 | cases.add( |
| ... | ... | @@ -3730,8 +3758,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3730 | 3758 | \\ var foo = @bytesToSlice(u32, array)[0]; |
| 3731 | 3759 | \\} |
| 3732 | 3760 | , |
| 3733 | ".tmp_source.zig:3:15: error: unable to convert [5]u8 to []align(1) const u32: size mismatch", | |
| 3734 | ".tmp_source.zig:3:29: note: u32 has size 4; remaining bytes: 1", | |
| 3761 | "tmp.zig:3:15: error: unable to convert [5]u8 to []align(1) const u32: size mismatch", | |
| 3762 | "tmp.zig:3:29: note: u32 has size 4; remaining bytes: 1", | |
| 3735 | 3763 | ); |
| 3736 | 3764 | |
| 3737 | 3765 | cases.add( |
| ... | ... | @@ -3755,8 +3783,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3755 | 3783 | \\ list.length = 10; |
| 3756 | 3784 | \\} |
| 3757 | 3785 | , |
| 3758 | ".tmp_source.zig:3:7: error: unable to evaluate constant expression", | |
| 3759 | ".tmp_source.zig:16:19: note: called from here", | |
| 3786 | "tmp.zig:3:7: error: unable to evaluate constant expression", | |
| 3787 | "tmp.zig:16:19: note: called from here", | |
| 3760 | 3788 | ); |
| 3761 | 3789 | |
| 3762 | 3790 | cases.add( |
| ... | ... | @@ -3767,7 +3795,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3767 | 3795 | \\} |
| 3768 | 3796 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 3769 | 3797 | , |
| 3770 | ".tmp_source.zig:3:6: error: no member named 'copy' in '[]const u8'", | |
| 3798 | "tmp.zig:3:6: error: no member named 'copy' in '[]const u8'", | |
| 3771 | 3799 | ); |
| 3772 | 3800 | |
| 3773 | 3801 | cases.add( |
| ... | ... | @@ -3781,7 +3809,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3781 | 3809 | \\} |
| 3782 | 3810 | \\export fn entry() usize { return @sizeOf(@typeOf(f)); } |
| 3783 | 3811 | , |
| 3784 | ".tmp_source.zig:6:15: error: expected 2 arguments, found 3", | |
| 3812 | "tmp.zig:6:15: error: expected 2 arguments, found 3", | |
| 3785 | 3813 | ); |
| 3786 | 3814 | |
| 3787 | 3815 | cases.add( |
| ... | ... | @@ -3791,7 +3819,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3791 | 3819 | \\ cstr[0] = 'W'; |
| 3792 | 3820 | \\} |
| 3793 | 3821 | , |
| 3794 | ".tmp_source.zig:3:11: error: cannot assign to constant", | |
| 3822 | "tmp.zig:3:11: error: cannot assign to constant", | |
| 3795 | 3823 | ); |
| 3796 | 3824 | |
| 3797 | 3825 | cases.add( |
| ... | ... | @@ -3801,14 +3829,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3801 | 3829 | \\ cstr[0] = 'W'; |
| 3802 | 3830 | \\} |
| 3803 | 3831 | , |
| 3804 | ".tmp_source.zig:3:11: error: cannot assign to constant", | |
| 3832 | "tmp.zig:3:11: error: cannot assign to constant", | |
| 3805 | 3833 | ); |
| 3806 | 3834 | |
| 3807 | 3835 | cases.add( |
| 3808 | 3836 | "main function with bogus args type", |
| 3809 | 3837 | \\pub fn main(args: [][]bogus) !void {} |
| 3810 | 3838 | , |
| 3811 | ".tmp_source.zig:1:23: error: use of undeclared identifier 'bogus'", | |
| 3839 | "tmp.zig:1:23: error: use of undeclared identifier 'bogus'", | |
| 3812 | 3840 | ); |
| 3813 | 3841 | |
| 3814 | 3842 | cases.add( |
| ... | ... | @@ -3844,7 +3872,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3844 | 3872 | \\ |
| 3845 | 3873 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 3846 | 3874 | , |
| 3847 | ".tmp_source.zig:5:16: error: use of undeclared identifier 'JsonList'", | |
| 3875 | "tmp.zig:5:16: error: use of undeclared identifier 'JsonList'", | |
| 3848 | 3876 | ); |
| 3849 | 3877 | |
| 3850 | 3878 | cases.add( |
| ... | ... | @@ -3865,7 +3893,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3865 | 3893 | \\ derp.init(); |
| 3866 | 3894 | \\} |
| 3867 | 3895 | , |
| 3868 | ".tmp_source.zig:14:5: error: expected type 'i32', found 'Foo'", | |
| 3896 | "tmp.zig:14:5: error: expected type 'i32', found 'Foo'", | |
| 3869 | 3897 | ); |
| 3870 | 3898 | |
| 3871 | 3899 | cases.add( |
| ... | ... | @@ -3895,7 +3923,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3895 | 3923 | \\ x.init(); |
| 3896 | 3924 | \\} |
| 3897 | 3925 | , |
| 3898 | ".tmp_source.zig:23:5: error: expected type '*Allocator', found '*List'", | |
| 3926 | "tmp.zig:23:5: error: expected type '*Allocator', found '*List'", | |
| 3899 | 3927 | ); |
| 3900 | 3928 | |
| 3901 | 3929 | cases.add( |
| ... | ... | @@ -3906,7 +3934,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3906 | 3934 | \\ |
| 3907 | 3935 | \\export fn entry() usize { return @sizeOf(@typeOf(block_aligned_stuff)); } |
| 3908 | 3936 | , |
| 3909 | ".tmp_source.zig:3:60: error: unable to perform binary not operation on type 'comptime_int'", | |
| 3937 | "tmp.zig:3:60: error: unable to perform binary not operation on type 'comptime_int'", | |
| 3910 | 3938 | ); |
| 3911 | 3939 | |
| 3912 | 3940 | cases.addCase(x: { |
| ... | ... | @@ -3918,7 +3946,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3918 | 3946 | \\ foo.privateFunction(); |
| 3919 | 3947 | \\} |
| 3920 | 3948 | , |
| 3921 | ".tmp_source.zig:4:8: error: 'privateFunction' is private", | |
| 3949 | "tmp.zig:4:8: error: 'privateFunction' is private", | |
| 3922 | 3950 | "foo.zig:1:1: note: declared here", |
| 3923 | 3951 | ); |
| 3924 | 3952 | |
| ... | ... | @@ -3936,7 +3964,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3936 | 3964 | \\ |
| 3937 | 3965 | \\export fn entry() usize { return @sizeOf(@typeOf(a)); } |
| 3938 | 3966 | , |
| 3939 | ".tmp_source.zig:2:11: error: expected type 'type', found 'i32'", | |
| 3967 | "tmp.zig:2:11: error: expected type 'type', found 'i32'", | |
| 3940 | 3968 | ); |
| 3941 | 3969 | |
| 3942 | 3970 | cases.add( |
| ... | ... | @@ -3949,7 +3977,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3949 | 3977 | \\ f.field = 0; |
| 3950 | 3978 | \\} |
| 3951 | 3979 | , |
| 3952 | ".tmp_source.zig:6:13: error: cannot assign to constant", | |
| 3980 | "tmp.zig:6:13: error: cannot assign to constant", | |
| 3953 | 3981 | ); |
| 3954 | 3982 | |
| 3955 | 3983 | cases.add( |
| ... | ... | @@ -3970,7 +3998,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3970 | 3998 | \\ |
| 3971 | 3999 | \\export fn entry() usize { return @sizeOf(@typeOf(testTrickyDefer)); } |
| 3972 | 4000 | , |
| 3973 | ".tmp_source.zig:4:11: error: cannot return from defer expression", | |
| 4001 | "tmp.zig:4:11: error: cannot return from defer expression", | |
| 3974 | 4002 | ); |
| 3975 | 4003 | |
| 3976 | 4004 | cases.add( |
| ... | ... | @@ -3985,8 +4013,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 3985 | 4013 | \\ |
| 3986 | 4014 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 3987 | 4015 | , |
| 3988 | ".tmp_source.zig:2:26: error: index 1 outside argument list of size 1", | |
| 3989 | ".tmp_source.zig:6:15: note: called from here", | |
| 4016 | "tmp.zig:2:26: error: index 1 outside argument list of size 1", | |
| 4017 | "tmp.zig:6:15: note: called from here", | |
| 3990 | 4018 | ); |
| 3991 | 4019 | |
| 3992 | 4020 | cases.add( |
| ... | ... | @@ -4005,7 +4033,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4005 | 4033 | \\ |
| 4006 | 4034 | \\export fn entry() usize { return @sizeOf(@typeOf(bar)); } |
| 4007 | 4035 | , |
| 4008 | ".tmp_source.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted", | |
| 4036 | "tmp.zig:10:16: error: compiler bug: integer and float literals in var args function must be casted", | |
| 4009 | 4037 | ); |
| 4010 | 4038 | |
| 4011 | 4039 | cases.add( |
| ... | ... | @@ -4014,7 +4042,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4014 | 4042 | \\ var vga_mem: u16 = 0xB8000; |
| 4015 | 4043 | \\} |
| 4016 | 4044 | , |
| 4017 | ".tmp_source.zig:2:24: error: integer value 753664 cannot be implicitly casted to type 'u16'", | |
| 4045 | "tmp.zig:2:24: error: integer value 753664 cannot be implicitly casted to type 'u16'", | |
| 4018 | 4046 | ); |
| 4019 | 4047 | |
| 4020 | 4048 | cases.add( |
| ... | ... | @@ -4022,7 +4050,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4022 | 4050 | \\const some_data: [100]u8 align(3) = undefined; |
| 4023 | 4051 | \\export fn entry() usize { return @sizeOf(@typeOf(some_data)); } |
| 4024 | 4052 | , |
| 4025 | ".tmp_source.zig:1:32: error: alignment value 3 is not a power of 2", | |
| 4053 | "tmp.zig:1:32: error: alignment value 3 is not a power of 2", | |
| 4026 | 4054 | ); |
| 4027 | 4055 | |
| 4028 | 4056 | cases.add( |
| ... | ... | @@ -4030,7 +4058,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4030 | 4058 | \\extern fn foo() align(3) void; |
| 4031 | 4059 | \\export fn entry() void { return foo(); } |
| 4032 | 4060 | , |
| 4033 | ".tmp_source.zig:1:23: error: alignment value 3 is not a power of 2", | |
| 4061 | "tmp.zig:1:23: error: alignment value 3 is not a power of 2", | |
| 4034 | 4062 | ); |
| 4035 | 4063 | |
| 4036 | 4064 | cases.add( |
| ... | ... | @@ -4044,9 +4072,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4044 | 4072 | \\ @compileLog("end",); |
| 4045 | 4073 | \\} |
| 4046 | 4074 | , |
| 4047 | ".tmp_source.zig:5:5: error: found compile log statement", | |
| 4048 | ".tmp_source.zig:6:5: error: found compile log statement", | |
| 4049 | ".tmp_source.zig:7:5: error: found compile log statement", | |
| 4075 | "tmp.zig:5:5: error: found compile log statement", | |
| 4076 | "tmp.zig:6:5: error: found compile log statement", | |
| 4077 | "tmp.zig:7:5: error: found compile log statement", | |
| 4050 | 4078 | ); |
| 4051 | 4079 | |
| 4052 | 4080 | cases.add( |
| ... | ... | @@ -4067,7 +4095,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4067 | 4095 | \\ |
| 4068 | 4096 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 4069 | 4097 | , |
| 4070 | ".tmp_source.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", | |
| 4098 | "tmp.zig:8:26: error: expected type '*const u3', found '*align(:3:1) const u3'", | |
| 4071 | 4099 | ); |
| 4072 | 4100 | |
| 4073 | 4101 | cases.add( |
| ... | ... | @@ -4084,8 +4112,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4084 | 4112 | \\ if (!ok) unreachable; |
| 4085 | 4113 | \\} |
| 4086 | 4114 | , |
| 4087 | ".tmp_source.zig:10:14: error: unable to evaluate constant expression", | |
| 4088 | ".tmp_source.zig:6:20: note: called from here", | |
| 4115 | "tmp.zig:10:14: error: unable to evaluate constant expression", | |
| 4116 | "tmp.zig:6:20: note: called from here", | |
| 4089 | 4117 | ); |
| 4090 | 4118 | |
| 4091 | 4119 | cases.add( |
| ... | ... | @@ -4099,8 +4127,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4099 | 4127 | \\ |
| 4100 | 4128 | \\fn bar() void { } |
| 4101 | 4129 | , |
| 4102 | ".tmp_source.zig:3:5: error: control flow attempts to use compile-time variable at runtime", | |
| 4103 | ".tmp_source.zig:3:24: note: compile-time variable assigned here", | |
| 4130 | "tmp.zig:3:5: error: control flow attempts to use compile-time variable at runtime", | |
| 4131 | "tmp.zig:3:24: note: compile-time variable assigned here", | |
| 4104 | 4132 | ); |
| 4105 | 4133 | |
| 4106 | 4134 | cases.add( |
| ... | ... | @@ -4110,7 +4138,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4110 | 4138 | \\} |
| 4111 | 4139 | \\fn bar() i32 { return 0; } |
| 4112 | 4140 | , |
| 4113 | ".tmp_source.zig:2:8: error: expression value is ignored", | |
| 4141 | "tmp.zig:2:8: error: expression value is ignored", | |
| 4114 | 4142 | ); |
| 4115 | 4143 | |
| 4116 | 4144 | cases.add( |
| ... | ... | @@ -4120,7 +4148,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4120 | 4148 | \\} |
| 4121 | 4149 | \\fn bar() anyerror!i32 { return 0; } |
| 4122 | 4150 | , |
| 4123 | ".tmp_source.zig:2:11: error: expression value is ignored", | |
| 4151 | "tmp.zig:2:11: error: expression value is ignored", | |
| 4124 | 4152 | ); |
| 4125 | 4153 | |
| 4126 | 4154 | cases.add( |
| ... | ... | @@ -4129,7 +4157,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4129 | 4157 | \\ 1; |
| 4130 | 4158 | \\} |
| 4131 | 4159 | , |
| 4132 | ".tmp_source.zig:2:5: error: expression value is ignored", | |
| 4160 | "tmp.zig:2:5: error: expression value is ignored", | |
| 4133 | 4161 | ); |
| 4134 | 4162 | |
| 4135 | 4163 | cases.add( |
| ... | ... | @@ -4138,7 +4166,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4138 | 4166 | \\ comptime {1;} |
| 4139 | 4167 | \\} |
| 4140 | 4168 | , |
| 4141 | ".tmp_source.zig:2:15: error: expression value is ignored", | |
| 4169 | "tmp.zig:2:15: error: expression value is ignored", | |
| 4142 | 4170 | ); |
| 4143 | 4171 | |
| 4144 | 4172 | cases.add( |
| ... | ... | @@ -4147,7 +4175,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4147 | 4175 | \\ comptime 1; |
| 4148 | 4176 | \\} |
| 4149 | 4177 | , |
| 4150 | ".tmp_source.zig:2:5: error: expression value is ignored", | |
| 4178 | "tmp.zig:2:5: error: expression value is ignored", | |
| 4151 | 4179 | ); |
| 4152 | 4180 | |
| 4153 | 4181 | cases.add( |
| ... | ... | @@ -4156,7 +4184,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4156 | 4184 | \\ defer {1;} |
| 4157 | 4185 | \\} |
| 4158 | 4186 | , |
| 4159 | ".tmp_source.zig:2:12: error: expression value is ignored", | |
| 4187 | "tmp.zig:2:12: error: expression value is ignored", | |
| 4160 | 4188 | ); |
| 4161 | 4189 | |
| 4162 | 4190 | cases.add( |
| ... | ... | @@ -4166,7 +4194,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4166 | 4194 | \\} |
| 4167 | 4195 | \\fn bar() anyerror!i32 { return 0; } |
| 4168 | 4196 | , |
| 4169 | ".tmp_source.zig:2:14: error: expression value is ignored", | |
| 4197 | "tmp.zig:2:14: error: expression value is ignored", | |
| 4170 | 4198 | ); |
| 4171 | 4199 | |
| 4172 | 4200 | cases.add( |
| ... | ... | @@ -4180,7 +4208,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4180 | 4208 | \\ |
| 4181 | 4209 | \\export fn entry() usize { return @sizeOf(@typeOf(pass)); } |
| 4182 | 4210 | , |
| 4183 | ".tmp_source.zig:4:10: error: attempt to dereference non-pointer type '[10]u8'", | |
| 4211 | "tmp.zig:4:10: error: attempt to dereference non-pointer type '[10]u8'", | |
| 4184 | 4212 | ); |
| 4185 | 4213 | |
| 4186 | 4214 | cases.add( |
| ... | ... | @@ -4196,7 +4224,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4196 | 4224 | \\ |
| 4197 | 4225 | \\export fn entry() usize { return @sizeOf(@typeOf(foo)); } |
| 4198 | 4226 | , |
| 4199 | ".tmp_source.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", | |
| 4227 | "tmp.zig:4:19: error: expected type '*[]const u8', found '*const []const u8'", | |
| 4200 | 4228 | ); |
| 4201 | 4229 | |
| 4202 | 4230 | cases.addCase(x: { |
| ... | ... | @@ -4209,7 +4237,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4209 | 4237 | \\} |
| 4210 | 4238 | , |
| 4211 | 4239 | "foo.zig:1:1: error: exported symbol collision: 'bar'", |
| 4212 | ".tmp_source.zig:3:1: note: other symbol here", | |
| 4240 | "tmp.zig:3:1: note: other symbol here", | |
| 4213 | 4241 | ); |
| 4214 | 4242 | |
| 4215 | 4243 | tc.addSourceFile("foo.zig", |
| ... | ... | @@ -4228,7 +4256,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4228 | 4256 | \\ foo(global_array); |
| 4229 | 4257 | \\} |
| 4230 | 4258 | , |
| 4231 | ".tmp_source.zig:4:9: error: expected type '[]i32', found '[10]i32'", | |
| 4259 | "tmp.zig:4:9: error: expected type '[]i32', found '[10]i32'", | |
| 4232 | 4260 | ); |
| 4233 | 4261 | |
| 4234 | 4262 | cases.add( |
| ... | ... | @@ -4237,7 +4265,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4237 | 4265 | \\ return @ptrCast(usize, a); |
| 4238 | 4266 | \\} |
| 4239 | 4267 | , |
| 4240 | ".tmp_source.zig:2:21: error: expected pointer, found 'usize'", | |
| 4268 | "tmp.zig:2:21: error: expected pointer, found 'usize'", | |
| 4241 | 4269 | ); |
| 4242 | 4270 | |
| 4243 | 4271 | cases.add( |
| ... | ... | @@ -4254,7 +4282,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4254 | 4282 | \\ ); |
| 4255 | 4283 | \\} |
| 4256 | 4284 | , |
| 4257 | ".tmp_source.zig:6:5: error: unable to evaluate constant expression", | |
| 4285 | "tmp.zig:6:5: error: unable to evaluate constant expression", | |
| 4258 | 4286 | ); |
| 4259 | 4287 | |
| 4260 | 4288 | cases.add( |
| ... | ... | @@ -4264,7 +4292,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4264 | 4292 | \\ const foo = builtin.Arch.x86; |
| 4265 | 4293 | \\} |
| 4266 | 4294 | , |
| 4267 | ".tmp_source.zig:3:29: error: container 'Arch' has no member called 'x86'", | |
| 4295 | "tmp.zig:3:29: error: container 'builtin.Arch' has no member called 'x86'", | |
| 4268 | 4296 | ); |
| 4269 | 4297 | |
| 4270 | 4298 | cases.add( |
| ... | ... | @@ -4274,7 +4302,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4274 | 4302 | \\ var y: *void = @intToPtr(*void, x); |
| 4275 | 4303 | \\} |
| 4276 | 4304 | , |
| 4277 | ".tmp_source.zig:3:30: error: type '*void' has 0 bits and cannot store information", | |
| 4305 | "tmp.zig:3:30: error: type '*void' has 0 bits and cannot store information", | |
| 4278 | 4306 | ); |
| 4279 | 4307 | |
| 4280 | 4308 | cases.add( |
| ... | ... | @@ -4284,7 +4312,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4284 | 4312 | \\ return @fieldParentPtr(Foo, "a", a); |
| 4285 | 4313 | \\} |
| 4286 | 4314 | , |
| 4287 | ".tmp_source.zig:3:28: error: expected struct type, found 'i32'", | |
| 4315 | "tmp.zig:3:28: error: expected struct type, found 'i32'", | |
| 4288 | 4316 | ); |
| 4289 | 4317 | |
| 4290 | 4318 | cases.add( |
| ... | ... | @@ -4296,7 +4324,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4296 | 4324 | \\ return @fieldParentPtr(Foo, "a", a); |
| 4297 | 4325 | \\} |
| 4298 | 4326 | , |
| 4299 | ".tmp_source.zig:5:33: error: struct 'Foo' has no field 'a'", | |
| 4327 | "tmp.zig:5:33: error: struct 'Foo' has no field 'a'", | |
| 4300 | 4328 | ); |
| 4301 | 4329 | |
| 4302 | 4330 | cases.add( |
| ... | ... | @@ -4308,7 +4336,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4308 | 4336 | \\ return @fieldParentPtr(Foo, "a", a); |
| 4309 | 4337 | \\} |
| 4310 | 4338 | , |
| 4311 | ".tmp_source.zig:5:38: error: expected pointer, found 'i32'", | |
| 4339 | "tmp.zig:5:38: error: expected pointer, found 'i32'", | |
| 4312 | 4340 | ); |
| 4313 | 4341 | |
| 4314 | 4342 | cases.add( |
| ... | ... | @@ -4324,7 +4352,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4324 | 4352 | \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", field_ptr); |
| 4325 | 4353 | \\} |
| 4326 | 4354 | , |
| 4327 | ".tmp_source.zig:9:55: error: pointer value not based on parent struct", | |
| 4355 | "tmp.zig:9:55: error: pointer value not based on parent struct", | |
| 4328 | 4356 | ); |
| 4329 | 4357 | |
| 4330 | 4358 | cases.add( |
| ... | ... | @@ -4339,7 +4367,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4339 | 4367 | \\ const another_foo_ptr = @fieldParentPtr(Foo, "b", &foo.a); |
| 4340 | 4368 | \\} |
| 4341 | 4369 | , |
| 4342 | ".tmp_source.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo'", | |
| 4370 | "tmp.zig:8:29: error: field 'b' has index 1 but pointer value is index 0 of struct 'Foo'", | |
| 4343 | 4371 | ); |
| 4344 | 4372 | |
| 4345 | 4373 | cases.add( |
| ... | ... | @@ -4349,7 +4377,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4349 | 4377 | \\ return @byteOffsetOf(Foo, "a",); |
| 4350 | 4378 | \\} |
| 4351 | 4379 | , |
| 4352 | ".tmp_source.zig:3:26: error: expected struct type, found 'i32'", | |
| 4380 | "tmp.zig:3:26: error: expected struct type, found 'i32'", | |
| 4353 | 4381 | ); |
| 4354 | 4382 | |
| 4355 | 4383 | cases.add( |
| ... | ... | @@ -4361,14 +4389,14 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4361 | 4389 | \\ return @byteOffsetOf(Foo, "a",); |
| 4362 | 4390 | \\} |
| 4363 | 4391 | , |
| 4364 | ".tmp_source.zig:5:31: error: struct 'Foo' has no field 'a'", | |
| 4392 | "tmp.zig:5:31: error: struct 'Foo' has no field 'a'", | |
| 4365 | 4393 | ); |
| 4366 | 4394 | |
| 4367 | 4395 | cases.addExe( |
| 4368 | 4396 | "missing main fn in executable", |
| 4369 | 4397 | \\ |
| 4370 | 4398 | , |
| 4371 | "error: no member named 'main' in '", | |
| 4399 | "error: root source file has no member called 'main'", | |
| 4372 | 4400 | ); |
| 4373 | 4401 | |
| 4374 | 4402 | cases.addExe( |
| ... | ... | @@ -4376,7 +4404,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4376 | 4404 | \\fn main() void {} |
| 4377 | 4405 | , |
| 4378 | 4406 | "error: 'main' is private", |
| 4379 | ".tmp_source.zig:1:1: note: declared here", | |
| 4407 | "tmp.zig:1:1: note: declared here", | |
| 4380 | 4408 | ); |
| 4381 | 4409 | |
| 4382 | 4410 | cases.add( |
| ... | ... | @@ -4386,7 +4414,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4386 | 4414 | \\ return foo; |
| 4387 | 4415 | \\} |
| 4388 | 4416 | , |
| 4389 | ".tmp_source.zig:1:33: error: cannot set section of external variable 'foo'", | |
| 4417 | "tmp.zig:1:33: error: cannot set section of external variable 'foo'", | |
| 4390 | 4418 | ); |
| 4391 | 4419 | |
| 4392 | 4420 | cases.add( |
| ... | ... | @@ -4396,7 +4424,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4396 | 4424 | \\ return foo; |
| 4397 | 4425 | \\} |
| 4398 | 4426 | , |
| 4399 | ".tmp_source.zig:2:30: error: cannot set section of local variable 'foo'", | |
| 4427 | "tmp.zig:2:30: error: cannot set section of local variable 'foo'", | |
| 4400 | 4428 | ); |
| 4401 | 4429 | |
| 4402 | 4430 | cases.add( |
| ... | ... | @@ -4406,7 +4434,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4406 | 4434 | \\ foo(); |
| 4407 | 4435 | \\} |
| 4408 | 4436 | , |
| 4409 | ".tmp_source.zig:1:29: error: cannot set section of external function 'foo'", | |
| 4437 | "tmp.zig:1:29: error: cannot set section of external function 'foo'", | |
| 4410 | 4438 | ); |
| 4411 | 4439 | |
| 4412 | 4440 | cases.add( |
| ... | ... | @@ -4416,7 +4444,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4416 | 4444 | \\ return &a; |
| 4417 | 4445 | \\} |
| 4418 | 4446 | , |
| 4419 | ".tmp_source.zig:3:13: error: function returns address of local variable", | |
| 4447 | "tmp.zig:3:13: error: function returns address of local variable", | |
| 4420 | 4448 | ); |
| 4421 | 4449 | |
| 4422 | 4450 | cases.add( |
| ... | ... | @@ -4427,7 +4455,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4427 | 4455 | \\ return if (c) &a else &b; |
| 4428 | 4456 | \\} |
| 4429 | 4457 | , |
| 4430 | ".tmp_source.zig:4:12: error: function returns address of local variable", | |
| 4458 | "tmp.zig:4:12: error: function returns address of local variable", | |
| 4431 | 4459 | ); |
| 4432 | 4460 | |
| 4433 | 4461 | cases.add( |
| ... | ... | @@ -4452,8 +4480,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4452 | 4480 | \\ if (!ok) unreachable; |
| 4453 | 4481 | \\} |
| 4454 | 4482 | , |
| 4455 | ".tmp_source.zig:9:17: error: redefinition of 'Self'", | |
| 4456 | ".tmp_source.zig:5:9: note: previous definition is here", | |
| 4483 | "tmp.zig:9:17: error: redefinition of 'Self'", | |
| 4484 | "tmp.zig:5:9: note: previous definition is here", | |
| 4457 | 4485 | ); |
| 4458 | 4486 | |
| 4459 | 4487 | cases.add( |
| ... | ... | @@ -4463,7 +4491,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4463 | 4491 | \\} |
| 4464 | 4492 | \\fn bar() ?i32 { return 1; } |
| 4465 | 4493 | , |
| 4466 | ".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'", | |
| 4494 | "tmp.zig:2:15: error: expected type 'bool', found '?i32'", | |
| 4467 | 4495 | ); |
| 4468 | 4496 | |
| 4469 | 4497 | cases.add( |
| ... | ... | @@ -4473,7 +4501,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4473 | 4501 | \\} |
| 4474 | 4502 | \\fn bar() anyerror!i32 { return 1; } |
| 4475 | 4503 | , |
| 4476 | ".tmp_source.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", | |
| 4504 | "tmp.zig:2:15: error: expected type 'bool', found 'anyerror!i32'", | |
| 4477 | 4505 | ); |
| 4478 | 4506 | |
| 4479 | 4507 | cases.add( |
| ... | ... | @@ -4483,7 +4511,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4483 | 4511 | \\} |
| 4484 | 4512 | \\fn bar() bool { return true; } |
| 4485 | 4513 | , |
| 4486 | ".tmp_source.zig:2:15: error: expected optional type, found 'bool'", | |
| 4514 | "tmp.zig:2:15: error: expected optional type, found 'bool'", | |
| 4487 | 4515 | ); |
| 4488 | 4516 | |
| 4489 | 4517 | cases.add( |
| ... | ... | @@ -4493,7 +4521,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4493 | 4521 | \\} |
| 4494 | 4522 | \\fn bar() anyerror!i32 { return 1; } |
| 4495 | 4523 | , |
| 4496 | ".tmp_source.zig:2:15: error: expected optional type, found 'anyerror!i32'", | |
| 4524 | "tmp.zig:2:15: error: expected optional type, found 'anyerror!i32'", | |
| 4497 | 4525 | ); |
| 4498 | 4526 | |
| 4499 | 4527 | cases.add( |
| ... | ... | @@ -4503,7 +4531,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4503 | 4531 | \\} |
| 4504 | 4532 | \\fn bar() bool { return true; } |
| 4505 | 4533 | , |
| 4506 | ".tmp_source.zig:2:15: error: expected error union type, found 'bool'", | |
| 4534 | "tmp.zig:2:15: error: expected error union type, found 'bool'", | |
| 4507 | 4535 | ); |
| 4508 | 4536 | |
| 4509 | 4537 | cases.add( |
| ... | ... | @@ -4513,7 +4541,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4513 | 4541 | \\} |
| 4514 | 4542 | \\fn bar() ?i32 { return 1; } |
| 4515 | 4543 | , |
| 4516 | ".tmp_source.zig:2:15: error: expected error union type, found '?i32'", | |
| 4544 | "tmp.zig:2:15: error: expected error union type, found '?i32'", | |
| 4517 | 4545 | ); |
| 4518 | 4546 | |
| 4519 | 4547 | cases.add( |
| ... | ... | @@ -4531,7 +4559,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4531 | 4559 | \\} |
| 4532 | 4560 | \\extern fn quux() void; |
| 4533 | 4561 | , |
| 4534 | ".tmp_source.zig:4:1: error: unable to inline function", | |
| 4562 | "tmp.zig:4:1: error: unable to inline function", | |
| 4535 | 4563 | ); |
| 4536 | 4564 | |
| 4537 | 4565 | cases.add( |
| ... | ... | @@ -4542,7 +4570,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4542 | 4570 | \\inline fn bar() void { } |
| 4543 | 4571 | \\extern fn quux(usize) void; |
| 4544 | 4572 | , |
| 4545 | ".tmp_source.zig:4:1: error: unable to inline function", | |
| 4573 | "tmp.zig:4:1: error: unable to inline function", | |
| 4546 | 4574 | ); |
| 4547 | 4575 | |
| 4548 | 4576 | cases.add( |
| ... | ... | @@ -4551,7 +4579,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4551 | 4579 | \\ return a / b; |
| 4552 | 4580 | \\} |
| 4553 | 4581 | , |
| 4554 | ".tmp_source.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 4582 | "tmp.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact", | |
| 4555 | 4583 | ); |
| 4556 | 4584 | |
| 4557 | 4585 | cases.add( |
| ... | ... | @@ -4560,7 +4588,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4560 | 4588 | \\ return a % b; |
| 4561 | 4589 | \\} |
| 4562 | 4590 | , |
| 4563 | ".tmp_source.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod", | |
| 4591 | "tmp.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod", | |
| 4564 | 4592 | ); |
| 4565 | 4593 | |
| 4566 | 4594 | cases.add( |
| ... | ... | @@ -4571,7 +4599,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4571 | 4599 | \\ const c = a / b; |
| 4572 | 4600 | \\} |
| 4573 | 4601 | , |
| 4574 | ".tmp_source.zig:4:17: error: division by zero", | |
| 4602 | "tmp.zig:4:17: error: division by zero", | |
| 4575 | 4603 | ); |
| 4576 | 4604 | |
| 4577 | 4605 | cases.add( |
| ... | ... | @@ -4582,7 +4610,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4582 | 4610 | \\ const c = a % b; |
| 4583 | 4611 | \\} |
| 4584 | 4612 | , |
| 4585 | ".tmp_source.zig:4:17: error: division by zero", | |
| 4613 | "tmp.zig:4:17: error: division by zero", | |
| 4586 | 4614 | ); |
| 4587 | 4615 | |
| 4588 | 4616 | cases.add( |
| ... | ... | @@ -4592,8 +4620,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4592 | 4620 | \\ @setRuntimeSafety(false); |
| 4593 | 4621 | \\} |
| 4594 | 4622 | , |
| 4595 | ".tmp_source.zig:3:5: error: runtime safety set twice for same scope", | |
| 4596 | ".tmp_source.zig:2:5: note: first set here", | |
| 4623 | "tmp.zig:3:5: error: runtime safety set twice for same scope", | |
| 4624 | "tmp.zig:2:5: note: first set here", | |
| 4597 | 4625 | ); |
| 4598 | 4626 | |
| 4599 | 4627 | cases.add( |
| ... | ... | @@ -4603,8 +4631,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4603 | 4631 | \\ @setFloatMode(@import("builtin").FloatMode.Optimized); |
| 4604 | 4632 | \\} |
| 4605 | 4633 | , |
| 4606 | ".tmp_source.zig:3:5: error: float mode set twice for same scope", | |
| 4607 | ".tmp_source.zig:2:5: note: first set here", | |
| 4634 | "tmp.zig:3:5: error: float mode set twice for same scope", | |
| 4635 | "tmp.zig:2:5: note: first set here", | |
| 4608 | 4636 | ); |
| 4609 | 4637 | |
| 4610 | 4638 | cases.add( |
| ... | ... | @@ -4613,7 +4641,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4613 | 4641 | \\ var b: u8[40] = undefined; |
| 4614 | 4642 | \\} |
| 4615 | 4643 | , |
| 4616 | ".tmp_source.zig:2:14: error: array access of non-array type 'type'", | |
| 4644 | "tmp.zig:2:14: error: array access of non-array type 'type'", | |
| 4617 | 4645 | ); |
| 4618 | 4646 | |
| 4619 | 4647 | cases.add( |
| ... | ... | @@ -4626,7 +4654,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4626 | 4654 | \\ } |
| 4627 | 4655 | \\} |
| 4628 | 4656 | , |
| 4629 | ".tmp_source.zig:4:13: error: cannot break out of defer expression", | |
| 4657 | "tmp.zig:4:13: error: cannot break out of defer expression", | |
| 4630 | 4658 | ); |
| 4631 | 4659 | |
| 4632 | 4660 | cases.add( |
| ... | ... | @@ -4639,7 +4667,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4639 | 4667 | \\ } |
| 4640 | 4668 | \\} |
| 4641 | 4669 | , |
| 4642 | ".tmp_source.zig:4:13: error: cannot continue out of defer expression", | |
| 4670 | "tmp.zig:4:13: error: cannot continue out of defer expression", | |
| 4643 | 4671 | ); |
| 4644 | 4672 | |
| 4645 | 4673 | cases.add( |
| ... | ... | @@ -4653,7 +4681,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4653 | 4681 | \\ foos[0](); |
| 4654 | 4682 | \\} |
| 4655 | 4683 | , |
| 4656 | ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value", | |
| 4684 | "tmp.zig:7:9: error: calling a generic function requires compile-time known function value", | |
| 4657 | 4685 | ); |
| 4658 | 4686 | |
| 4659 | 4687 | cases.add( |
| ... | ... | @@ -4667,7 +4695,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4667 | 4695 | \\ foos[0](true); |
| 4668 | 4696 | \\} |
| 4669 | 4697 | , |
| 4670 | ".tmp_source.zig:7:9: error: calling a generic function requires compile-time known function value", | |
| 4698 | "tmp.zig:7:9: error: calling a generic function requires compile-time known function value", | |
| 4671 | 4699 | ); |
| 4672 | 4700 | |
| 4673 | 4701 | cases.add( |
| ... | ... | @@ -4681,24 +4709,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4681 | 4709 | \\ return bar; |
| 4682 | 4710 | \\} |
| 4683 | 4711 | , |
| 4684 | ".tmp_source.zig:1:13: error: aoeu", | |
| 4685 | ".tmp_source.zig:3:19: note: referenced here", | |
| 4686 | ".tmp_source.zig:7:12: note: referenced here", | |
| 4687 | ); | |
| 4688 | ||
| 4689 | cases.add( | |
| 4690 | "instantiating an undefined value for an invalid struct that contains itself", | |
| 4691 | \\const Foo = struct { | |
| 4692 | \\ x: Foo, | |
| 4693 | \\}; | |
| 4694 | \\ | |
| 4695 | \\var foo: Foo = undefined; | |
| 4696 | \\ | |
| 4697 | \\export fn entry() usize { | |
| 4698 | \\ return @sizeOf(@typeOf(foo.x)); | |
| 4699 | \\} | |
| 4700 | , | |
| 4701 | ".tmp_source.zig:1:13: error: struct 'Foo' contains itself", | |
| 4712 | "tmp.zig:1:13: error: aoeu", | |
| 4713 | "tmp.zig:3:19: note: referenced here", | |
| 4714 | "tmp.zig:7:12: note: referenced here", | |
| 4702 | 4715 | ); |
| 4703 | 4716 | |
| 4704 | 4717 | cases.add( |
| ... | ... | @@ -4707,7 +4720,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4707 | 4720 | \\ const a = 0x1.0p16384; |
| 4708 | 4721 | \\} |
| 4709 | 4722 | , |
| 4710 | ".tmp_source.zig:2:15: error: float literal out of range of any type", | |
| 4723 | "tmp.zig:2:15: error: float literal out of range of any type", | |
| 4711 | 4724 | ); |
| 4712 | 4725 | |
| 4713 | 4726 | cases.add( |
| ... | ... | @@ -4716,7 +4729,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4716 | 4729 | \\ const a = 0x1.0p-16384; |
| 4717 | 4730 | \\} |
| 4718 | 4731 | , |
| 4719 | ".tmp_source.zig:2:15: error: float literal out of range of any type", | |
| 4732 | "tmp.zig:2:15: error: float literal out of range of any type", | |
| 4720 | 4733 | ); |
| 4721 | 4734 | |
| 4722 | 4735 | cases.add( |
| ... | ... | @@ -4725,7 +4738,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4725 | 4738 | \\ return i32(12.34); |
| 4726 | 4739 | \\} |
| 4727 | 4740 | , |
| 4728 | ".tmp_source.zig:2:16: error: fractional component prevents float value 12.340000 from being casted to type 'i32'", | |
| 4741 | "tmp.zig:2:16: error: fractional component prevents float value 12.340000 from being casted to type 'i32'", | |
| 4729 | 4742 | ); |
| 4730 | 4743 | |
| 4731 | 4744 | cases.add( |
| ... | ... | @@ -4734,7 +4747,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4734 | 4747 | \\ return @ptrToInt(x); |
| 4735 | 4748 | \\} |
| 4736 | 4749 | , |
| 4737 | ".tmp_source.zig:2:22: error: expected pointer, found 'i32'", | |
| 4750 | "tmp.zig:2:22: error: expected pointer, found 'i32'", | |
| 4738 | 4751 | ); |
| 4739 | 4752 | |
| 4740 | 4753 | cases.add( |
| ... | ... | @@ -4743,7 +4756,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4743 | 4756 | \\ const x = @shlExact(u8(0b01010101), 2); |
| 4744 | 4757 | \\} |
| 4745 | 4758 | , |
| 4746 | ".tmp_source.zig:2:15: error: operation caused overflow", | |
| 4759 | "tmp.zig:2:15: error: operation caused overflow", | |
| 4747 | 4760 | ); |
| 4748 | 4761 | |
| 4749 | 4762 | cases.add( |
| ... | ... | @@ -4752,7 +4765,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4752 | 4765 | \\ const x = @shrExact(u8(0b10101010), 2); |
| 4753 | 4766 | \\} |
| 4754 | 4767 | , |
| 4755 | ".tmp_source.zig:2:15: error: exact shift shifted out 1 bits", | |
| 4768 | "tmp.zig:2:15: error: exact shift shifted out 1 bits", | |
| 4756 | 4769 | ); |
| 4757 | 4770 | |
| 4758 | 4771 | cases.add( |
| ... | ... | @@ -4761,7 +4774,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4761 | 4774 | \\ return 0x11 << x; |
| 4762 | 4775 | \\} |
| 4763 | 4776 | , |
| 4764 | ".tmp_source.zig:2:17: error: LHS of shift must be an integer type, or RHS must be compile-time known", | |
| 4777 | "tmp.zig:2:17: error: LHS of shift must be an integer type, or RHS must be compile-time known", | |
| 4765 | 4778 | ); |
| 4766 | 4779 | |
| 4767 | 4780 | cases.add( |
| ... | ... | @@ -4770,7 +4783,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4770 | 4783 | \\ return x << y; |
| 4771 | 4784 | \\} |
| 4772 | 4785 | , |
| 4773 | ".tmp_source.zig:2:17: error: expected type 'u3', found 'u8'", | |
| 4786 | "tmp.zig:2:17: error: expected type 'u3', found 'u8'", | |
| 4774 | 4787 | ); |
| 4775 | 4788 | |
| 4776 | 4789 | cases.add( |
| ... | ... | @@ -4780,7 +4793,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4780 | 4793 | \\ const a: u16 = 300; |
| 4781 | 4794 | \\} |
| 4782 | 4795 | , |
| 4783 | ".tmp_source.zig:1:1: error: declaration shadows primitive type 'u16'", | |
| 4796 | "tmp.zig:1:1: error: declaration shadows primitive type 'u16'", | |
| 4784 | 4797 | ); |
| 4785 | 4798 | |
| 4786 | 4799 | cases.add( |
| ... | ... | @@ -4799,7 +4812,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4799 | 4812 | \\ x.* += 1; |
| 4800 | 4813 | \\} |
| 4801 | 4814 | , |
| 4802 | ".tmp_source.zig:8:13: error: expected type '*u32', found '*align(1) u32'", | |
| 4815 | "tmp.zig:8:13: error: expected type '*u32', found '*align(1) u32'", | |
| 4803 | 4816 | ); |
| 4804 | 4817 | |
| 4805 | 4818 | cases.add( |
| ... | ... | @@ -4819,9 +4832,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4819 | 4832 | \\ x[0] += 1; |
| 4820 | 4833 | \\} |
| 4821 | 4834 | , |
| 4822 | ".tmp_source.zig:9:18: error: cast increases pointer alignment", | |
| 4823 | ".tmp_source.zig:9:23: note: '*align(1) u32' has alignment 1", | |
| 4824 | ".tmp_source.zig:9:18: note: '*[1]u32' has alignment 4", | |
| 4835 | "tmp.zig:9:18: error: cast increases pointer alignment", | |
| 4836 | "tmp.zig:9:23: note: '*align(1) u32' has alignment 1", | |
| 4837 | "tmp.zig:9:18: note: '*[1]u32' has alignment 4", | |
| 4825 | 4838 | ); |
| 4826 | 4839 | |
| 4827 | 4840 | cases.add( |
| ... | ... | @@ -4832,9 +4845,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4832 | 4845 | \\ return ptr.*; |
| 4833 | 4846 | \\} |
| 4834 | 4847 | , |
| 4835 | ".tmp_source.zig:3:17: error: cast increases pointer alignment", | |
| 4836 | ".tmp_source.zig:3:38: note: '*u8' has alignment 1", | |
| 4837 | ".tmp_source.zig:3:26: note: '*u32' has alignment 4", | |
| 4848 | "tmp.zig:3:17: error: cast increases pointer alignment", | |
| 4849 | "tmp.zig:3:38: note: '*u8' has alignment 1", | |
| 4850 | "tmp.zig:3:26: note: '*u32' has alignment 4", | |
| 4838 | 4851 | ); |
| 4839 | 4852 | |
| 4840 | 4853 | cases.add( |
| ... | ... | @@ -4843,7 +4856,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4843 | 4856 | \\ @alignCast(4, u32(3)); |
| 4844 | 4857 | \\} |
| 4845 | 4858 | , |
| 4846 | ".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'", | |
| 4859 | "tmp.zig:2:22: error: expected pointer or slice, found 'u32'", | |
| 4847 | 4860 | ); |
| 4848 | 4861 | |
| 4849 | 4862 | cases.add( |
| ... | ... | @@ -4856,7 +4869,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4856 | 4869 | \\} |
| 4857 | 4870 | \\fn alignedSmall() align(4) i32 { return 1234; } |
| 4858 | 4871 | , |
| 4859 | ".tmp_source.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", | |
| 4872 | "tmp.zig:2:35: error: expected type 'fn() align(8) i32', found 'fn() align(4) i32'", | |
| 4860 | 4873 | ); |
| 4861 | 4874 | |
| 4862 | 4875 | cases.add( |
| ... | ... | @@ -4868,7 +4881,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4868 | 4881 | \\ return x == 5678; |
| 4869 | 4882 | \\} |
| 4870 | 4883 | , |
| 4871 | ".tmp_source.zig:4:32: error: expected type '*i32', found '*align(1) i32'", | |
| 4884 | "tmp.zig:4:32: error: expected type '*i32', found '*align(1) i32'", | |
| 4872 | 4885 | ); |
| 4873 | 4886 | |
| 4874 | 4887 | cases.add( |
| ... | ... | @@ -4877,7 +4890,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4877 | 4890 | \\ const array = [2]u8{1, 2, 3}; |
| 4878 | 4891 | \\} |
| 4879 | 4892 | , |
| 4880 | ".tmp_source.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal", | |
| 4893 | "tmp.zig:2:24: error: expected [2]u8 literal, found [3]u8 literal", | |
| 4881 | 4894 | ); |
| 4882 | 4895 | |
| 4883 | 4896 | cases.add( |
| ... | ... | @@ -4889,7 +4902,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4889 | 4902 | \\ bar(@ptrCast(*c_void, &x)); |
| 4890 | 4903 | \\} |
| 4891 | 4904 | , |
| 4892 | ".tmp_source.zig:5:9: error: expected type '*Derp', found '*c_void'", | |
| 4905 | "tmp.zig:5:9: error: expected type '*Derp', found '*c_void'", | |
| 4893 | 4906 | ); |
| 4894 | 4907 | |
| 4895 | 4908 | cases.add( |
| ... | ... | @@ -4906,7 +4919,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4906 | 4919 | \\ var d = null; |
| 4907 | 4920 | \\ var e = opaque.*; |
| 4908 | 4921 | \\ var f = i32; |
| 4909 | \\ var g = @import("std",); | |
| 4910 | 4922 | \\ var h = (Foo {}).bar; |
| 4911 | 4923 | \\ |
| 4912 | 4924 | \\ var z: noreturn = return; |
| ... | ... | @@ -4916,16 +4928,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4916 | 4928 | \\ fn bar(self: *const Foo) void {} |
| 4917 | 4929 | \\}; |
| 4918 | 4930 | , |
| 4919 | ".tmp_source.zig:4:4: error: variable of type '*comptime_int' must be const or comptime", | |
| 4920 | ".tmp_source.zig:7:4: error: variable of type '(undefined)' must be const or comptime", | |
| 4921 | ".tmp_source.zig:8:4: error: variable of type 'comptime_int' must be const or comptime", | |
| 4922 | ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", | |
| 4923 | ".tmp_source.zig:10:4: error: variable of type '(null)' must be const or comptime", | |
| 4924 | ".tmp_source.zig:11:4: error: variable of type 'Opaque' not allowed", | |
| 4925 | ".tmp_source.zig:12:4: error: variable of type 'type' must be const or comptime", | |
| 4926 | ".tmp_source.zig:13:4: error: variable of type '(namespace)' must be const or comptime", | |
| 4927 | ".tmp_source.zig:14:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", | |
| 4928 | ".tmp_source.zig:16:4: error: unreachable code", | |
| 4931 | "tmp.zig:4:4: error: variable of type '*comptime_int' must be const or comptime", | |
| 4932 | "tmp.zig:7:4: error: variable of type '(undefined)' must be const or comptime", | |
| 4933 | "tmp.zig:8:4: error: variable of type 'comptime_int' must be const or comptime", | |
| 4934 | "tmp.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", | |
| 4935 | "tmp.zig:10:4: error: variable of type '(null)' must be const or comptime", | |
| 4936 | "tmp.zig:11:4: error: variable of type 'Opaque' not allowed", | |
| 4937 | "tmp.zig:12:4: error: variable of type 'type' must be const or comptime", | |
| 4938 | "tmp.zig:13:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", | |
| 4939 | "tmp.zig:15:4: error: unreachable code", | |
| 4929 | 4940 | ); |
| 4930 | 4941 | |
| 4931 | 4942 | cases.add( |
| ... | ... | @@ -4935,7 +4946,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4935 | 4946 | \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} |
| 4936 | 4947 | \\} |
| 4937 | 4948 | , |
| 4938 | ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'", | |
| 4949 | "tmp.zig:3:50: error: expected type 'builtin.AtomicOrder', found 'u32'", | |
| 4939 | 4950 | ); |
| 4940 | 4951 | |
| 4941 | 4952 | cases.add( |
| ... | ... | @@ -4945,7 +4956,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4945 | 4956 | \\ @export("entry", entry, u32(1234)); |
| 4946 | 4957 | \\} |
| 4947 | 4958 | , |
| 4948 | ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'", | |
| 4959 | "tmp.zig:3:32: error: expected type 'builtin.GlobalLinkage', found 'u32'", | |
| 4949 | 4960 | ); |
| 4950 | 4961 | |
| 4951 | 4962 | cases.add( |
| ... | ... | @@ -4974,7 +4985,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4974 | 4985 | \\ }; |
| 4975 | 4986 | \\} |
| 4976 | 4987 | , |
| 4977 | ".tmp_source.zig:14:17: error: use of undeclared identifier 'HeaderValue'", | |
| 4988 | "tmp.zig:14:17: error: use of undeclared identifier 'HeaderValue'", | |
| 4978 | 4989 | ); |
| 4979 | 4990 | |
| 4980 | 4991 | cases.add( |
| ... | ... | @@ -4983,7 +4994,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4983 | 4994 | \\ @setAlignStack(16); |
| 4984 | 4995 | \\} |
| 4985 | 4996 | , |
| 4986 | ".tmp_source.zig:2:5: error: @setAlignStack outside function", | |
| 4997 | "tmp.zig:2:5: error: @setAlignStack outside function", | |
| 4987 | 4998 | ); |
| 4988 | 4999 | |
| 4989 | 5000 | cases.add( |
| ... | ... | @@ -4992,7 +5003,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 4992 | 5003 | \\ @setAlignStack(16); |
| 4993 | 5004 | \\} |
| 4994 | 5005 | , |
| 4995 | ".tmp_source.zig:2:5: error: @setAlignStack in naked function", | |
| 5006 | "tmp.zig:2:5: error: @setAlignStack in naked function", | |
| 4996 | 5007 | ); |
| 4997 | 5008 | |
| 4998 | 5009 | cases.add( |
| ... | ... | @@ -5004,7 +5015,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5004 | 5015 | \\ @setAlignStack(16); |
| 5005 | 5016 | \\} |
| 5006 | 5017 | , |
| 5007 | ".tmp_source.zig:5:5: error: @setAlignStack in inline function", | |
| 5018 | "tmp.zig:5:5: error: @setAlignStack in inline function", | |
| 5008 | 5019 | ); |
| 5009 | 5020 | |
| 5010 | 5021 | cases.add( |
| ... | ... | @@ -5014,8 +5025,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5014 | 5025 | \\ @setAlignStack(16); |
| 5015 | 5026 | \\} |
| 5016 | 5027 | , |
| 5017 | ".tmp_source.zig:3:5: error: alignstack set twice", | |
| 5018 | ".tmp_source.zig:2:5: note: first set here", | |
| 5028 | "tmp.zig:3:5: error: alignstack set twice", | |
| 5029 | "tmp.zig:2:5: note: first set here", | |
| 5019 | 5030 | ); |
| 5020 | 5031 | |
| 5021 | 5032 | cases.add( |
| ... | ... | @@ -5024,7 +5035,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5024 | 5035 | \\ @setAlignStack(511 + 1); |
| 5025 | 5036 | \\} |
| 5026 | 5037 | , |
| 5027 | ".tmp_source.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256", | |
| 5038 | "tmp.zig:2:5: error: attempt to @setAlignStack(512); maximum is 256", | |
| 5028 | 5039 | ); |
| 5029 | 5040 | |
| 5030 | 5041 | cases.add( |
| ... | ... | @@ -5071,7 +5082,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5071 | 5082 | \\ } |
| 5072 | 5083 | \\} |
| 5073 | 5084 | , |
| 5074 | ".tmp_source.zig:37:16: error: cannot store runtime value in compile time variable", | |
| 5085 | "tmp.zig:37:16: error: cannot store runtime value in compile time variable", | |
| 5075 | 5086 | ); |
| 5076 | 5087 | |
| 5077 | 5088 | cases.add( |
| ... | ... | @@ -5087,7 +5098,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5087 | 5098 | \\ return x.blah; |
| 5088 | 5099 | \\} |
| 5089 | 5100 | , |
| 5090 | ".tmp_source.zig:9:13: error: type '*MyType' does not support field access", | |
| 5101 | "tmp.zig:9:13: error: type '*MyType' does not support field access", | |
| 5091 | 5102 | ); |
| 5092 | 5103 | |
| 5093 | 5104 | cases.add( |
| ... | ... | @@ -5095,7 +5106,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5095 | 5106 | "fn test() bool {\r\n" ++ |
| 5096 | 5107 | " true\r\n" ++ |
| 5097 | 5108 | "}\r\n", |
| 5098 | ".tmp_source.zig:1:17: error: invalid carriage return, only '\\n' line endings are supported", | |
| 5109 | "tmp.zig:1:17: error: invalid carriage return, only '\\n' line endings are supported", | |
| 5099 | 5110 | ); |
| 5100 | 5111 | |
| 5101 | 5112 | cases.add( |
| ... | ... | @@ -5105,7 +5116,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5105 | 5116 | \\ true\r |
| 5106 | 5117 | \\} |
| 5107 | 5118 | , |
| 5108 | ".tmp_source.zig:1:1: error: invalid character: '\\xff'", | |
| 5119 | "tmp.zig:1:1: error: invalid character: '\\xff'", | |
| 5109 | 5120 | ); |
| 5110 | 5121 | |
| 5111 | 5122 | cases.add( |
| ... | ... | @@ -5113,7 +5124,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5113 | 5124 | "fn test() bool {\n" ++ |
| 5114 | 5125 | "\ttrue\n" ++ |
| 5115 | 5126 | "}\n", |
| 5116 | ".tmp_source.zig:2:1: error: invalid character: '\\t'", | |
| 5127 | "tmp.zig:2:1: error: invalid character: '\\t'", | |
| 5117 | 5128 | ); |
| 5118 | 5129 | |
| 5119 | 5130 | cases.add( |
| ... | ... | @@ -5122,7 +5133,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5122 | 5133 | \\ _ = @ArgType(i32, 3); |
| 5123 | 5134 | \\} |
| 5124 | 5135 | , |
| 5125 | ".tmp_source.zig:2:18: error: expected function, found 'i32'", | |
| 5136 | "tmp.zig:2:18: error: expected function, found 'i32'", | |
| 5126 | 5137 | ); |
| 5127 | 5138 | |
| 5128 | 5139 | cases.add( |
| ... | ... | @@ -5132,7 +5143,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5132 | 5143 | \\} |
| 5133 | 5144 | \\fn add(a: i32, b: i32) i32 { return a + b; } |
| 5134 | 5145 | , |
| 5135 | ".tmp_source.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) i32' has 2 arguments", | |
| 5146 | "tmp.zig:2:32: error: arg index 2 out of bounds; 'fn(i32, i32) i32' has 2 arguments", | |
| 5136 | 5147 | ); |
| 5137 | 5148 | |
| 5138 | 5149 | cases.add( |
| ... | ... | @@ -5141,7 +5152,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5141 | 5152 | \\ _ = @memberType(i32, 0); |
| 5142 | 5153 | \\} |
| 5143 | 5154 | , |
| 5144 | ".tmp_source.zig:2:21: error: type 'i32' does not support @memberType", | |
| 5155 | "tmp.zig:2:21: error: type 'i32' does not support @memberType", | |
| 5145 | 5156 | ); |
| 5146 | 5157 | |
| 5147 | 5158 | cases.add( |
| ... | ... | @@ -5151,7 +5162,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5151 | 5162 | \\} |
| 5152 | 5163 | \\const Foo = enum {A,}; |
| 5153 | 5164 | , |
| 5154 | ".tmp_source.zig:2:21: error: type 'Foo' does not support @memberType", | |
| 5165 | "tmp.zig:2:21: error: type 'Foo' does not support @memberType", | |
| 5155 | 5166 | ); |
| 5156 | 5167 | |
| 5157 | 5168 | cases.add( |
| ... | ... | @@ -5161,7 +5172,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5161 | 5172 | \\} |
| 5162 | 5173 | \\const Foo = struct {}; |
| 5163 | 5174 | , |
| 5164 | ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", | |
| 5175 | "tmp.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", | |
| 5165 | 5176 | ); |
| 5166 | 5177 | |
| 5167 | 5178 | cases.add( |
| ... | ... | @@ -5171,7 +5182,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5171 | 5182 | \\} |
| 5172 | 5183 | \\const Foo = union {A: void,}; |
| 5173 | 5184 | , |
| 5174 | ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", | |
| 5185 | "tmp.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", | |
| 5175 | 5186 | ); |
| 5176 | 5187 | |
| 5177 | 5188 | cases.add( |
| ... | ... | @@ -5180,7 +5191,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5180 | 5191 | \\ _ = @memberName(i32, 0); |
| 5181 | 5192 | \\} |
| 5182 | 5193 | , |
| 5183 | ".tmp_source.zig:2:21: error: type 'i32' does not support @memberName", | |
| 5194 | "tmp.zig:2:21: error: type 'i32' does not support @memberName", | |
| 5184 | 5195 | ); |
| 5185 | 5196 | |
| 5186 | 5197 | cases.add( |
| ... | ... | @@ -5190,7 +5201,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5190 | 5201 | \\} |
| 5191 | 5202 | \\const Foo = struct {}; |
| 5192 | 5203 | , |
| 5193 | ".tmp_source.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", | |
| 5204 | "tmp.zig:2:26: error: member index 0 out of bounds; 'Foo' has 0 members", | |
| 5194 | 5205 | ); |
| 5195 | 5206 | |
| 5196 | 5207 | cases.add( |
| ... | ... | @@ -5200,7 +5211,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5200 | 5211 | \\} |
| 5201 | 5212 | \\const Foo = enum {A,}; |
| 5202 | 5213 | , |
| 5203 | ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", | |
| 5214 | "tmp.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", | |
| 5204 | 5215 | ); |
| 5205 | 5216 | |
| 5206 | 5217 | cases.add( |
| ... | ... | @@ -5210,7 +5221,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5210 | 5221 | \\} |
| 5211 | 5222 | \\const Foo = union {A:i32,}; |
| 5212 | 5223 | , |
| 5213 | ".tmp_source.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", | |
| 5224 | "tmp.zig:2:26: error: member index 1 out of bounds; 'Foo' has 1 members", | |
| 5214 | 5225 | ); |
| 5215 | 5226 | |
| 5216 | 5227 | cases.add( |
| ... | ... | @@ -5220,7 +5231,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5220 | 5231 | \\} |
| 5221 | 5232 | \\pub extern fn foo(format: *const u8, ...) void; |
| 5222 | 5233 | , |
| 5223 | ".tmp_source.zig:2:9: error: expected type '*const u8', found '[5]u8'", | |
| 5234 | "tmp.zig:2:9: error: expected type '*const u8', found '[5]u8'", | |
| 5224 | 5235 | ); |
| 5225 | 5236 | |
| 5226 | 5237 | cases.add( |
| ... | ... | @@ -5239,9 +5250,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5239 | 5250 | \\ var allocator: ContextAllocator = undefined; |
| 5240 | 5251 | \\} |
| 5241 | 5252 | , |
| 5242 | ".tmp_source.zig:4:25: error: aoeu", | |
| 5243 | ".tmp_source.zig:1:36: note: called from here", | |
| 5244 | ".tmp_source.zig:12:20: note: referenced here", | |
| 5253 | "tmp.zig:4:25: error: aoeu", | |
| 5254 | "tmp.zig:1:36: note: called from here", | |
| 5255 | "tmp.zig:12:20: note: referenced here", | |
| 5245 | 5256 | ); |
| 5246 | 5257 | |
| 5247 | 5258 | cases.add( |
| ... | ... | @@ -5258,7 +5269,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5258 | 5269 | \\ var x = Small.One; |
| 5259 | 5270 | \\} |
| 5260 | 5271 | , |
| 5261 | ".tmp_source.zig:1:21: error: 'u2' too small to hold all bits; must be at least 'u3'", | |
| 5272 | "tmp.zig:1:21: error: 'u2' too small to hold all bits; must be at least 'u3'", | |
| 5262 | 5273 | ); |
| 5263 | 5274 | |
| 5264 | 5275 | cases.add( |
| ... | ... | @@ -5273,7 +5284,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5273 | 5284 | \\ var x = Small.One; |
| 5274 | 5285 | \\} |
| 5275 | 5286 | , |
| 5276 | ".tmp_source.zig:1:21: error: expected integer, found 'f32'", | |
| 5287 | "tmp.zig:1:21: error: expected integer, found 'f32'", | |
| 5277 | 5288 | ); |
| 5278 | 5289 | |
| 5279 | 5290 | cases.add( |
| ... | ... | @@ -5289,7 +5300,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5289 | 5300 | \\ var x: u2 = Small.Two; |
| 5290 | 5301 | \\} |
| 5291 | 5302 | , |
| 5292 | ".tmp_source.zig:9:22: error: expected type 'u2', found 'Small'", | |
| 5303 | "tmp.zig:9:22: error: expected type 'u2', found 'Small'", | |
| 5293 | 5304 | ); |
| 5294 | 5305 | |
| 5295 | 5306 | cases.add( |
| ... | ... | @@ -5306,7 +5317,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5306 | 5317 | \\ var x = @intToEnum(Small, y); |
| 5307 | 5318 | \\} |
| 5308 | 5319 | , |
| 5309 | ".tmp_source.zig:10:31: error: expected type 'u2', found 'u3'", | |
| 5320 | "tmp.zig:10:31: error: expected type 'u2', found 'u3'", | |
| 5310 | 5321 | ); |
| 5311 | 5322 | |
| 5312 | 5323 | cases.add( |
| ... | ... | @@ -5322,7 +5333,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5322 | 5333 | \\ var y = Small.Two; |
| 5323 | 5334 | \\} |
| 5324 | 5335 | , |
| 5325 | ".tmp_source.zig:1:20: error: expected unsigned integer, found 'i2'", | |
| 5336 | "tmp.zig:1:20: error: expected unsigned integer, found 'i2'", | |
| 5326 | 5337 | ); |
| 5327 | 5338 | |
| 5328 | 5339 | cases.add( |
| ... | ... | @@ -5334,7 +5345,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5334 | 5345 | \\ var x: MultipleChoice = undefined; |
| 5335 | 5346 | \\} |
| 5336 | 5347 | , |
| 5337 | ".tmp_source.zig:2:14: error: enums, not structs, support field assignment", | |
| 5348 | "tmp.zig:2:14: error: enums, not structs, support field assignment", | |
| 5338 | 5349 | ); |
| 5339 | 5350 | |
| 5340 | 5351 | cases.add( |
| ... | ... | @@ -5346,8 +5357,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5346 | 5357 | \\ var x: MultipleChoice = undefined; |
| 5347 | 5358 | \\} |
| 5348 | 5359 | , |
| 5349 | ".tmp_source.zig:2:14: error: non-enum union field assignment", | |
| 5350 | ".tmp_source.zig:1:24: note: consider 'union(enum)' here", | |
| 5360 | "tmp.zig:2:14: error: non-enum union field assignment", | |
| 5361 | "tmp.zig:1:24: note: consider 'union(enum)' here", | |
| 5351 | 5362 | ); |
| 5352 | 5363 | |
| 5353 | 5364 | cases.add( |
| ... | ... | @@ -5357,7 +5368,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5357 | 5368 | \\ return @sizeOf(Foo); |
| 5358 | 5369 | \\} |
| 5359 | 5370 | , |
| 5360 | ".tmp_source.zig:1:13: error: enums must have 1 or more fields", | |
| 5371 | "tmp.zig:1:13: error: enums must have 1 or more fields", | |
| 5361 | 5372 | ); |
| 5362 | 5373 | |
| 5363 | 5374 | cases.add( |
| ... | ... | @@ -5367,7 +5378,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5367 | 5378 | \\ return @sizeOf(Foo); |
| 5368 | 5379 | \\} |
| 5369 | 5380 | , |
| 5370 | ".tmp_source.zig:1:13: error: unions must have 1 or more fields", | |
| 5381 | "tmp.zig:1:13: error: unions must have 1 or more fields", | |
| 5371 | 5382 | ); |
| 5372 | 5383 | |
| 5373 | 5384 | cases.add( |
| ... | ... | @@ -5383,8 +5394,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5383 | 5394 | \\ var x = MultipleChoice.C; |
| 5384 | 5395 | \\} |
| 5385 | 5396 | , |
| 5386 | ".tmp_source.zig:6:9: error: enum tag value 60 already taken", | |
| 5387 | ".tmp_source.zig:4:9: note: other occurrence here", | |
| 5397 | "tmp.zig:6:9: error: enum tag value 60 already taken", | |
| 5398 | "tmp.zig:4:9: note: other occurrence here", | |
| 5388 | 5399 | ); |
| 5389 | 5400 | |
| 5390 | 5401 | cases.add( |
| ... | ... | @@ -5402,8 +5413,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5402 | 5413 | \\ return @sizeOf(Payload); |
| 5403 | 5414 | \\} |
| 5404 | 5415 | , |
| 5405 | ".tmp_source.zig:6:17: error: enum field missing: 'C'", | |
| 5406 | ".tmp_source.zig:4:5: note: declared here", | |
| 5416 | "tmp.zig:6:17: error: enum field missing: 'C'", | |
| 5417 | "tmp.zig:4:5: note: declared here", | |
| 5407 | 5418 | ); |
| 5408 | 5419 | |
| 5409 | 5420 | cases.add( |
| ... | ... | @@ -5415,8 +5426,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5415 | 5426 | \\ const x = @TagType(Foo); |
| 5416 | 5427 | \\} |
| 5417 | 5428 | , |
| 5418 | ".tmp_source.zig:5:24: error: union 'Foo' has no tag", | |
| 5419 | ".tmp_source.zig:1:13: note: consider 'union(enum)' here", | |
| 5429 | "tmp.zig:5:24: error: union 'Foo' has no tag", | |
| 5430 | "tmp.zig:1:13: note: consider 'union(enum)' here", | |
| 5420 | 5431 | ); |
| 5421 | 5432 | |
| 5422 | 5433 | cases.add( |
| ... | ... | @@ -5428,7 +5439,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5428 | 5439 | \\ const x = @TagType(Foo); |
| 5429 | 5440 | \\} |
| 5430 | 5441 | , |
| 5431 | ".tmp_source.zig:1:24: error: expected integer tag type, found 'f32'", | |
| 5442 | "tmp.zig:1:24: error: expected integer tag type, found 'f32'", | |
| 5432 | 5443 | ); |
| 5433 | 5444 | |
| 5434 | 5445 | cases.add( |
| ... | ... | @@ -5440,7 +5451,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5440 | 5451 | \\ const x = @TagType(Foo); |
| 5441 | 5452 | \\} |
| 5442 | 5453 | , |
| 5443 | ".tmp_source.zig:1:19: error: expected enum tag type, found 'u32'", | |
| 5454 | "tmp.zig:1:19: error: expected enum tag type, found 'u32'", | |
| 5444 | 5455 | ); |
| 5445 | 5456 | |
| 5446 | 5457 | cases.add( |
| ... | ... | @@ -5456,8 +5467,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5456 | 5467 | \\ var x = MultipleChoice { .C = {} }; |
| 5457 | 5468 | \\} |
| 5458 | 5469 | , |
| 5459 | ".tmp_source.zig:6:9: error: enum tag value 60 already taken", | |
| 5460 | ".tmp_source.zig:4:9: note: other occurrence here", | |
| 5470 | "tmp.zig:6:9: error: enum tag value 60 already taken", | |
| 5471 | "tmp.zig:4:9: note: other occurrence here", | |
| 5461 | 5472 | ); |
| 5462 | 5473 | |
| 5463 | 5474 | cases.add( |
| ... | ... | @@ -5477,8 +5488,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5477 | 5488 | \\ var a = Payload {.A = 1234}; |
| 5478 | 5489 | \\} |
| 5479 | 5490 | , |
| 5480 | ".tmp_source.zig:10:5: error: enum field not found: 'D'", | |
| 5481 | ".tmp_source.zig:1:16: note: enum declared here", | |
| 5491 | "tmp.zig:10:5: error: enum field not found: 'D'", | |
| 5492 | "tmp.zig:1:16: note: enum declared here", | |
| 5482 | 5493 | ); |
| 5483 | 5494 | |
| 5484 | 5495 | cases.add( |
| ... | ... | @@ -5492,8 +5503,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5492 | 5503 | \\ var b = Letter.B; |
| 5493 | 5504 | \\} |
| 5494 | 5505 | , |
| 5495 | ".tmp_source.zig:2:8: error: structs and unions, not enums, support field types", | |
| 5496 | ".tmp_source.zig:1:16: note: consider 'union(enum)' here", | |
| 5506 | "tmp.zig:2:8: error: structs and unions, not enums, support field types", | |
| 5507 | "tmp.zig:1:16: note: consider 'union(enum)' here", | |
| 5497 | 5508 | ); |
| 5498 | 5509 | |
| 5499 | 5510 | cases.add( |
| ... | ... | @@ -5505,7 +5516,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5505 | 5516 | \\ var a = Letter { .A = {} }; |
| 5506 | 5517 | \\} |
| 5507 | 5518 | , |
| 5508 | ".tmp_source.zig:2:5: error: struct field missing type", | |
| 5519 | "tmp.zig:2:5: error: struct field missing type", | |
| 5509 | 5520 | ); |
| 5510 | 5521 | |
| 5511 | 5522 | cases.add( |
| ... | ... | @@ -5517,7 +5528,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5517 | 5528 | \\ var a = Letter { .A = {} }; |
| 5518 | 5529 | \\} |
| 5519 | 5530 | , |
| 5520 | ".tmp_source.zig:2:5: error: union field missing type", | |
| 5531 | "tmp.zig:2:5: error: union field missing type", | |
| 5521 | 5532 | ); |
| 5522 | 5533 | |
| 5523 | 5534 | cases.add( |
| ... | ... | @@ -5536,7 +5547,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5536 | 5547 | \\ var a = Payload { .A = 1234 }; |
| 5537 | 5548 | \\} |
| 5538 | 5549 | , |
| 5539 | ".tmp_source.zig:6:30: error: extern union does not support enum tag type", | |
| 5550 | "tmp.zig:6:30: error: extern union does not support enum tag type", | |
| 5540 | 5551 | ); |
| 5541 | 5552 | |
| 5542 | 5553 | cases.add( |
| ... | ... | @@ -5555,7 +5566,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5555 | 5566 | \\ var a = Payload { .A = 1234 }; |
| 5556 | 5567 | \\} |
| 5557 | 5568 | , |
| 5558 | ".tmp_source.zig:6:30: error: packed union does not support enum tag type", | |
| 5569 | "tmp.zig:6:30: error: packed union does not support enum tag type", | |
| 5559 | 5570 | ); |
| 5560 | 5571 | |
| 5561 | 5572 | cases.add( |
| ... | ... | @@ -5576,8 +5587,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5576 | 5587 | \\ } |
| 5577 | 5588 | \\} |
| 5578 | 5589 | , |
| 5579 | ".tmp_source.zig:11:14: error: switch on union which has no attached enum", | |
| 5580 | ".tmp_source.zig:1:17: note: consider 'union(enum)' here", | |
| 5590 | "tmp.zig:11:14: error: switch on union which has no attached enum", | |
| 5591 | "tmp.zig:1:17: note: consider 'union(enum)' here", | |
| 5581 | 5592 | ); |
| 5582 | 5593 | |
| 5583 | 5594 | cases.add( |
| ... | ... | @@ -5590,8 +5601,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5590 | 5601 | \\ var x = @intToEnum(Foo, 0); |
| 5591 | 5602 | \\} |
| 5592 | 5603 | , |
| 5593 | ".tmp_source.zig:6:13: error: enum 'Foo' has no tag matching integer value 0", | |
| 5594 | ".tmp_source.zig:1:13: note: 'Foo' declared here", | |
| 5604 | "tmp.zig:6:13: error: enum 'Foo' has no tag matching integer value 0", | |
| 5605 | "tmp.zig:1:13: note: 'Foo' declared here", | |
| 5595 | 5606 | ); |
| 5596 | 5607 | |
| 5597 | 5608 | cases.add( |
| ... | ... | @@ -5606,8 +5617,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5606 | 5617 | \\ var x: Value = Letter.A; |
| 5607 | 5618 | \\} |
| 5608 | 5619 | , |
| 5609 | ".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", | |
| 5610 | ".tmp_source.zig:3:5: note: field 'A' declared here", | |
| 5620 | "tmp.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'", | |
| 5621 | "tmp.zig:3:5: note: field 'A' declared here", | |
| 5611 | 5622 | ); |
| 5612 | 5623 | |
| 5613 | 5624 | cases.add( |
| ... | ... | @@ -5625,8 +5636,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5625 | 5636 | \\ var x: Value = l; |
| 5626 | 5637 | \\} |
| 5627 | 5638 | , |
| 5628 | ".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", | |
| 5629 | ".tmp_source.zig:3:5: note: field 'A' has type 'i32'", | |
| 5639 | "tmp.zig:11:20: error: runtime cast to union 'Value' which has non-void fields", | |
| 5640 | "tmp.zig:3:5: note: field 'A' has type 'i32'", | |
| 5630 | 5641 | ); |
| 5631 | 5642 | |
| 5632 | 5643 | cases.add( |
| ... | ... | @@ -5638,7 +5649,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5638 | 5649 | \\ const fieldOffset = @byteOffsetOf(Empty, "val",); |
| 5639 | 5650 | \\} |
| 5640 | 5651 | , |
| 5641 | ".tmp_source.zig:5:46: error: zero-bit field 'val' in struct 'Empty' has no offset", | |
| 5652 | "tmp.zig:5:46: error: zero-bit field 'val' in struct 'Empty' has no offset", | |
| 5642 | 5653 | ); |
| 5643 | 5654 | |
| 5644 | 5655 | cases.add( |
| ... | ... | @@ -5650,7 +5661,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5650 | 5661 | \\ const fieldOffset = @bitOffsetOf(Empty, "val",); |
| 5651 | 5662 | \\} |
| 5652 | 5663 | , |
| 5653 | ".tmp_source.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset", | |
| 5664 | "tmp.zig:5:45: error: zero-bit field 'val' in struct 'Empty' has no offset", | |
| 5654 | 5665 | ); |
| 5655 | 5666 | |
| 5656 | 5667 | cases.add( |
| ... | ... | @@ -5664,7 +5675,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5664 | 5675 | \\ const bar_val = foo.Bar; |
| 5665 | 5676 | \\} |
| 5666 | 5677 | , |
| 5667 | ".tmp_source.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set", | |
| 5678 | "tmp.zig:7:24: error: accessing union field 'Bar' while field 'Baz' is set", | |
| 5668 | 5679 | ); |
| 5669 | 5680 | |
| 5670 | 5681 | cases.add( |
| ... | ... | @@ -5674,7 +5685,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5674 | 5685 | \\ _ = @typeOf(generic).ReturnType; |
| 5675 | 5686 | \\} |
| 5676 | 5687 | , |
| 5677 | ".tmp_source.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic", | |
| 5688 | "tmp.zig:3:25: error: ReturnType has not been resolved because 'fn(var)var' is generic", | |
| 5678 | 5689 | ); |
| 5679 | 5690 | |
| 5680 | 5691 | cases.add( |
| ... | ... | @@ -5684,7 +5695,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5684 | 5695 | \\ _ = @ArgType(@typeOf(generic), 0); |
| 5685 | 5696 | \\} |
| 5686 | 5697 | , |
| 5687 | ".tmp_source.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", | |
| 5698 | "tmp.zig:3:36: error: @ArgType could not resolve the type of arg 0 because 'fn(var)var' is generic", | |
| 5688 | 5699 | ); |
| 5689 | 5700 | |
| 5690 | 5701 | cases.add( |
| ... | ... | @@ -5694,7 +5705,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5694 | 5705 | \\ asm volatile ("" : [baz]"+r"(bar) : : ""); |
| 5695 | 5706 | \\} |
| 5696 | 5707 | , |
| 5697 | ".tmp_source.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215", | |
| 5708 | "tmp.zig:3:5: error: invalid modifier starting output constraint for 'baz': '+', only '=' is supported. Compiler TODO: see https://github.com/ziglang/zig/issues/215", | |
| 5698 | 5709 | ); |
| 5699 | 5710 | |
| 5700 | 5711 | cases.add( |
| ... | ... | @@ -5703,7 +5714,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5703 | 5714 | \\ asm volatile ("" : : [bar]"r"(3) : ""); |
| 5704 | 5715 | \\} |
| 5705 | 5716 | , |
| 5706 | ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_int", | |
| 5717 | "tmp.zig:2:35: error: expected sized integer or sized float, found comptime_int", | |
| 5707 | 5718 | ); |
| 5708 | 5719 | |
| 5709 | 5720 | cases.add( |
| ... | ... | @@ -5712,7 +5723,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5712 | 5723 | \\ asm volatile ("" : : [bar]"r"(3.17) : ""); |
| 5713 | 5724 | \\} |
| 5714 | 5725 | , |
| 5715 | ".tmp_source.zig:2:35: error: expected sized integer or sized float, found comptime_float", | |
| 5726 | "tmp.zig:2:35: error: expected sized integer or sized float, found comptime_float", | |
| 5716 | 5727 | ); |
| 5717 | 5728 | |
| 5718 | 5729 | cases.add( |
| ... | ... | @@ -5726,7 +5737,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5726 | 5737 | \\ const foo = Foo { .Bar = x, .Baz = u8 }; |
| 5727 | 5738 | \\} |
| 5728 | 5739 | , |
| 5729 | ".tmp_source.zig:7:30: error: unable to evaluate constant expression", | |
| 5740 | "tmp.zig:7:30: error: unable to evaluate constant expression", | |
| 5730 | 5741 | ); |
| 5731 | 5742 | |
| 5732 | 5743 | cases.add( |
| ... | ... | @@ -5740,7 +5751,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5740 | 5751 | \\ const foo = Foo { .Bar = x }; |
| 5741 | 5752 | \\} |
| 5742 | 5753 | , |
| 5743 | ".tmp_source.zig:7:30: error: unable to evaluate constant expression", | |
| 5754 | "tmp.zig:7:30: error: unable to evaluate constant expression", | |
| 5744 | 5755 | ); |
| 5745 | 5756 | |
| 5746 | 5757 | cases.addTest( |
| ... | ... | @@ -5750,7 +5761,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5750 | 5761 | \\ var v: V = undefined; |
| 5751 | 5762 | \\} |
| 5752 | 5763 | , |
| 5753 | ".tmp_source.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", | |
| 5764 | "tmp.zig:2:26: error: vector element type must be integer, float, or pointer; '@Vector(4, u8)' is invalid", | |
| 5754 | 5765 | ); |
| 5755 | 5766 | |
| 5756 | 5767 | cases.add("compileLog of tagged enum doesn't crash the compiler", |
| ... | ... | @@ -5765,5 +5776,5 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5765 | 5776 | \\pub fn main () void { |
| 5766 | 5777 | \\ comptime testCompileLog(Bar{.X = 123}); |
| 5767 | 5778 | \\} |
| 5768 | , ".tmp_source.zig:6:5: error: found compile log statement"); | |
| 5779 | , "tmp.zig:6:5: error: found compile log statement"); | |
| 5769 | 5780 | } |
test/stage1/behavior.zig+5-2| ... | ... | @@ -18,6 +18,7 @@ comptime { |
| 18 | 18 | _ = @import("behavior/bugs/1421.zig"); |
| 19 | 19 | _ = @import("behavior/bugs/1442.zig"); |
| 20 | 20 | _ = @import("behavior/bugs/1486.zig"); |
| 21 | _ = @import("behavior/bugs/1500.zig"); | |
| 21 | 22 | _ = @import("behavior/bugs/1851.zig"); |
| 22 | 23 | _ = @import("behavior/bugs/2006.zig"); |
| 23 | 24 | _ = @import("behavior/bugs/394.zig"); |
| ... | ... | @@ -25,6 +26,7 @@ comptime { |
| 25 | 26 | _ = @import("behavior/bugs/529.zig"); |
| 26 | 27 | _ = @import("behavior/bugs/655.zig"); |
| 27 | 28 | _ = @import("behavior/bugs/656.zig"); |
| 29 | _ = @import("behavior/bugs/679.zig"); | |
| 28 | 30 | _ = @import("behavior/bugs/704.zig"); |
| 29 | 31 | _ = @import("behavior/bugs/718.zig"); |
| 30 | 32 | _ = @import("behavior/bugs/726.zig"); |
| ... | ... | @@ -54,14 +56,14 @@ comptime { |
| 54 | 56 | _ = @import("behavior/math.zig"); |
| 55 | 57 | _ = @import("behavior/merge_error_sets.zig"); |
| 56 | 58 | _ = @import("behavior/misc.zig"); |
| 57 | _ = @import("behavior/namespace_depends_on_compile_var/index.zig"); | |
| 59 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); | |
| 58 | 60 | _ = @import("behavior/new_stack_call.zig"); |
| 59 | 61 | _ = @import("behavior/null.zig"); |
| 60 | 62 | _ = @import("behavior/optional.zig"); |
| 61 | 63 | _ = @import("behavior/pointers.zig"); |
| 62 | 64 | _ = @import("behavior/popcount.zig"); |
| 63 | 65 | _ = @import("behavior/ptrcast.zig"); |
| 64 | _ = @import("behavior/pub_enum/index.zig"); | |
| 66 | _ = @import("behavior/pub_enum.zig"); | |
| 65 | 67 | _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig"); |
| 66 | 68 | _ = @import("behavior/reflection.zig"); |
| 67 | 69 | _ = @import("behavior/sizeof_and_typeof.zig"); |
| ... | ... | @@ -78,6 +80,7 @@ comptime { |
| 78 | 80 | _ = @import("behavior/truncate.zig"); |
| 79 | 81 | _ = @import("behavior/try.zig"); |
| 80 | 82 | _ = @import("behavior/type_info.zig"); |
| 83 | _ = @import("behavior/typename.zig"); | |
| 81 | 84 | _ = @import("behavior/undefined.zig"); |
| 82 | 85 | _ = @import("behavior/underscore.zig"); |
| 83 | 86 | _ = @import("behavior/union.zig"); |
test/stage1/behavior/asm.zig+5-5| ... | ... | @@ -4,16 +4,16 @@ const expect = @import("std").testing.expect; |
| 4 | 4 | comptime { |
| 5 | 5 | if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { |
| 6 | 6 | asm volatile ( |
| 7 | \\.globl aoeu; | |
| 8 | \\.type aoeu, @function; | |
| 9 | \\.set aoeu, derp; | |
| 7 | \\.globl this_is_my_alias; | |
| 8 | \\.type this_is_my_alias, @function; | |
| 9 | \\.set this_is_my_alias, derp; | |
| 10 | 10 | ); |
| 11 | 11 | } |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | 14 | test "module level assembly" { |
| 15 | 15 | if (config.arch == config.Arch.x86_64 and config.os == config.Os.linux) { |
| 16 | expect(aoeu() == 1234); | |
| 16 | expect(this_is_my_alias() == 1234); | |
| 17 | 17 | } |
| 18 | 18 | } |
| 19 | 19 | |
| ... | ... | @@ -85,7 +85,7 @@ test "sized integer/float in asm input" { |
| 85 | 85 | ); |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | extern fn aoeu() i32; | |
| 88 | extern fn this_is_my_alias() i32; | |
| 89 | 89 | |
| 90 | 90 | export fn derp() i32 { |
| 91 | 91 | return 1234; |
test/stage1/behavior/bugs/1500.zig created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | const A = struct { | |
| 2 | b: B, | |
| 3 | }; | |
| 4 | ||
| 5 | const B = fn (A) void; | |
| 6 | ||
| 7 | test "allow these dependencies" { | |
| 8 | var a: A = undefined; | |
| 9 | var b: B = undefined; | |
| 10 | } |
test/stage1/behavior/bugs/679.zig created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | pub fn List(comptime T: type) type { | |
| 5 | return u32; | |
| 6 | } | |
| 7 | ||
| 8 | const ElementList = List(Element); | |
| 9 | const Element = struct { | |
| 10 | link: ElementList, | |
| 11 | }; | |
| 12 | ||
| 13 | test "false dependency loop in struct definition" { | |
| 14 | const listType = ElementList; | |
| 15 | var x: listType = 42; | |
| 16 | expect(x == 42); | |
| 17 | } |
test/stage1/behavior/misc.zig+3-2| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const expect = std.testing.expect; |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 3 | 4 | const mem = std.mem; |
| 4 | 5 | const cstr = std.cstr; |
| 5 | 6 | const builtin = @import("builtin"); |
| ... | ... | @@ -469,7 +470,7 @@ test "@typeId" { |
| 469 | 470 | expect(@typeId(AUnionEnum) == Tid.Union); |
| 470 | 471 | expect(@typeId(AUnion) == Tid.Union); |
| 471 | 472 | expect(@typeId(fn () void) == Tid.Fn); |
| 472 | expect(@typeId(@typeOf(builtin)) == Tid.Namespace); | |
| 473 | expect(@typeId(@typeOf(builtin)) == Tid.Type); | |
| 473 | 474 | // TODO bound fn |
| 474 | 475 | // TODO arg tuple |
| 475 | 476 | // TODO opaque |
| ... | ... | @@ -488,7 +489,7 @@ test "@typeName" { |
| 488 | 489 | expect(mem.eql(u8, @typeName(i64), "i64")); |
| 489 | 490 | expect(mem.eql(u8, @typeName(*usize), "*usize")); |
| 490 | 491 | // https://github.com/ziglang/zig/issues/675 |
| 491 | expect(mem.eql(u8, @typeName(TypeFromFn(u8)), "TypeFromFn(u8)")); | |
| 492 | expectEqualSlices(u8, "behavior.misc.TypeFromFn(u8)", @typeName(TypeFromFn(u8))); | |
| 492 | 493 | expect(mem.eql(u8, @typeName(Struct), "Struct")); |
| 493 | 494 | expect(mem.eql(u8, @typeName(Union), "Union")); |
| 494 | 495 | expect(mem.eql(u8, @typeName(Enum), "Enum")); |
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/pointers.zig+6| ... | ... | @@ -124,3 +124,9 @@ test "implicit cast error unions with non-optional to optional pointer" { |
| 124 | 124 | S.doTheTest(); |
| 125 | 125 | comptime S.doTheTest(); |
| 126 | 126 | } |
| 127 | ||
| 128 | test "initialize const optional C pointer to null" { | |
| 129 | const a: ?[*c]i32 = null; | |
| 130 | expect(a == null); | |
| 131 | comptime expect(a == null); | |
| 132 | } |
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 | } |
test/stage1/behavior/slice.zig+11-2| ... | ... | @@ -1,5 +1,7 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | const mem = @import("std").mem; | |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 4 | const mem = std.mem; | |
| 3 | 5 | |
| 4 | 6 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; |
| 5 | 7 | const y = x[0x100..]; |
| ... | ... | @@ -38,3 +40,10 @@ test "implicitly cast array of size 0 to slice" { |
| 38 | 40 | fn assertLenIsZero(msg: []const u8) void { |
| 39 | 41 | expect(msg.len == 0); |
| 40 | 42 | } |
| 43 | ||
| 44 | test "C pointer" { | |
| 45 | var buf: [*c]const u8 = c"kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf"; | |
| 46 | var len: u32 = 10; | |
| 47 | var slice = buf[0..len]; | |
| 48 | expectEqualSlices(u8, "kjdhfkjdhf", slice); | |
| 49 | } |
test/stage1/behavior/type_info.zig+1-1| ... | ... | @@ -186,7 +186,7 @@ fn testUnion() void { |
| 186 | 186 | expect(TypeId(typeinfo_info) == TypeId.Union); |
| 187 | 187 | expect(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); |
| 188 | 188 | expect(typeinfo_info.Union.tag_type.? == TypeId); |
| 189 | expect(typeinfo_info.Union.fields.len == 25); | |
| 189 | expect(typeinfo_info.Union.fields.len == 24); | |
| 190 | 190 | expect(typeinfo_info.Union.fields[4].enum_field != null); |
| 191 | 191 | expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4); |
| 192 | 192 | expect(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); |
test/stage1/behavior/typename.zig created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | |
| 4 | ||
| 5 | test "slice" { | |
| 6 | expectEqualSlices(u8, "[]u8", @typeName([]u8)); | |
| 7 | } |
test/standalone/main_pkg_path/a/test.zig created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | const b = @import("../b.zig"); | |
| 2 | ||
| 3 | test "main pkg path" { | |
| 4 | b.foo(); | |
| 5 | } |
test/standalone/main_pkg_path/b.zig created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | pub fn foo() void {} |
test/standalone/main_pkg_path/build.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const test_exe = b.addTest("a/test.zig"); | |
| 5 | test_exe.setMainPkgPath("."); | |
| 6 | ||
| 7 | const test_step = b.step("test", "Test the program"); | |
| 8 | test_step.dependOn(&test_exe.step); | |
| 9 | } |
test/standalone/static_c_lib/build.zig created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | const Builder = @import("std").build.Builder; | |
| 2 | ||
| 3 | pub fn build(b: *Builder) void { | |
| 4 | const mode = b.standardReleaseOptions(); | |
| 5 | ||
| 6 | const foo = b.addStaticLibrary("foo", null); | |
| 7 | foo.addCSourceFile("foo.c", [][]const u8{}); | |
| 8 | foo.setBuildMode(mode); | |
| 9 | foo.addIncludeDir("."); | |
| 10 | ||
| 11 | const test_exe = b.addTest("foo.zig"); | |
| 12 | test_exe.setBuildMode(mode); | |
| 13 | test_exe.linkLibrary(foo); | |
| 14 | test_exe.addIncludeDir("."); | |
| 15 | ||
| 16 | const test_step = b.step("test", "Test it"); | |
| 17 | test_step.dependOn(&test_exe.step); | |
| 18 | } |
test/standalone/static_c_lib/foo.c created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | #include "foo.h" | |
| 2 | uint32_t add(uint32_t a, uint32_t b) { | |
| 3 | return a + b; | |
| 4 | } |
test/standalone/static_c_lib/foo.h created+2| ... | ... | @@ -0,0 +1,2 @@ |
| 1 | #include <stdint.h> | |
| 2 | uint32_t add(uint32_t a, uint32_t b); |
test/standalone/static_c_lib/foo.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const c = @cImport(@cInclude("foo.h")); | |
| 4 | ||
| 5 | test "C add" { | |
| 6 | const result = c.add(1, 2); | |
| 7 | expect(result == 3); | |
| 8 | } |
test/tests.zig+5-2| ... | ... | @@ -569,7 +569,7 @@ pub const CompileErrorContext = struct { |
| 569 | 569 | const ErrLineIter = struct { |
| 570 | 570 | lines: mem.SplitIterator, |
| 571 | 571 | |
| 572 | const source_file = ".tmp_source.zig"; | |
| 572 | const source_file = "tmp.zig"; | |
| 573 | 573 | |
| 574 | 574 | fn init(input: []const u8) ErrLineIter { |
| 575 | 575 | return ErrLineIter{ .lines = mem.separate(input, "\n") }; |
| ... | ... | @@ -759,7 +759,7 @@ pub const CompileErrorContext = struct { |
| 759 | 759 | .is_test = false, |
| 760 | 760 | }; |
| 761 | 761 | |
| 762 | tc.addSourceFile(".tmp_source.zig", source); | |
| 762 | tc.addSourceFile("tmp.zig", source); | |
| 763 | 763 | comptime var arg_i = 0; |
| 764 | 764 | inline while (arg_i < expected_lines.len) : (arg_i += 1) { |
| 765 | 765 | tc.addExpectedError(expected_lines[arg_i]); |
| ... | ... | @@ -980,15 +980,18 @@ pub const TranslateCContext = struct { |
| 980 | 980 | Term.Exited => |code| { |
| 981 | 981 | if (code != 0) { |
| 982 | 982 | warn("Compilation failed with exit code {}\n", code); |
| 983 | printInvocation(zig_args.toSliceConst()); | |
| 983 | 984 | return error.TestFailed; |
| 984 | 985 | } |
| 985 | 986 | }, |
| 986 | 987 | Term.Signal => |code| { |
| 987 | 988 | warn("Compilation failed with signal {}\n", code); |
| 989 | printInvocation(zig_args.toSliceConst()); | |
| 988 | 990 | return error.TestFailed; |
| 989 | 991 | }, |
| 990 | 992 | else => { |
| 991 | 993 | warn("Compilation terminated unexpectedly\n"); |
| 994 | printInvocation(zig_args.toSliceConst()); | |
| 992 | 995 | return error.TestFailed; |
| 993 | 996 | }, |
| 994 | 997 | } |